From 558d91af21ad3c9f02890b84d5b73f50d92acc33 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Mon, 14 Feb 2011 17:57:49 +0100 Subject: [PATCH 001/641] Add some stuff on the possibility of hasOwnProperty being a property of an object --- doc/hasownproperty.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/hasownproperty.md b/doc/hasownproperty.md index 39aa33a0..fe9638e7 100644 --- a/doc/hasownproperty.md +++ b/doc/hasownproperty.md @@ -26,6 +26,24 @@ essential when iterating over the properties of any object. There is no other way to exclude properties that are not defined on the object **itself**, but somewhere on its prototype chain. +### `hasOwnProperty` as a property + +JavaScript does not protect the property name `hasOwnProperty`; therefore, if the +possibility exists that an object might have a property with this name, it is +necessary to use an *external* `hasOwnProperty` in order to get correct results. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // always returns false + + // Use another hasOwnProperty and call it with 'this' set to foo + {}.hasOwnProperty.call(foo, 'bar'); // true + ### In conclusion When checking for the existence of a property on a object, `hasOwnProperty` is From 00fc9c92f5f0d285e728db4ecc16381645c065f0 Mon Sep 17 00:00:00 2001 From: MutantTractor Date: Tue, 15 Feb 2011 08:37:12 -0800 Subject: [PATCH 002/641] --- html/css/garden.css | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/html/css/garden.css b/html/css/garden.css index c0013f6b..70027401 100644 --- a/html/css/garden.css +++ b/html/css/garden.css @@ -301,6 +301,30 @@ a abbr { opacity: 0.85; } + aside p { + font-size: 13px; + } +} + +@media screen and (max-device-width: 480px) { + body > div { + padding-right: 30px; + } + + article { + font-size: 15px; + width: 100%; + } + + aside { + position: static; + width: auto; + margin: 30px; + padding: 0.6em 1em 0.625em; + border-top: 1px solid #9eabb7; + opacity: 0.85; + } + aside p { font-size: 13px; } From 35da83eeea160bf87d07ab28bfe1febeaa9861d4 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Fri, 18 Feb 2011 21:35:11 +0100 Subject: [PATCH 003/641] Rework the semicolon section to be more of an advice --- doc/semicolon.md | 149 +++++++++++++++++++++++++++++++---------------- 1 file changed, 100 insertions(+), 49 deletions(-) diff --git a/doc/semicolon.md b/doc/semicolon.md index 2b56705f..b2909f03 100644 --- a/doc/semicolon.md +++ b/doc/semicolon.md @@ -1,71 +1,122 @@ ## Automatic semicolon insertion Although JavaScript has C style syntax, it does **not** enforce the use of -semicolons in the source code. Since the parser still needs semicolons in order -to be able to figure out what the code should do, it inserts them -**automatically**. +semicolons in the source code, it is possible to omot them. -When the parser encounters an error due to newline that is not preceded by a -semicolon, it will insert a semicolon automatically and try again. When the -parser still hits an error, it will raise it, otherwise it will simply proceed. +But JavaScript is not a semicolon-less language, it in fact needs the +semicolons in order to understand the sourcecode. 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. It makes the below code work, but with a -completely different result than intended. +design flaws in the language as it *can* change the behavior of code. - return - { - foo: 1 - } +### How it works -After the JavaScript parser fixed it, this will **not** return an object which -has a property called `foo`, it will instead simply return `undefined`. +The code below has no semicolons in it, so it is up to the parser to decide where +to insert them. -### How the parser "fixes" missing semicolons + (function(window, undefined) { + function test(options) { + log('testing!') - return // Error, semicolon expected. Automatic insertion happens - { // Block syntax is handle just fine + (options.list || []).forEach(function(i) { - // foo is not interpreted as property name, but as a label - foo: 1 // JavaScript supports single expression evaluation - // So 1 evaluates to 1 and no error is being raised + }) - } // Automatic semicolon insertion + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ) -After the parser has done its "magic", the resulting code has completely -different behavior. + return + { + foo: function() {} + } + } + window.test = test - return; // implicitly returns undefined + })(window) - // dead code - { - foo: 1 - }; + (function(window) { + window.someLibrary = {} -### Missing semicolons and evaluation + })(window) - var foo = function() { - } // missing semicolon after assignment - - (function() { - // do something in it's own scope - })(); - -Again, the above code will behave **drastically different**. - - var foo = function(){ - - }( // The parser does NOT insert a semicolon here - // call the anonymous function and pass another function in - function() { +Below is the result of the parsers "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 + { + foo: function() {} + }; // <- inserted } - )() // now call the result of the previous call + 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 parse 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 `TypeError` saying that `undefined is not a function`. + +### Broken `return` statements + +The JavaScript parse also does not correctly handle return statements which are +followed by a new line. + + return; + { // gets interpreted as a block + + // a label and a single expression statement + foo: function() {} + }; + +Instead it produces the above, which simply is a silent error ### In conclusion -Semicolons should **never** be omitted, it is also recommended to keep braces -on the same line with their associated statements and never omit them for one -line `if` / `else` statements. This will not only improve the consistency of the -code, it will also prevent the JavaScript parser from applying too much "magic" -to the code. +It is highly recommended to **never** omit semicolons, it is also advocated to +keep braces on the same line with their corresponding statements and to never omit +them for one single-line `if` / `else` statements. Both of these measures will +not only improve the consistency of the code, they will also prevent the +JavaScript parser from changing its behavior. From e1dd097eddafa4975a7334aee7777c7d7aa32e66 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Fri, 18 Feb 2011 21:44:28 +0100 Subject: [PATCH 004/641] Fix typos --- doc/semicolon.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/semicolon.md b/doc/semicolon.md index b2909f03..a091d2ed 100644 --- a/doc/semicolon.md +++ b/doc/semicolon.md @@ -1,10 +1,10 @@ ## Automatic semicolon insertion Although JavaScript has C style syntax, it does **not** enforce the use of -semicolons in the source code, it is possible to omot them. +semicolons in the source code, it is possible to omit them. But JavaScript is not a semicolon-less language, it in fact needs the -semicolons in order to understand the sourcecode. Therefore the JavaScript +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. @@ -53,7 +53,7 @@ to insert them. })(window) -Below is the result of the parsers "guessing" game. +Below is the result of the parser's "guessing" game. (function(window, undefined) { function test(options) { From 4304690e77b8022115ffd54c2409ed44aab7a24c Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Fri, 18 Feb 2011 22:06:13 +0100 Subject: [PATCH 005/641] Fix wording --- doc/semicolon.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/semicolon.md b/doc/semicolon.md index a091d2ed..e039fac5 100644 --- a/doc/semicolon.md +++ b/doc/semicolon.md @@ -110,7 +110,7 @@ followed by a new line. foo: function() {} }; -Instead it produces the above, which simply is a silent error +Instead it produces the above, that is simply a silent error ### In conclusion From a59d4cd683fe806d066b4a22ec28ccc144e3ff1e Mon Sep 17 00:00:00 2001 From: Yi Jiang Date: Sat, 19 Feb 2011 16:01:08 -0800 Subject: [PATCH 006/641] Minor copyediting --- doc/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/functions.md b/doc/functions.md index 28743167..7ea89da0 100644 --- a/doc/functions.md +++ b/doc/functions.md @@ -63,7 +63,7 @@ is always made available in the local scope of the function itself. } Since there is **no** [block scope](#scopes) in JavaScript, the above will -**not** assign the value `2` to the *global* variable `bar`. It will rather +**not** assign the value `2` to the *global* variable `bar`. Instead, it will assign the value of `2` to the *local* variable `bar` of `test`. Also, while the statements inside the `if` block never get executed, the variable From 4b15195ffaa788e424e5bbeb45b4cc7f89b14f2d Mon Sep 17 00:00:00 2001 From: Yi Jiang Date: Sat, 19 Feb 2011 16:05:07 -0800 Subject: [PATCH 007/641] Fixed issue #16 --- doc/arguments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/arguments.md b/doc/arguments.md index c12ab431..dfcd7e9d 100644 --- a/doc/arguments.md +++ b/doc/arguments.md @@ -29,7 +29,7 @@ critical sections of code. ### Modification "magic" The `arguments` object creates getter and setter functions for both its properties -as well as the functions formal parameters. +as well as the function's formal parameters. As a result, changing the value of a formal parameter will also change the value corresponding formal parameter, and the other way around. From b1df3046df2a2080e2b3d7f7ff532ed7de7be7dc Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sun, 20 Feb 2011 11:42:16 +0100 Subject: [PATCH 008/641] Should be possessive --- doc/arguments.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/arguments.md b/doc/arguments.md index c12ab431..e0e97797 100644 --- a/doc/arguments.md +++ b/doc/arguments.md @@ -3,7 +3,7 @@ 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 functions +> **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. @@ -29,7 +29,7 @@ critical sections of code. ### Modification "magic" The `arguments` object creates getter and setter functions for both its properties -as well as the functions formal parameters. +as well as the function's formal parameters. As a result, changing the value of a formal parameter will also change the value corresponding formal parameter, and the other way around. From 2a03b61e144af1724ff3c15efd193043f62bf30a Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Tue, 22 Feb 2011 14:38:24 +0100 Subject: [PATCH 009/641] Add example on how to avoid the conversion to an array. --- doc/arguments.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/doc/arguments.md b/doc/arguments.md index e0e97797..826b159b 100644 --- a/doc/arguments.md +++ b/doc/arguments.md @@ -23,9 +23,39 @@ The code below will return a new `Array` containing all the elements of the Array.prototype.slice.call(arguments); -This conversion is **slow**, it is not recommended to use it in performance +This conversion is **slow**, it is **not** recommended to use it in performance critical sections of code. +### Passing arguments + +The following is the recommended way of passing arguments from one function to +another. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // do stuff here + } + +Another trick is to use both `call` and `apply` together to create fast, unbound +wrappers. + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Create an unbound version of "method" + // It takes the parameters: this, arg1, arg2...argN + Foo.method = function() { + + // Result: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + ### Modification "magic" The `arguments` object creates getter and setter functions for both its properties From fd818dcb3b9801a3f4f09a93f6873cc5bf850abd Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Tue, 22 Feb 2011 14:38:43 +0100 Subject: [PATCH 010/641] Remove duplicate stuff and put it into a note. --- doc/semicolon.md | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/doc/semicolon.md b/doc/semicolon.md index e039fac5..19d85f5d 100644 --- a/doc/semicolon.md +++ b/doc/semicolon.md @@ -69,7 +69,9 @@ Below is the result of the parser's "guessing" game. ); // <- inserted return; <- inserted, breaks the return statement - { + { // treated as a block + + // a label and a single expression statement foo: function() {} }; // <- inserted } @@ -81,6 +83,10 @@ Below is the result of the parser's "guessing" game. })(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. @@ -98,20 +104,6 @@ This code gets transformed into one line. Chances are **very** high that `log` does **not** return a function, therefore the above will yield `TypeError` saying that `undefined is not a function`. -### Broken `return` statements - -The JavaScript parse also does not correctly handle return statements which are -followed by a new line. - - return; - { // gets interpreted as a block - - // a label and a single expression statement - foo: function() {} - }; - -Instead it produces the above, that is simply a silent error - ### In conclusion It is highly recommended to **never** omit semicolons, it is also advocated to From b3fd65cf6d3f7ed7b4ded65bfa267628e7c03963 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Tue, 22 Feb 2011 14:38:51 +0100 Subject: [PATCH 011/641] ES5 Notes. --- doc/typeof.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/typeof.md b/doc/typeof.md index 7e186666..86fcb5d2 100644 --- a/doc/typeof.md +++ b/doc/typeof.md @@ -42,6 +42,9 @@ The *Class* refers to the value of the internal `[[Class]]` property of an objec > following strings. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, > `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. +> **ES5 Note:** The `[[Class]]` value of `null` and `undefined` in ECMAScript 5 +> was changed from `Object` to `Null` and `Undefined`. + In order to retrieve the value of `[[Class]]` one can has to make use of the `toString` method of `Object`. From b6fa5b207550e23472e93ec29e54b9a99220dc61 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Tue, 22 Feb 2011 14:39:21 +0100 Subject: [PATCH 012/641] Make clear that the list is not complete and that the wrapper can be done in a different fashion. --- doc/undefined.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/doc/undefined.md b/doc/undefined.md index 491d0cbe..1e64cbdc 100644 --- a/doc/undefined.md +++ b/doc/undefined.md @@ -11,7 +11,7 @@ The language also defines a global variable that has the value of `undefined`, this variable is also called `undefined`. But this variable is **not** a constant, meaning that it can be easily overwritten which then leads to abstruse bugs. -The value `undefined` is returned in the following cases: +Some examples for when the value `undefined` is returned: - Accessing the (unmodified) global variable `undefined` - Implicit returns of functions due to missing `return` statements @@ -20,7 +20,6 @@ The value `undefined` is returned in the following cases: - Function parameters which don't had any explicit value passed - Anything that has been set to the value of `undefined` - ### The case of the "overridden" `undefined` Since the variable `undefined` only has the value of `undefined`, changing its @@ -40,6 +39,20 @@ common technique used is to add an additional parameter to the encapsulation })('Hello World', 42); +Another way to achieve the same effect would be to use a declaration inside the +wrapper. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +The only difference being here, that this version results in 4 more bytes being +used in case it is minified and there is no other `var` statement inside the +anonymous wrapper. + ### Uses of `null` While `undefined` in the context of the JavaScript language is mostly used in From 6d2b8aa2ed70689193b6a10d1b2e9501782b3973 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Tue, 22 Feb 2011 15:31:49 +0100 Subject: [PATCH 013/641] Further fixes --- doc/casting.md | 5 +++-- doc/typeof.md | 7 ++++--- doc/undefined.md | 4 ++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/doc/casting.md b/doc/casting.md index 9a2f5114..dcd1d61f 100644 --- a/doc/casting.md +++ b/doc/casting.md @@ -17,8 +17,9 @@ wherever possible. 10 == 010; 10 == '-10'; -> **Note:** Number literals that start with a `0` are interpreted as octal (Base -> 8). +> **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 that the use of the [strict equal operator](#equality) is recommended. diff --git a/doc/typeof.md b/doc/typeof.md index 86fcb5d2..c83aee0e 100644 --- a/doc/typeof.md +++ b/doc/typeof.md @@ -42,9 +42,6 @@ The *Class* refers to the value of the internal `[[Class]]` property of an objec > following strings. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, > `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. -> **ES5 Note:** The `[[Class]]` value of `null` and `undefined` in ECMAScript 5 -> was changed from `Object` to `Null` and `Undefined`. - In order to retrieve the value of `[[Class]]` one can has to make use of the `toString` method of `Object`. @@ -64,6 +61,10 @@ which the use of `Object.prototype.toString`. In the above example, `Object.prototype.toString` gets called with the value of [this](#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' diff --git a/doc/undefined.md b/doc/undefined.md index 1e64cbdc..0e478cbb 100644 --- a/doc/undefined.md +++ b/doc/undefined.md @@ -11,6 +11,10 @@ The language also defines a global variable that has the value of `undefined`, this variable is also called `undefined`. But this variable is **not** a constant, meaning that it can be easily overwritten which then leads to abstruse bugs. +> **ES5 Note:** `undefined` in ECMAScript 5 is **no longer** *writable* in strict +> mode, but its name can still be shadowed by for example a function with the name +> `undefined`. + Some examples for when the value `undefined` is returned: - Accessing the (unmodified) global variable `undefined` From 01bba65c287fb5612895ada704d90853ea6b10c3 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Tue, 22 Feb 2011 20:42:08 +0100 Subject: [PATCH 014/641] Minimal change in wording --- doc/scopes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/scopes.md b/doc/scopes.md index 7ed540c6..1dd6a797 100644 --- a/doc/scopes.md +++ b/doc/scopes.md @@ -33,7 +33,7 @@ still has not found the requested name, it will raise a `ReferenceError`. 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 -*local* scope. +*current* scope. Again, that is **not** at all the same effect, not using `var` can have major implications. From a9e8f352edee22be65cb9c67987ea67e12bc2447 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Tue, 22 Feb 2011 21:12:49 +0100 Subject: [PATCH 015/641] should be a lowercase l --- doc/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/functions.md b/doc/functions.md index 7ea89da0..cc6ac5ca 100644 --- a/doc/functions.md +++ b/doc/functions.md @@ -29,7 +29,7 @@ Due to the fact that `var` is a *statement*, which - just like the function declaration - creates the variable `foo` before the actual execution of the code starts, `foo` is already defined when the script gets executed. -Since assignments only happens at runtime, the value of `Foo` will default +Since assignments only happens at runtime, the value of `foo` will default to [undefined](#undefined) before the corresponding code is executed. ### Named function expression From b650d111c2e7ae8ac6f3fa7d1b90545b5d4b937d Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Tue, 22 Feb 2011 21:53:19 +0100 Subject: [PATCH 016/641] Clean up sentence structures --- doc/arguments.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/doc/arguments.md b/doc/arguments.md index 826b159b..c9f9e92f 100644 --- a/doc/arguments.md +++ b/doc/arguments.md @@ -7,7 +7,7 @@ This variable holds a list of all the arguments that were passed to the function > scope either via a `var` statement or being the name of a formal parameter, > the `arguments` object will not be created. -The `arguments` variable is **not** an `Array`. While it has some of the +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`. @@ -62,7 +62,7 @@ The `arguments` object creates getter and setter functions for both its properti as well as the function's formal parameters. As a result, changing the value of a formal parameter will also change the value -corresponding formal parameter, and the other way around. +of the corresponding property of the arguments object, and the other way around. function foo(a, b, c) { arguments[0] = 2; @@ -79,11 +79,13 @@ corresponding formal parameter, and the other way around. ### Performance myths and truths -The `arguments` is, except for the two cases named at the start of this section, -always created. It doesn't 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 an access to -the arguments object properties. +The `arguments` object is always created the only two exceptions being the cases +where it is declared as a name inside of a function or one of its formal +parameters. It does not matter whether it is used or not. + +Both getters and setters are **always** created; thus, using it has nearly +no performance impact at all, especially not in real world code where there is +more than an access to the arguments object properties. > **ES5 Note:** These getters and setters are not created in strict mode. From 93c2b5bacead9e44a4987d7fffd89af2af88ed15 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 26 Feb 2011 10:31:32 +0100 Subject: [PATCH 017/641] Fix broken code notation --- doc/typeof.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/typeof.md b/doc/typeof.md index c83aee0e..bfe850d7 100644 --- a/doc/typeof.md +++ b/doc/typeof.md @@ -62,7 +62,7 @@ In the above example, `Object.prototype.toString` gets called with the value of [this](#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 +> for both `null` and `undefined` was **changed** from `Object` to `Null` and > `Undefined` in ECMAScript 5. ### Testing for undefined variables From afb8600a4cf78c01eb67178976fe8fbe9e3d2790 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 26 Feb 2011 10:31:59 +0100 Subject: [PATCH 018/641] Remove this part, since I now have an awesome job --- doc/intro.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/doc/intro.md b/doc/intro.md index 6df253a4..7f49458c 100644 --- a/doc/intro.md +++ b/doc/intro.md @@ -16,10 +16,6 @@ the excellent [guide][1] on the Mozilla Developer Network. This guide is the work of two lovely Stack Overflow users, [Ivo Wetzel][6] (Writing) and [Zhang Yi Jiang][5] (Design). -In case you are interested in additional guidance or reviews concerning your JavaScript -projects, Ivo Wetzel offers these on a freelance basis. Please feel free to -contact him via [e-mail][7] for further details. - ### Contributors - [Caio Romão][8] (Spelling corrections) @@ -38,7 +34,7 @@ Stack Overflow chat. [4]: https://github.com/BonsaiDen/JavaScript-Garden [5]: http://stackoverflow.com/users/313758/yi-jiang [6]: http://stackoverflow.com/users/170224/ivo-wetzel -[7]: mailto:ivo.wetzel@googlemail.com [8]: https://github.com/caio [9]: https://github.com/blixt -[10]: http://chat.stackoverflow.com/rooms/17/javascript \ No newline at end of file +[10]: http://chat.stackoverflow.com/rooms/17/javascript + From 481e25740953766b41bf7fd88c6d6dd44f85cd3a Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 26 Feb 2011 13:51:38 +0100 Subject: [PATCH 019/641] Also remove the freelance part here, since the awesome job is awesome --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 95ff0653..e203d2f0 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,6 @@ the excellent [guide][1] on the Mozilla Developer Network. This guide is the work of two lovely Stack Overflow users, [Ivo Wetzel][6] (Writing) and [Zhang Yi Jiang][5] (Design). -In case you are interested in additional guidance or reviews concerning your JavaScript -projects, Ivo Wetzel offers these on a freelance basis. Please feel free to -contact him via [e-mail][7] for further details. - ### Contributors - [Caio Romão][8] (Spelling corrections) @@ -39,7 +35,7 @@ Stack Overflow chat. [4]: https://github.com/BonsaiDen/JavaScript-Garden [5]: http://stackoverflow.com/users/313758/yi-jiang [6]: http://stackoverflow.com/users/170224/ivo-wetzel -[7]: mailto:ivo.wetzel@googlemail.com [8]: https://github.com/caio [9]: https://github.com/blixt -[10]: http://chat.stackoverflow.com/rooms/17/javascript \ No newline at end of file +[10]: http://chat.stackoverflow.com/rooms/17/javascript + From ca6cf0fc3b3bc67fd0a1b602a056472941176369 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Mon, 28 Feb 2011 11:29:41 +0100 Subject: [PATCH 020/641] First bunch of fixes for today, rest of the guide follows --- doc/arguments.md | 28 +++++++-------- doc/closures.md | 21 ++++++------ doc/constructors.md | 79 ++++++++++++++++++++++++++++--------------- doc/equality.md | 29 +++++++--------- doc/functions.md | 12 +++---- doc/hasownproperty.md | 10 +++--- doc/objects.md | 15 ++++---- doc/prototype.md | 24 ++++++------- doc/scopes.md | 40 +++++++++++----------- doc/this.md | 42 +++++++++++------------ 10 files changed, 162 insertions(+), 138 deletions(-) diff --git a/doc/arguments.md b/doc/arguments.md index c9f9e92f..69623431 100644 --- a/doc/arguments.md +++ b/doc/arguments.md @@ -11,10 +11,10 @@ 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`, +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 -array like methods on it. +standard `Array` methods on it. ### Converting to an array @@ -23,7 +23,7 @@ The code below will return a new `Array` containing all the elements of the Array.prototype.slice.call(arguments); -This conversion is **slow**, it is **not** recommended to use it in performance +This conversion is **slow**, it is **not recommended** to use it in performance critical sections of code. ### Passing arguments @@ -58,11 +58,11 @@ wrappers. ### Modification "magic" -The `arguments` object creates getter and setter functions for both its properties -as well as the function's formal parameters. +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 of the arguments object, and the other way around. +of the corresponding property on the `arguments` object, and the other way around. function foo(a, b, c) { arguments[0] = 2; @@ -79,15 +79,15 @@ of the corresponding property of the arguments object, and the other way around. ### Performance myths and truths -The `arguments` object is always created the only two exceptions being the cases -where it is declared as a name inside of a function or one of its formal +The `arguments` object is always created with the only two exceptions being the +cases where it is declared as a name inside of a function or one of its formal parameters. It does not matter whether it is used or not. -Both getters and setters are **always** created; thus, using it has nearly +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 an access to the arguments object properties. +more than a simple access to the `arguments` object's properties. -> **ES5 Note:** These getters and setters are not created in strict mode. +> **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`. @@ -105,10 +105,10 @@ modern JavaScript engines. That case is the use of `arguments.callee`. 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 due to inlining, it also breaks encapsulation since the -function may now be dependent on being called in a specific context. +performance gains that would arise from inlining, it also breaks encapsulation +since the function may now be dependent on a specific calling context. -It is highly recommended to **never** make use of `arguments.callee` or any of +It is **highly recommended** to **never** make use of `arguments.callee` or any of its properties. > **ES5 Note:** In strict mode, `arguments.callee` will throw a `TypeError` since diff --git a/doc/closures.md b/doc/closures.md index f5709f1c..b1cde1da 100644 --- a/doc/closures.md +++ b/doc/closures.md @@ -2,7 +2,7 @@ One of JavaScript's most powerful features is the availability of *closures*, this means that scopes **always** keep access to the outer scope they were -defined in. Since the only scope that JavaScript has is the +defined in. Since the only scoping that JavaScript has is [function scope](#scopes), all functions, by default, act as closures. ### Emulating private variables @@ -26,7 +26,7 @@ defined in. Since the only scope that JavaScript has is the 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 have access to the `count` variable that was +`Counter` and, therefore, always keep access to the `count` variable that was defined in that very scope. ### Why private variables work @@ -44,7 +44,7 @@ The above code will **not** change the variable `count` in the scope of `Counter since `foo.hack` was not defined in **that** scope. It will instead create - or override - the *global* variable `count`. -### Closures inside loops +### Closures inside of loops One often made mistake is to use closures inside of loops, as if they were copying the value of the loops index variable. @@ -55,18 +55,19 @@ copying the value of the loops index variable. }, 1000); } -The above will **not** output the numbers `0` through `9`, it will simply print +The above will **not** output the numbers `0` through `9`, but will simply print the number `10` ten times. -The *anonymous* function keeps a reference to `i` and at the time `console.log` -gets called, the `for` loop has already finished and the value of `i` is now `10`. +The *anonymous* function keeps a **reference** to `i` and at the time +`console.log` gets called, the `for loop` has already finished and the value of +`i` as been set to `10`. In order to get the desired behavior, it is necessary to create a **copy** of the value of `i`. ### Avoiding the reference problem -In order to copy the value of the loop its index variable, it is best to use an +In order to copy the value of the loop's index variable, it is best to use an [anonymous wrapper](#scopes). for(var i = 0; i < 10; i++) { @@ -77,14 +78,14 @@ In order to copy the value of the loop its index variable, it is best to use an })(i); } -The anonymous outer function gets called immediately with `i` as the first +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`, which value does **not** get changed by the loop. +`e`, whose value does **not** get changed by the loop. There is another possible way of achieving this; that is to return a function -from the anonymous wrapper, which will then have the same behavior as the code +from the anonymous wrapper, that will then have the same behavior as the code above. for(var i = 0; i < 10; i++) { diff --git a/doc/constructors.md b/doc/constructors.md index f99ac54a..6bc977f2 100644 --- a/doc/constructors.md +++ b/doc/constructors.md @@ -3,14 +3,13 @@ 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 +Inside the constructor - the called function - the value of `this` refers to a newly created `Object`. The [`prototype`](#prototype) of this **new** object is -set to the `prototype` of the function object that was called. +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). Otherwise it returns -the value of the `return` statement, **but** only if the return value is an -object. +implicitly returns the value of `this` - the new object. function Foo() { this.bla = 1; @@ -25,10 +24,33 @@ object. The above calls `Foo` as constructor and sets the `prototype` of the newly created object to `Foo.prototype`. -Keep in mind that if you do not use the `new` keyword the function will **not** -return a new object. While it might still work due to the workings of -[`this`](#how-this-works-in-javascript) in JavaScript, it will use the *global* -object as the value of `this`. +In case of an explicit `return` statement the function returns the value +specified that statement, **but only** if the return value is an `Object`. + + function Bar() { + return 2; + } + new Bar(); // a new object + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // the returned object + +When the `new` keyword is omitted, the function will **not** return a new object. + + function Foo() { + this.bla = 1; // gets set on the global object + } + Foo(); // undefined + +While the above example might still appear to work in some cases, due to the +workings of [`this`](#how-this-works-in-javascript) in JavaScript, it will +use the *global object* as the value of `this`. ### Factories @@ -50,24 +72,25 @@ explicitly return a value. new Bar(); Bar(); -Both these calls return the exact same thing, a newly create object which -has a property called `method` which is a [Closure](#closures-and-references). +Both calls to `Bar` return the exact same thing, a newly create object which +has a property called `method` on it, that is a +[Closure](#closures-and-references). -Also note that the call `new Bar()` does **not** affect the prototype of the -returned object. While the prototype will be set on the newly created object, -`Bar` never returns that object. +It is also to note that the call `new Bar()` does **not** affect the prototype +of the returned object. While the prototype will be set on the newly created +object, `Bar` never returns that new object. -So in the above example there is no functional difference between using and -omitting the `new` keyword. +In the above example, there is no functional difference between using and +not using the `new` keyword. ### Creating new objects via factories -An often made recommendation is to **not** use `new` since forgetting the use of -it may lead to a lot of bugs. +An often made recommendation is to **not** use `new` since forgetting its use +may lead to bugs. -In order to create new object one now has to use a factory and set up the new -object inside it. +In order to create new object, one should rather use a factory and construct a +new object inside of that factory. function Foo() { var obj = {}; @@ -84,19 +107,21 @@ object inside it. return obj; } -While the above is robust against forgetting to use `new` and makes the use of -[private variables](#closures) certainly easier, it comes with some down sides. +While the above is robust against a missing `new` keyword and certainly makes +the use of [private variables](#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 - 3. It somehow goes against the spirit of the language, by dropping prototype - chain just because a left out `new` keyword can break code + object or put that object on the prototype of the new object. + 3. Dropping the prototype chain just because of a left out `new` keyword + somehow goes against the spirit of the language. ### In conclusion While omitting the `new` keyword might lead to bugs, it is certainly **not** a reason to drop the use of prototypes altogether. In the end it comes down to -which solution is better suited for the needs of the application, it is especially -important to choose a specific style of object creation **and** stick with it. +which solution is better suited for the needs of the application, it is +especially important to choose a specific style of object creation **and stick** +with it. diff --git a/doc/equality.md b/doc/equality.md index 63ca5612..54f7fe51 100644 --- a/doc/equality.md +++ b/doc/equality.md @@ -1,14 +1,13 @@ ## Equality and comparisons -JavaScript has two different ways of comparing the values of objects for else -equality. +JavaScript has two different ways of comparing the values of objects for equality. ### The equals operator The equals operator consists of two equal signs: `==` -JavaScript features *weak typing*, that means, the equals operator does -**coerce** types in order to compare them. +JavaScript features *weak typing*, that means, that the equals operator +**coerces** types in order to compare them. "" == "0" // false 0 == "" // true @@ -21,19 +20,19 @@ JavaScript features *weak typing*, that means, the equals operator does " \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 regarded as bad practice, it introduces hard to track down -bugs due to its complicated conversion rules. +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 -with another number. +to another number. ### The strict equals operator The strict equals operator consists of **three** equal signs: `===` Other than the normal equals operator, the strict equals operator does **not** -coerce the types of its operands. +perform type coercion between its operands. "" === "0" // false 0 === "" // false @@ -45,11 +44,9 @@ coerce the types of its operands. null === undefined // false " \t\r\n" === 0 // false -The above results not only make a lot more sense, they also get rid of most of -the weak typing in the language. This makes writing code a lot easier since -things will break earlier and a lot of subtle bugs can be avoided. - -It will also be a lot faster when the operands are of different types. +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 @@ -62,13 +59,13 @@ different when at least one of their operands happens to be an `Object`. var foo = {}; foo === foo; // true -Here both operators compare for **identity** and not equality - that is, they +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 a pointer comparison in C do. ### In conclusion -It is highly recommended to **only** use the strict equals operator. In cases +It is highly recommended to only use the **strict equals** operator. In cases where types need to be coerced, it should be done [explicitly](#casting) and not -left to the "magic" of the languages complicated coercing rules. +left to the language's complicated coercing rules. diff --git a/doc/functions.md b/doc/functions.md index cc6ac5ca..fded3d79 100644 --- a/doc/functions.md +++ b/doc/functions.md @@ -1,6 +1,6 @@ ## Functions -Functions in JavaScript are first class objects, which means that they can be +Functions in JavaScript are first class objects, that means that they can be passed around like any other value. One common use of this feature is to pass an *anonymous function* as a callback to another, possibly asynchronous function. @@ -19,7 +19,7 @@ called before the actual definition in the source. var foo = function() {}; -The above assigns the unnamed and - *anonymous* - function to the variable `foo`. +This example assigns the unnamed and *anonymous* function to the variable `foo`. foo; // 'undefined' foo(); // this raises a TypeError @@ -29,7 +29,7 @@ Due to the fact that `var` is a *statement*, which - just like the function declaration - creates the variable `foo` before the actual execution of the code starts, `foo` is already defined when the script gets executed. -Since assignments only happens at runtime, the value of `foo` will default +But since assignments only happens at runtime, the value of `foo` will default to [undefined](#undefined) before the corresponding code is executed. ### Named function expression @@ -42,9 +42,9 @@ Another special case is the assignment of named functions. 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 +assigned to `foo`; however, inside of `bar` it is available. This is due to how [name resolution](#scopes) in JavaScript works, the name of the function -is always made available in the local scope of the function itself. +is *always* made available in the local scope of the function itself. ### The `var` statement @@ -68,7 +68,7 @@ assign the value of `2` to the *local* variable `bar` of `test`. Also, while the statements inside the `if` block never get executed, the variable `foo` still gets created and defaults to the value of `undefined`; again, this -is due to the lack of block scoping. +is due to the lack of block scoping and the workings of hoisting. ### Order of parsing diff --git a/doc/hasownproperty.md b/doc/hasownproperty.md index fe9638e7..2fb6df1d 100644 --- a/doc/hasownproperty.md +++ b/doc/hasownproperty.md @@ -1,6 +1,6 @@ ## `hasOwnProperty` -In order to check whether a object has a property defined on itself and **not** +In order to check whether a object has a property defined on *itself* and **not** somewhere on its [prototype chain](#prototype), it is necessary to use the `hasOwnProperty` method which all objects inherit from `Object.prototype`. @@ -21,14 +21,14 @@ does **not** traverse the prototype chain. foo.hasOwnProperty('bar'); // false foo.hasOwnProperty('goo'); // true -Only `hasOwnProperty` will give the correct and expected result. This is -essential when iterating over the properties of any object. There is no other -way to exclude properties that are not defined on the object **itself**, but +Only `hasOwnProperty` will give the correct and expected result, this is +essential when iterating over the properties of any object. There is **no** other +way to exclude properties that are not defined on the object *itself*, but somewhere on its prototype chain. ### `hasOwnProperty` as a property -JavaScript does not protect the property name `hasOwnProperty`; therefore, if the +JavaScript does **not** protect the property name `hasOwnProperty`; thus, if the possibility exists that an object might have a property with this name, it is necessary to use an *external* `hasOwnProperty` in order to get correct results. diff --git a/doc/objects.md b/doc/objects.md index 6a803982..7d968cf6 100644 --- a/doc/objects.md +++ b/doc/objects.md @@ -17,7 +17,7 @@ notation* on a number as a floating point literal. 2.toString(); // raises SyntaxError There are a couple of workarounds which can be used in order make number -literals act as object too. +literals act as objects too. 2..toString(); // the second point is correctly recognized 2 .toString(); // note the space left to the dot @@ -28,9 +28,9 @@ literals act as object too. Objects in JavaScript can also be used as a [*Hashmap*][1], they mainly consist of named properties mapping to values. -Using the curly brace notation `{}` one can create a plain object. This new -object [inherits](#prototype) from `Object.prototype` and has no -[own properties](#hasownproperty) defined on it. +Using a object literal - curly brace notation `{}` - it is possible to create a +plain object. This new object [inherits](#prototype) from `Object.prototype` and +has no [own properties](#hasownproperty) defined on it. var foo = {}; // a new empty object @@ -88,11 +88,12 @@ removed and is therefore missing from the output. }; Object properties can be both notated as plain characters and as strings. Due to -another mis-design in JavaScript's parser, prior to ECMAScript 5 the above will throw -a `SyntaxError`. +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* of the language; -therefore, it must be notated as a string literal in order to ensure working +therefore, it must be notated as a *string literal* in order to ensure working code under older JavaScript engines. [1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/doc/prototype.md b/doc/prototype.md index dca688c1..25c074e9 100644 --- a/doc/prototype.md +++ b/doc/prototype.md @@ -1,12 +1,12 @@ ## The prototype -JavaScript does not feature the classical inheritance model, instead it uses a +JavaScript does not feature a classical inheritance model, instead it uses a *prototypical* one. While this is often considered to be one of JavaScript's weaknesses, the prototypical inheritance model is in fact more powerful than the classic model. -For example, it is fairly trivial to build a classic model on top of it, while -the other way around is a far more difficult task. +It is for example fairly trivial to build a classic model on top of it, while the +other way around is a far more difficult task. Due to the fact that JavaScript is basically the only widely used language that features prototypical inheritance, it takes some time to adjust to the @@ -16,8 +16,8 @@ The first major difference is that inheritance in JavaScript is done by using so called *prototype chains*. > **Note:** Simply using `Bar.prototype = Foo.prototype` will result in both objects -> sharing the **same** prototype. Therefore, changes to either object its prototype -> will affect the other its prototype as well, which in most cases is not the +> 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() { @@ -95,19 +95,19 @@ other built in prototypes. This technique is called [monkey patching][1] and breaks *encapsulation*. While used by widely spread frameworks such as [Prototype][2], there is still no good -reason for cluttering built in types with additional non-standard functionality. +reason for cluttering built in types with additional *non-standard* functionality. -The **only** good reason for extending a built in prototype is to back port +The **only** good reason for extending a built-in prototype is to backport the features of newer JavaScript engines; for example, [`Array.forEach`][3]. ### In conclusion -It is a must to understand the prototypical inheritance model completely before -writing complex code which makes use of it. Also, watching the length of the -prototype chains and breaking them up if necessary can avoid possible performance -issues. Further, the native prototypes should **never** be extended unless it is -for the sake of compatibility with newer JavaScript features. +It is a **must** to understand the prototypical inheritance model completely +before writing complex code which makes use of it. Also, watching the length of +the prototype chains and breaking them up if necessary can avoid possible +performance issues. Further, the native prototypes should **never** be extended +unless it is for the sake of compatibility with newer JavaScript features. [1]: http://en.wikipedia.org/wiki/Monkey_patch [2]: http://prototypejs.org/ diff --git a/doc/scopes.md b/doc/scopes.md index 1dd6a797..82083a16 100644 --- a/doc/scopes.md +++ b/doc/scopes.md @@ -16,8 +16,8 @@ is in the language is *function scope*. > **not** as an object literal. This, in conjunction with > [automatic insertion of semicolons](#semicolon), can lead to subtle errors. -There are also no distinct namespaces in JavaScript. This means that everything -gets defined in **one** globally shared namespace. +There are also no distinct namespaces in JavaScript, that means that everything +gets defined in one *globally shared* namespace. Each time a variable is referenced, JavaScript will traverse upwards through all the scopes until it finds it. In the case that it reaches the global scope and @@ -35,7 +35,7 @@ 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 +Again, that is **not** at all the *same effect*, not using `var` can have major implications. // global scope @@ -49,8 +49,8 @@ implications. 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 hard to -track down bugs. +thousands of lines of JavaScript and not using `var` will introduce horrible and +hard to track down bugs. // global scope var items = [/* some list */]; @@ -67,8 +67,8 @@ track down bugs. 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. +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 @@ -94,28 +94,28 @@ the assignment of `bar` will override the global variable with the same name. ### Name resolution order -All scopes in JavaScript - including the global one - have the name -[this](#this) defined in them, which refers to the "current object". +All scopes in JavaScript, including the *global scope*, have the special name +[`this`](#this) defined in them, which refers to the *current object*. -Function scopes also have the name [arguments](#arguments) defined, which +Function scopes also have the name [`arguments`](#arguments) defined, 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's a `var foo` statement in the current scope use that. + 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 **override** the default -> `arguments` object. +> **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 be -easily avoided with the help of anonymous *function wrappers*. +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" @@ -136,7 +136,7 @@ callable, they must first be evaluated. () // call the result of the evaluation There are other ways for evaluating and calling the function expression; which, -while different in syntax, do the exact same thing. +while different in syntax, do behave the exact same way. // Two other ways +function(){}(); @@ -145,9 +145,9 @@ while different in syntax, do the exact same thing. ### In conclusion It is recommended to always use an *anonymous wrapper* for encapsulating code in -its own namespace. This does not only protect the code against name clashes, it -also allows for better modularization. +its own namespace. This does not only protect code against name clashes, it +also allows for better modularization of programs. -Additionally, the use of global variables is considered **bad practice**, any use -of them indicates badly written code that is prone to errors and hard to maintain. +Additionally, the use of global variables is considered **bad practice**. **Any** +use of them indicates badly written code that is prone to errors and hard to maintain. diff --git a/doc/this.md b/doc/this.md index 2a0273f0..b3acdd4f 100644 --- a/doc/this.md +++ b/doc/this.md @@ -1,8 +1,8 @@ ## How `this` works -JavaScript has a different concept of what `this` refers to than most other -languages do. There are exactly **five** different ways in which the value of `this` -can be bound in the language. +JavaScript has a different concept of what the special name `this` refers to +than most other programming languages do. There are exactly **five** different +ways in which the value of `this` can be bound in the language. ### The global scope @@ -17,8 +17,8 @@ When using `this` in global scope, it will simply refer to the *global* object. Here `this` will again refer to the *global* object. -> **ES5 Note:** In strict mode, `this` will **no longer** refer to the global object. -> It will instead have the value of `undefined`. +> **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 @@ -31,8 +31,8 @@ In this example `this` will refer to `test`. new foo(); A function call that is preceded by the `new` keyword acts as -a [constructor](#constructors). Inside the function `this` will refer to a newly -created `Object`. +a [constructor](#constructors). Inside the function `this` will refer to a *newly +created* `Object`. ### Explicit setting of `this` @@ -43,15 +43,15 @@ created `Object`. 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. +`this` inside the called function gets **explicitly set** to the first argument +of the corresponding function call. -In the above example the *method case* does **not** apply, and `this` inside of -`foo` will be set to `bar`. +As a result, the above example the *method case* does **not** apply, and `this` +inside of `foo` will be set to `bar`. > **Note:** `this` **cannot** be used to refer to the object inside of an `Object` > literal. So `var obj = {me: this}` will **not** result in `me` referring to -> `obj`, since `this` only gets bound by one of the above five cases. +> `obj`, since `this` only gets bound by one of the five listed cases. ### Common pitfalls @@ -65,8 +65,8 @@ mis-design of the language, as it **never** has any practical use. test(); } -A common misconception is that `this` inside of `test` refers to `Foo`, but it -does **not**. +A common misconception is that `this` inside of `test` refers to `Foo`, while in +fact it **does not**. In order to gain access to `Foo` from within `test` it is necessary to create a local variable inside of `method` which refers to `Foo`. @@ -85,17 +85,17 @@ pass `this` values around. ### Assigning methods -Another thing that does **not** work in JavaScript is **assigning** a method -reference to a variable. +Another thing that does **not** work in JavaScript is function aliasing, that is, +**assigning** a method to a variable. var test = someObject.methodTest; test(); -Again due to the first case `test` now acts like like a plain function call; -therefore, `this` inside it will no longer refer to `someObject`. +Due to the first case `test` now acts like 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, it is in fact what -makes [prototypical inheritance](#prototype) work. +While the late binding of `this` might seem like a bad idea at first, it is in +fact what makes [prototypical inheritance](#prototype) work. function Foo() {} Foo.prototype.method = function() {}; @@ -106,5 +106,5 @@ makes [prototypical inheritance](#prototype) work. new Bar().method(); When `method` gets called on a instance of `Bar`, `this` will now refer to that -instance. +very instance. From e8998df7301f388e384028fe326207459dd909e4 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Mon, 28 Feb 2011 11:46:30 +0100 Subject: [PATCH 021/641] Two more fixes for the first half of the guide --- doc/closures.md | 2 +- doc/equality.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/closures.md b/doc/closures.md index b1cde1da..64b9e287 100644 --- a/doc/closures.md +++ b/doc/closures.md @@ -44,7 +44,7 @@ The above code will **not** change the variable `count` in the scope of `Counter since `foo.hack` was not defined in **that** scope. It will instead create - or override - the *global* variable `count`. -### Closures inside of loops +### Closures inside loops One often made mistake is to use closures inside of loops, as if they were copying the value of the loops index variable. diff --git a/doc/equality.md b/doc/equality.md index 54f7fe51..ba7971da 100644 --- a/doc/equality.md +++ b/doc/equality.md @@ -67,5 +67,5 @@ and a pointer comparison in C do. It is highly recommended to only use the **strict equals** operator. In cases where types need to be coerced, it should be done [explicitly](#casting) and not -left to the language's complicated coercing rules. +left to the language's complicated coercion rules. From 3be18f831f7399a2b9b23cfa943a39bb671e8b46 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Mon, 28 Feb 2011 14:02:49 +0100 Subject: [PATCH 022/641] And the rest of the fixes. More proof reads will follow --- doc/arrayctor.md | 13 +++++++------ doc/arrays.md | 14 ++++++-------- doc/casting.md | 30 ++++++++++++++---------------- doc/eval.md | 18 +++++++++--------- doc/forinloop.md | 8 ++++---- doc/instanceof.md | 6 +++--- doc/objects.md | 2 +- doc/semicolon.md | 10 +++++----- doc/timeouts.md | 32 ++++++++++++++++---------------- doc/typeof.md | 6 +++--- doc/undefined.md | 32 +++++++++++++++++--------------- 11 files changed, 85 insertions(+), 86 deletions(-) diff --git a/doc/arrayctor.md b/doc/arrayctor.md index 938e9184..1054b7de 100644 --- a/doc/arrayctor.md +++ b/doc/arrayctor.md @@ -1,7 +1,8 @@ ## The `Array` constructor Since the `Array` constructor is ambiguous in how it deals with its parameters, -it is recommended to always use the `[]` notation when creating new arrays. +it is highly recommended to always use the array literals - `[]` notation - +when creating new arrays. [1, 2, 3]; // Result: [1, 2, 3] new Array(1, 2, 3); // Result: [1, 2, 3] @@ -10,18 +11,18 @@ it is recommended to always use the `[]` notation when creating new arrays. new Array(3); // Result: [undefined, undefined, undefined] new Array('3') // Result: ['3'] -In cases when there is only one argument being passed to the `Array` constructor, +In cases when there is only one argument passed to the `Array` constructor, and that argument is a `Number`, the constructor will use that number as the *length* of the new array to be created. This behavior only comes in handy in a few cases, like repeating a string, in -which it avoids the use of a `for` loop. +which it avoids the use of a `for loop` code. new Array(count + 1).join(stringToRepeat); ### In conclusion -The use of the `Array` constructor should be avoided as much as possible. The `[]` -notation is definitely preferred. It is shorter and has a clearer syntax; thus, -it also increases the readability of code. +The use of the `Array` constructor should be avoided as much as possible. +Literals are definitely preferred. They are shorter and have a clearer syntax; +therefore, they also increase the readability of the code. diff --git a/doc/arrays.md b/doc/arrays.md index a5adcc14..173dd339 100644 --- a/doc/arrays.md +++ b/doc/arrays.md @@ -1,15 +1,15 @@ ## Arrays Although arrays in JavaScript are objects, there are no good reasons to use -the [for in loop](#forinloop) in for iteration on them. In fact there are a +the [`for in loop`](#forinloop) in for iteration on them. In fact there are a number of good reasons **against** the use of `for in` on arrays. > **Note:** JavaScript arrays are **not** *associative arrays*. JavaScript only > has [objects](#objects) for mapping keys to values. And while associative -> arrays **preserve** order, objects do **not**. +> arrays **preserve** order, objects **do not**. -Since the `for in` loop enumerates all properties on the prototype chain and -the only way to exclude those properties is to use +Since the `for in` loop enumerates all the properties that are on the prototype +chain and the only way to exclude those properties is to use [`hasOwnProperty`](#hasownproperty), it is already up to **twenty times** slower than a normal `for` loop. @@ -23,7 +23,7 @@ to use the classic `for` loop. console.log(list[i]); } -There is one extra catch in the above example, which is the caching of the +There is one extra catch in the above example, that is the caching of the length of the array via `l = list.length`. Although the `length` property is defined on the array itself, there is still an @@ -54,7 +54,5 @@ does not have any effect on the array. 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. Additionally, -never should any assumptions be made whether the JavaScript engine will apply -optimization to the code or not. +badly written code that is prone to bugs and bad performance. diff --git a/doc/casting.md b/doc/casting.md index dcd1d61f..8388dc6a 100644 --- a/doc/casting.md +++ b/doc/casting.md @@ -1,7 +1,7 @@ ## Type casting JavaScript is a *weakly typed* language, so it will apply *type coercion* -wherever possible. +**wherever** possible. // These are true new Number(10) == 10; // Number.toString() is converted @@ -21,13 +21,11 @@ wherever possible. > (Base 8). Octal support for these has been **removed** in ECMAScript 5 strict > mode. -In order to avoid that the use of the [strict equal operator](#equality) is -recommended. +In order to avoid the above, use of the [strict equal operator](#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. -But this does still not solve all the issues that arise from JavaScript's weak -typing system. - -### Madness with `new` and built in types +### 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. @@ -36,30 +34,30 @@ differently when being used with the `new` keyword and without it. Number(10) === 10; // True, Number and Number new Number(10) + 0 === 10; // True, due to implicit conversion -Using the built in type like `Number` as a constructor will create a new number -`Object`, but leaving out the `new` keyword will make it behave like a converter. +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 activate more -coercing magic. +In addition, having literals or non-object values in there will result in even +more type coercion. -The best option is to do cast to one of the three possible types explicitly. +The best option is to cast to one of the three possible types **explicitly**. ### Casting to a string '' + 10 === '10'; // true -By using a empty string a value can easily be casted to a plain string. +By prepending a empty string a value can easily be casted to a string. ### Casting to a number +'10' === 10; // true -Using the **unary** plus operator it is possible to cast to a plain number. +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 its boolean -value. +By using the **not** operator twice, a value can be converted a boolean. !!'foo'; // true !!''; // false diff --git a/doc/eval.md b/doc/eval.md index f0092904..fe585df7 100644 --- a/doc/eval.md +++ b/doc/eval.md @@ -11,8 +11,8 @@ The `eval` function will execute a string of JavaScript code in the local scope. test(); // 3 foo; // 1 -But `eval` only executes in local scope when it is being called directly **and** -the name of the function that was actually called is `eval`. +But `eval` only executes in local scope when it is being called **directly** *and* +the name of the called function is actually `eval`. var foo = 1; function test() { @@ -35,14 +35,14 @@ scope since `eval` is not being called directly in that case. ### Security issues -Also, `eval` is a security problem as it executes **any** code given to it, -it should be **NEVER** used with strings of unknown or untrusted origins. +`eval` also is a security problem as it executes **any** code given to it, +it should **never** be used with strings of unknown or untrusted origins. ### In conclusion -`eval` is **EVIL**. It should never be used, any code that makes use of it is to -be questioned in its workings and security. If something requires `eval` in -order to work, it is to be considered as magic and should **not** be used in the -first place, a **better** design should be used that does not require the use of -`eval`. +`eval` is **evil**. Never should it be used, any code that makes use of it is to +be questioned in both its workings and security. In case something requires `eval` +in order to work, its design is to be questioned and should **not** be used in +the first place, a *better design* should be used, that does not require the use +of `eval`. diff --git a/doc/forinloop.md b/doc/forinloop.md index 855bd1f0..655d8f1f 100644 --- a/doc/forinloop.md +++ b/doc/forinloop.md @@ -16,9 +16,9 @@ chain when iterating over the properties of an object. } 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 itself, +is necessary to filter out the unwanted properties inside the loop body , this is done by using the [`hasOwnProperty`](#hasownproperty) method of -objects. +`Object.prototype`. > **Note:** Since the `for in` always traverses the complete prototype chain, it > will get slower with each additional layer of inheritance added to an object. @@ -34,8 +34,8 @@ objects. This version is the only correct one to use. Due to the use of `hasOwnPropery` it will **only** print out `moo`. When `hasOwnProperty` is left out, the code is -prone to errors when the native prototypes (for example, -`Object.prototype`) have been extended. +prone to errors in cases where the native prototypes - e.g. `Object.prototype` - +have been extended. One widely used framework which does this is [Prototype][1]. When this framework is included, `for in` loops that do not use `hasOwnProperty` are diff --git a/doc/instanceof.md b/doc/instanceof.md index 913cc33f..18bd1032 100644 --- a/doc/instanceof.md +++ b/doc/instanceof.md @@ -1,7 +1,7 @@ ## The `instanceof` operator The `instanceof` operator compares the constructors of its two operands. It is -only useful when comparing custom made objects. Using it on built in types is +only useful when comparing custom made objects. Used on built-in types, it is nearly as useless as the [typeof operator](#typeof). ### Comparing custom objects @@ -26,8 +26,8 @@ nearly as useless as the [typeof operator](#typeof). 'foo' instanceof String; // false 'foo' instanceof Object; // false -One important thing to note is that `instanceof` does of course not work on -objects that origin from different JavaScript contexts (e.g. different documents +One important thing to note here is, that `instanceof` does not work on objects +that origin from different JavaScript contexts (e.g. different documents in a web browser), since their constructors will not be the exact same object. ### In conclusion diff --git a/doc/objects.md b/doc/objects.md index 7d968cf6..480df3bb 100644 --- a/doc/objects.md +++ b/doc/objects.md @@ -28,7 +28,7 @@ literals act as objects too. Objects in JavaScript can also be used as a [*Hashmap*][1], they mainly consist of named properties mapping to values. -Using a object literal - curly brace notation `{}` - it is possible to create a +Using a object literal - `{}` notation - it is possible to create a plain object. This new object [inherits](#prototype) from `Object.prototype` and has no [own properties](#hasownproperty) defined on it. diff --git a/doc/semicolon.md b/doc/semicolon.md index 19d85f5d..0f9b1a58 100644 --- a/doc/semicolon.md +++ b/doc/semicolon.md @@ -19,7 +19,7 @@ Insertion happens, and the parser tries again. test() The automatic insertion of semicolon is considered to be one of **biggest** -design flaws in the language as it *can* change the behavior of code. +design flaws in the language, as it *can* change the behavior of code. ### How it works @@ -88,11 +88,11 @@ Below is the result of the parser's "guessing" game. > 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. +it does the **wrong thing**. ### Leading parenthesis -In case of a leading parenthesis, the parse will **not** insert a semicolon. +In case of a leading parenthesis, the parser will **not** insert a semicolon. log('testing!') (options.list || []).forEach(function(i) {}) @@ -101,8 +101,8 @@ 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 `TypeError` saying that `undefined is not a function`. +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 diff --git a/doc/timeouts.md b/doc/timeouts.md index cd25a8e7..8b0c8998 100644 --- a/doc/timeouts.md +++ b/doc/timeouts.md @@ -11,16 +11,16 @@ function by using the `setTimeout` and `setInterval` functions. 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 executed exactly **once**. +`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 timeout they specified when calling `setTimeout`. +gets executed might block the thread, it is by **no means** a safe bet that one +will get the exact delay that was specified in the `setTimeout` call. The function that was passed as the first parameter will get called by the -global object, that means that [this](#this) inside the called function refers -to that very object. +*global object*, that means, that [`this`](#this) inside the called function +refers to that very object. function Foo() { this.value = 42; @@ -86,7 +86,7 @@ the first place. ### Clearing all timeouts -As there is no built in method for clearing all timeouts and/or intervals, +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 @@ -96,7 +96,7 @@ it is necessary to use brute force in order to achieve this functionality. 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 one by one. +they can be cleared specifically. ### Hidden `eval` magic @@ -115,12 +115,12 @@ This feature should **never** be used, since it internally makes use of `eval`. } bar(); -Since `eval` is not getting [called directly](#eval) here, 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`. +Since `eval` is not getting called [directly](#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 to pass arguments to the -function that will get called. +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) {} @@ -140,11 +140,11 @@ function that will get called. **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 which handles the actual calling. +to be supplied to the function that gets called. An *anonymous function* should +be passed that then takes care of the actual call. -Additionally, `setInterval` should be avoided since it is hard to control and -does not adjust to the single threaded nature of the language. +Further, the use of `setInterval` should be avoided since its scheduler is not +blocked by executing JavaScript. [1]: http://en.wikipedia.org/wiki/Document_Object_Model *[DOM]: Document Object Model diff --git a/doc/typeof.md b/doc/typeof.md index bfe850d7..8e325238 100644 --- a/doc/typeof.md +++ b/doc/typeof.md @@ -33,8 +33,8 @@ object. {} Object object new Object() Object object -In the above table *Type* refers to the value the `typeof` operator returns. As -you can see this is anything but consistent. +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. @@ -43,7 +43,7 @@ The *Class* refers to the value of the internal `[[Class]]` property of an objec > `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. In order to retrieve the value of `[[Class]]` one can has to make use of the -`toString` method of `Object`. +`toString` method of `Object.prototype`. ### The Class of an object diff --git a/doc/undefined.md b/doc/undefined.md index 0e478cbb..425192b0 100644 --- a/doc/undefined.md +++ b/doc/undefined.md @@ -1,6 +1,6 @@ ## `undefined` and `null` -JavaScript has two distinct values for "nothing", the more useful of those two +JavaScript has two distinct values for `nothing`, the more useful of these two being `undefined`. ### The value `undefined` @@ -9,7 +9,8 @@ being `undefined`. The language also defines a global variable that has the value of `undefined`, this variable is also called `undefined`. But this variable is **not** a constant, -meaning that it can be easily overwritten which then leads to abstruse bugs. +nor is it a keyword of the language. This means that its *value* can be easily +overwritten. > **ES5 Note:** `undefined` in ECMAScript 5 is **no longer** *writable* in strict > mode, but its name can still be shadowed by for example a function with the name @@ -17,24 +18,25 @@ meaning that it can be easily overwritten which then leads to abstruse bugs. Some examples for when the value `undefined` is returned: - - Accessing the (unmodified) global variable `undefined` - - Implicit returns of functions due to missing `return` statements - - `return` statements which don't explicitly return anything - - Lookups of non existent properties - - Function parameters which don't had any explicit value passed - - Anything that has been set to the value of `undefined` + - Accessing the (unmodified) global variable `undefined`. + - Implicit returns of functions due to missing `return` statements. + - `return` statements which do not explicitly return anything. + - Lookups of non-existent properties. + - Function parameters which do not had any explicit value passed. + - Anything that has been set to the value of `undefined`. -### The case of the "overridden" `undefined` +### Handling changes to the value of `undefined` -Since the variable `undefined` only has the value of `undefined`, changing its -value does not change the value of the **type** `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` in the first place. +necessary to retrieve the value of `undefined` first. In order to protect code against a possible overwritten `undefined` variable, a -common technique used is to add an additional parameter to the encapsulation -[anonymous wrapper](#scopes), which gets no argument passed to it. +common technique used is to add an additional parameter to an +[anonymous wrapper](#scopes), that gets no argument passed to it. var undefined = 123; (function(something, foo, undefined) { @@ -60,7 +62,7 @@ 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) +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 From d80c82ef3d62ad45a7f0df45c67e13ade7eb8e1f Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 5 Mar 2011 20:20:40 +0100 Subject: [PATCH 023/641] Finally add the hoisting stuff and clean up the functions section --- doc/functions.md | 44 ++++--------------------------------- doc/scopes.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 40 deletions(-) diff --git a/doc/functions.md b/doc/functions.md index fded3d79..dc794942 100644 --- a/doc/functions.md +++ b/doc/functions.md @@ -8,7 +8,7 @@ an *anonymous function* as a callback to another, possibly asynchronous function function foo() {} -The above function gets created **before** the execution of the program starts; +The above function gets [hoisted](#scopes) before the execution of the program starts; thus, it is available *everywhere* in the scope it was *defined* in, even if called before the actual definition in the source. @@ -25,9 +25,9 @@ This example assigns the unnamed and *anonymous* function to the variable `foo`. foo(); // this raises a TypeError var foo = function() {}; -Due to the fact that `var` is a *statement*, which - just like the function -declaration - creates the variable `foo` before the actual execution of the code -starts, `foo` is already defined when the script gets executed. +Due to the fact that `var` is a declaration, that hoists the variable name `foo` +before the actual execution of the code starts, `foo` is already defined when +the script gets executed. But since assignments only happens at runtime, the value of `foo` will default to [undefined](#undefined) before the corresponding code is executed. @@ -46,39 +46,3 @@ assigned to `foo`; however, inside of `bar` it is available. This is due to how [name resolution](#scopes) in JavaScript works, the name of the function is *always* made available in the local scope of the function itself. -### The `var` statement - - function test() { - if (foo) { - bar = 2; - - } else { - var bar = 1; - } - return foo; - } - - if (false) { - var foo = 1; - } - -Since there is **no** [block scope](#scopes) in JavaScript, the above will -**not** assign the value `2` to the *global* variable `bar`. Instead, it will -assign the value of `2` to the *local* variable `bar` of `test`. - -Also, while the statements inside the `if` block never get executed, the variable -`foo` still gets created and defaults to the value of `undefined`; again, this -is due to the lack of block scoping and the workings of hoisting. - -### Order of parsing - -All `var` statements get parsed **before** `function` declarations; hence, -subsequent statements will override the previous ones. - - function foo() {} - var foo; - - foo; // [function foo] - var foo = 2; - foo; // 2 - diff --git a/doc/scopes.md b/doc/scopes.md index 82083a16..5331d93d 100644 --- a/doc/scopes.md +++ b/doc/scopes.md @@ -92,6 +92,62 @@ parameters and variables that were declared via the `var` statement. 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, that means that both `var` statements and +`function` declarations will get moved to the top of their respective 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 is started, JavaScript moves +the `var` statements, followed by the `function` declarations, at the top of the +next surrounding scope. + + var bar, someValue; // default to undefined + + // test got moved up + 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; + bar = function() {}; + + test(); + +The 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 it seemed like the `if` statement would modify the *global +variable* `goo` while it reallydoes modify the *local variable* after hoisting +has been applied. + ### Name resolution order All scopes in JavaScript, including the *global scope*, have the special name From 7986de35a8a02ca42997fd3fa8a6077498d55e33 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sun, 6 Mar 2011 12:38:00 +0100 Subject: [PATCH 024/641] Some small additions --- doc/scopes.md | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/doc/scopes.md b/doc/scopes.md index 5331d93d..71a120d7 100644 --- a/doc/scopes.md +++ b/doc/scopes.md @@ -118,9 +118,10 @@ The above code gets transformed before execution is started, JavaScript moves the `var` statements, followed by the `function` declarations, at the top of the next surrounding scope. - var bar, someValue; // default to undefined + // var statements got moved here + var bar, someValue; // default to 'undefined' - // test got moved up + // the function declartion got moved up too function test(data) { var goo, i, e; // missing block scope moves these here if (false) { @@ -134,8 +135,8 @@ next surrounding scope. } } - bar(); // fails with a TypeError since bar is still undefined - someValue = 42; + bar(); // fails with a TypeError since bar is still 'undefined' + someValue = 42; // assignments are not affected by hoisting bar = function() {}; test(); @@ -145,9 +146,29 @@ their bodies, it will also make the results of certain `if` constructs non-intuitive. In the original code it seemed like the `if` statement would modify the *global -variable* `goo` while it reallydoes modify the *local variable* after hoisting +variable* `goo` while it really does modify the *local variable* after hoisting has been applied. +Without the knowledge about *hoisting*, below code might look like it would +raise a `ReferenceError`. + + // check whether SomeImportantThing has been initiliazed + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +But of course the above code works due to the `var` statement being moved to the +top of the global scope. + + var SomeImportantThing; + + // other code might initiliaze SomeImportantThing here, or not + + // make sure it's there + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + ### Name resolution order All scopes in JavaScript, including the *global scope*, have the special name From 9126bb454fe7bd9430fbc2924fa9b1896dcada0a Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Fri, 11 Mar 2011 07:15:27 -0800 Subject: [PATCH 025/641] Fixed typo. --- doc/typeof.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/typeof.md b/doc/typeof.md index 8e325238..9345ebf2 100644 --- a/doc/typeof.md +++ b/doc/typeof.md @@ -10,7 +10,7 @@ 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 there return value will be used as the operand of the +> 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 From 289edf5aaf99a4eadff5fe4eb2a7e6a838917251 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Fri, 11 Mar 2011 07:17:32 -0800 Subject: [PATCH 026/641] Fixed a typo. --- doc/typeof.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/typeof.md b/doc/typeof.md index 9345ebf2..f1fe3a88 100644 --- a/doc/typeof.md +++ b/doc/typeof.md @@ -42,7 +42,7 @@ The *Class* refers to the value of the internal `[[Class]]` property of an objec > following strings. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, > `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. -In order to retrieve the value of `[[Class]]` one can has to make use of the +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 From 4366dc484517c8cc987660b68936e83d7d848c2c Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Fri, 11 Mar 2011 07:18:22 -0800 Subject: [PATCH 027/641] Fixed a typo. --- doc/typeof.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/typeof.md b/doc/typeof.md index f1fe3a88..8d7133f8 100644 --- a/doc/typeof.md +++ b/doc/typeof.md @@ -48,7 +48,7 @@ In order to retrieve the value of `[[Class]]` one has to make use of the ### The Class of an object The specification gives exactly one way of accessing the `[[Class]]` value, -which the use of `Object.prototype.toString`. +with the use of `Object.prototype.toString`. function is(type, obj) { var clas = Object.prototype.toString.call(obj).slice(8, -1); From 91abdaf08d5a287e20209186eea00e3b83248d93 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Fri, 11 Mar 2011 20:41:02 +0100 Subject: [PATCH 028/641] Small change in wording --- doc/scopes.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/scopes.md b/doc/scopes.md index 71a120d7..84e4095e 100644 --- a/doc/scopes.md +++ b/doc/scopes.md @@ -94,8 +94,8 @@ the assignment of `bar` will override the global variable with the same name. ### Hoisting -JavaScript **hoists** declarations, that means that both `var` statements and -`function` declarations will get moved to the top of their respective scope. +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() {}; @@ -114,9 +114,9 @@ JavaScript **hoists** declarations, that means that both `var` statements and } } -The above code gets transformed before execution is started, JavaScript moves -the `var` statements, followed by the `function` declarations, at the top of the -next surrounding scope. +The above code gets transformed before any execution is started. JavaScript moves +the `var` statements as well as the `function` declarations to the top of the +nearest surrounding scope. // var statements got moved here var bar, someValue; // default to 'undefined' @@ -141,24 +141,24 @@ next surrounding scope. test(); -The missing block scoping will not only move `var` statements out of loops and +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 it seemed like the `if` statement would modify the *global -variable* `goo` while it really does modify the *local variable* after hoisting +In the original code the `if` statement seemed to modify the *global +variable* `goo`, while actually it modifies the *local variable* - after hoisting has been applied. -Without the knowledge about *hoisting*, below code might look like it would -raise a `ReferenceError`. +Without the knowledge about *hoisting*, below code might seem to raise a +`ReferenceError`. // check whether SomeImportantThing has been initiliazed if (!SomeImportantThing) { var SomeImportantThing = {}; } -But of course the above code works due to the `var` statement being moved to the -top of the global scope. +But of course, the above works due to the fact that the `var` statement is being +moved to the top of the *global scope*. var SomeImportantThing; From c4c9eda24183b7b676716485c92b4f2194f57bcf Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 12 Mar 2011 15:17:59 +0100 Subject: [PATCH 029/641] Remove some flowery language. --- doc/arguments.md | 2 +- doc/eval.md | 12 ++++++------ doc/index.md | 2 +- doc/timeouts.md | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/arguments.md b/doc/arguments.md index 69623431..3b519cfb 100644 --- a/doc/arguments.md +++ b/doc/arguments.md @@ -56,7 +56,7 @@ wrappers. }; -### Modification "magic" +### Formal parameters and arguments indexes The `arguments` object creates *getter* and *setter* functions for both its properties as well as the function's formal parameters. diff --git a/doc/eval.md b/doc/eval.md index fe585df7..a5f84861 100644 --- a/doc/eval.md +++ b/doc/eval.md @@ -1,4 +1,4 @@ -## The evil `eval` +## Reasons against `eval` The `eval` function will execute a string of JavaScript code in the local scope. @@ -40,9 +40,9 @@ it should **never** be used with strings of unknown or untrusted origins. ### In conclusion -`eval` is **evil**. Never should it be used, any code that makes use of it is to -be questioned in both its workings and security. In case something requires `eval` -in order to work, its design is to be questioned and should **not** be used in -the first place, a *better design* should be used, that does not require the use -of `eval`. +`eval` should never be used, any code that makes use of it is to be questioned in +its workings, performance and security. In case something requires `eval` in +order to work, its design is to be questioned and should **not** be used in the +first place, a *better design* should be used, that does not require the use of +`eval`. diff --git a/doc/index.md b/doc/index.md index 869f1a2e..549038fc 100644 --- a/doc/index.md +++ b/doc/index.md @@ -18,7 +18,7 @@ - [The `instanceof` operator](#instanceof) - [Type casting](#casting) - [`undefined` and `null`](#undefined) -- [The evil `eval`](#eval) +- [Reasons against `eval`](#eval) - [`setTimeout` and `setInterval`](#timeouts) - [Automatic semicolon insertion](#semicolon) diff --git a/doc/timeouts.md b/doc/timeouts.md index 8b0c8998..f961e498 100644 --- a/doc/timeouts.md +++ b/doc/timeouts.md @@ -98,7 +98,7 @@ 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 `eval` magic +### Hidden use of `eval` `setTimeout` and `setInterval` can also take a string as their first parameter. This feature should **never** be used, since it internally makes use of `eval`. From ad84fac43596ca3369eeca242146003de6833334 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 12 Mar 2011 15:18:34 +0100 Subject: [PATCH 030/641] Clarify the whole thing. --- doc/arrayctor.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/doc/arrayctor.md b/doc/arrayctor.md index 1054b7de..7d686527 100644 --- a/doc/arrayctor.md +++ b/doc/arrayctor.md @@ -8,15 +8,22 @@ when creating new arrays. new Array(1, 2, 3); // Result: [1, 2, 3] [3]; // Result: [3] - new Array(3); // Result: [undefined, undefined, undefined] + new Array(3); // Result: [] new Array('3') // Result: ['3'] In cases when there is only one argument passed to the `Array` constructor, -and that argument is a `Number`, the constructor will use that number as the -*length* of the new array to be created. - -This behavior only comes in handy in a few cases, like repeating a string, in -which it avoids the use of a `for loop` code. +and that argument is a `Number`, the constructor will return a new *sparse* +array with the `length` property set to the value of the argument. It should be +noted that **only** the `length` property of the new array will be set this way, +the actual indexes of the array will not be initialized. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, the index was not set + +The behavior of being able to set the length of the array upfront only comes in +handy in a few cases, like repeating a string, in which it avoids the use of a +`for loop` code. new Array(count + 1).join(stringToRepeat); From 1c92c343f0e2caa50e7508209456f9f1737d0a75 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 12 Mar 2011 15:19:56 +0100 Subject: [PATCH 031/641] Small change to the setence structure. --- doc/prototype.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/prototype.md b/doc/prototype.md index 25c074e9..252dacfd 100644 --- a/doc/prototype.md +++ b/doc/prototype.md @@ -104,8 +104,8 @@ the features of newer JavaScript engines; for example, ### In conclusion It is a **must** to understand the prototypical inheritance model completely -before writing complex code which makes use of it. Also, watching the length of -the prototype chains and breaking them up if necessary can avoid possible +before writing complex code which makes use of it. Also, watch the length of +the prototype chains and break them up if necessary to avoid possible performance issues. Further, the native prototypes should **never** be extended unless it is for the sake of compatibility with newer JavaScript features. From fc11364a5ad829edb3a28dec92240101ba65f5e5 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 12 Mar 2011 15:21:22 +0100 Subject: [PATCH 032/641] Fix the paragraph to make sense after the above code was changed. --- doc/prototype.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/prototype.md b/doc/prototype.md index 252dacfd..11d9eccb 100644 --- a/doc/prototype.md +++ b/doc/prototype.md @@ -43,15 +43,16 @@ called *prototype chains*. Bar.prototype [instance of Foo] { foo: 'Hello World' } Foo.prototype - {method: ...}; + { method: ... } Object.prototype - {toString: ... /* etc. */}; + { toString: ... /* etc. */ } In the above, the object `test` will inherit from both `Bar.prototype` and `Foo.prototype`; hence, it will have access to the function `method` that was -defined on `Foo`. But it will not have access to the property `value` of a -`Foo` instance, since that property gets defined in the [constructor](#constructor) -of `Foo`. But this constructor has to be called explicitly. +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 From 4a70c11121fe32cdcad4a4ee7e4b86b1bb502f77 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 12 Mar 2011 15:21:52 +0100 Subject: [PATCH 033/641] Fix code example formatting. --- doc/semicolon.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/semicolon.md b/doc/semicolon.md index 0f9b1a58..d3b9c5b5 100644 --- a/doc/semicolon.md +++ b/doc/semicolon.md @@ -68,7 +68,7 @@ Below is the result of the parser's "guessing" game. 'and another long string to pass' ); // <- inserted - return; <- inserted, breaks the return statement + return; // <- inserted, breaks the return statement { // treated as a block // a label and a single expression statement @@ -79,7 +79,7 @@ Below is the result of the parser's "guessing" game. // The lines got merged again })(window)(function(window) { - window.someLibrary = {}; //<- inserted + window.someLibrary = {}; // <- inserted })(window); //<- inserted From 0bf897fe7fbb6124a046adb020133a6cd4ffd85b Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 12 Mar 2011 15:22:06 +0100 Subject: [PATCH 034/641] Add a small not on implementation specific stuff. --- doc/timeouts.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/timeouts.md b/doc/timeouts.md index f961e498..5be9861c 100644 --- a/doc/timeouts.md +++ b/doc/timeouts.md @@ -103,6 +103,11 @@ they can be cleared specifically. `setTimeout` and `setInterval` can also take a string as their first parameter. This feature should **never** be used, since it internally makes use of `eval`. +> **Note:** Since the timeout functions are **not** specified by the ECMAScript +> standard, the exact workings when a string is passed to them might differ in +> various JavaScript implementations. As a fact, Microsoft's JScript makes use of +> the `Function` constructor in place of `eval`. + function foo() { // will get called } From a75d8fe4b1122aa90836496f4e6cdf638835039d Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 12 Mar 2011 15:22:21 +0100 Subject: [PATCH 035/641] 80 columns! --- doc/eval.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/eval.md b/doc/eval.md index a5f84861..969cf781 100644 --- a/doc/eval.md +++ b/doc/eval.md @@ -29,9 +29,9 @@ achieved **without** it. ### `eval` in disguise -The [timeout functions](#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. +The [timeout functions](#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 From 81107d7e5c7ecb5cdb59cb539e23697875bee581 Mon Sep 17 00:00:00 2001 From: Yi Jiang Date: Sat, 12 Mar 2011 19:24:05 -0800 Subject: [PATCH 036/641] Fixed issue #43 --- doc/casting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/casting.md b/doc/casting.md index 8388dc6a..79f70341 100644 --- a/doc/casting.md +++ b/doc/casting.md @@ -61,7 +61,7 @@ By using the **not** operator twice, a value can be converted a boolean. !!'foo'; // true !!''; // false - !!'0'; // false + !!'0'; // true !!'1'; // true !!'-1' // true !!{}; // true From cefc2459f9325957ce4a93d7036f7c44c3d0bdc3 Mon Sep 17 00:00:00 2001 From: Yi Jiang Date: Sat, 12 Mar 2011 19:34:45 -0800 Subject: [PATCH 037/641] Fixed Issue #41 and other stylistic changes --- doc/objects.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/objects.md b/doc/objects.md index 480df3bb..60cb37b5 100644 --- a/doc/objects.md +++ b/doc/objects.md @@ -59,7 +59,7 @@ the use of property names that would otherwise lead to a syntax error. ### Deleting properties The only way to actually remove a property from an object is to use the `delete` -operator; setting the property to `undefined` or `null` does **only** remove the +operator; setting the property to `undefined` or `null` **only** remove the value associated with the property, but not the key. var obj = { @@ -77,7 +77,7 @@ value associated with the property, but not the key. } } -The above outputs both `bar undefined` and `foo null` - only `baz` got actually +The above outputs both `bar undefined` and `foo null` - only `baz` was removed and is therefore missing from the output. ### Notation of keys @@ -91,9 +91,9 @@ 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* of the language; -therefore, it must be notated as a *string literal* in order to ensure working -code under older JavaScript engines. +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 From 8abb93e2369d0ed207dc90ccfd3f581165dd0e8e Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sun, 13 Mar 2011 12:56:34 +0100 Subject: [PATCH 038/641] Add a missing hypen --- doc/prototype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/prototype.md b/doc/prototype.md index 11d9eccb..2f1d4892 100644 --- a/doc/prototype.md +++ b/doc/prototype.md @@ -96,7 +96,7 @@ other built in prototypes. This technique is called [monkey patching][1] and breaks *encapsulation*. While used by widely spread frameworks such as [Prototype][2], there is still no good -reason for cluttering built in types with additional *non-standard* functionality. +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, From 631cbb70e0532dcddf514e57289a41a38cfa3b31 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sun, 13 Mar 2011 12:56:51 +0100 Subject: [PATCH 039/641] Fix the generated HTML --- make | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/make b/make index 3153fadf..c40617d8 100755 --- a/make +++ b/make @@ -35,7 +35,7 @@ def create_index(): def create_navigation(file): - content = to_content(file) + content = to_content(file, False) articles = [] links = content[1].split('\n') @@ -59,14 +59,14 @@ def create_article(link, title, html, top = None): return '
%s
\n%s
' % (title, html) -def to_content(file): +def to_content(file, section=True): md = open('%s.md' % file).read().decode('utf-8') title = md.split('\n')[0] content = md[len(title):].strip() html = to_markdown(content) html = html.replace('
', '
', '') - html = '

'.join(html.split('

')) + '

' + html = '

'.join(html.split('

')) + ('

' if section else '') html = html.replace('

', '

').replace('

', '

') title = title.strip('#').strip() return (title, content, html) From 8f786ffa24a7942ec6a8723d0c659d97bc9b6ac7 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sun, 13 Mar 2011 18:19:50 +0100 Subject: [PATCH 040/641] This wording should be more concise --- doc/en/function/this.md | 2 +- doc/en/object/prototype.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/en/function/this.md b/doc/en/function/this.md index 8f6d2e41..f1927e24 100644 --- a/doc/en/function/this.md +++ b/doc/en/function/this.md @@ -95,7 +95,7 @@ Due to the first case `test` now acts like like a plain function call; therefore `this` inside it will no longer refer to `someObject`. While the late binding of `this` might seem like a bad idea at first, it is in -fact what makes [prototypical inheritance](#object.prototype) work. +fact what makes [prototypal inheritance](#object.prototype) work. function Foo() {} Foo.prototype.method = function() {}; diff --git a/doc/en/object/prototype.md b/doc/en/object/prototype.md index dbbbb332..55ca096e 100644 --- a/doc/en/object/prototype.md +++ b/doc/en/object/prototype.md @@ -1,15 +1,15 @@ ## The Prototype JavaScript does not feature a classical inheritance model, instead it uses a -*prototypical* one. +*prototypal* one. While this is often considered to be one of JavaScript's weaknesses, the -prototypical inheritance model is in fact more powerful than the classic model. +prototypal inheritance model is in fact more powerful than the classic model. It is for example fairly trivial to build a classic model on top of it, while the other way around is a far more difficult task. Due to the fact that JavaScript is basically the only widely used language that -features prototypical inheritance, it takes some time to adjust to the +features prototypal inheritance, it takes some time to adjust to the differences between the two models. The first major difference is that inheritance in JavaScript is done by using so @@ -104,7 +104,7 @@ the features of newer JavaScript engines; for example, ### In conclusion -It is a **must** to understand the prototypical inheritance model completely +It is a **must** to understand the prototypal inheritance model completely before writing complex code which makes use of it. Also, watch the length of the prototype chains and break them up if necessary to avoid possible performance issues. Further, the native prototypes should **never** be extended From ace33db667aa892450a6087d2e48207840b1b306 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sun, 13 Mar 2011 18:23:15 +0100 Subject: [PATCH 041/641] Clean up that stuff --- site/de/index.html | 18 - site/index.html | 1863 -------------------------------------------- 2 files changed, 1881 deletions(-) delete mode 100644 site/de/index.html delete mode 100644 site/index.html diff --git a/site/de/index.html b/site/de/index.html deleted file mode 100644 index a26189a2..00000000 --- a/site/de/index.html +++ /dev/null @@ -1,18 +0,0 @@ -JavaScript Garden -

Einführung

Demnächst.

The Authors

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

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.

\ No newline at end of file diff --git a/site/index.html b/site/index.html deleted file mode 100644 index f1a58d6c..00000000 --- a/site/index.html +++ /dev/null @@ -1,1863 +0,0 @@ -JavaScript Garden -

Intro

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

- -

JavaScript Garden does not aim to teach you JavaScript. Former knowledge -of the language is strongly recommended in order to understand the topics covered -in this guide. In order to learn the basics of the language, please head over to -the excellent guide 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).

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 Foo(){}
-Foo.bar = 1;
-Foo.bar; // 1
-
- -

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

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

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

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

Objects as a data type

- -

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

- -

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

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

Accessing properties

- -

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

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

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

- -

Deleting properties

- -

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

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

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

- -

Notation of keys

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

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

- -

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

The Prototype

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

- -

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

- -

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

- -

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

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

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

- - - -

Property lookup

- -

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

- -

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

- -

The prototype property

- -

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

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

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

- -

Performance

- -

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

- -

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

- -

Extension of native prototypes

- -

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

- -

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

- -

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

- -

In conclusion

- -

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

hasOwnProperty

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

- - - -

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

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

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

- -

hasOwnProperty as a property

- -

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

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

In conclusion

- -

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

The for in Loop

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

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

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

- - - -

Using hasOwnProperty for filtering

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

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

- -

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

- -

Best practices

- -

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

Functions

Function Declarations and Expressions

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

- -

The function declaration

- -
function foo() {}
-
- -

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

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

The function expression

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

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

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

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

- -

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

- -

Named function expression

- -

Another special case is the assignment of named functions.

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

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

How this Works

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

- -

The global scope

- -
this;
-
- -

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

- -

Calling a function

- -
foo();
-
- -

Here this will again refer to the global object.

- - - -

Calling a method

- -
test.foo(); 
-
- -

In this example this will refer to test.

- -

Calling a constructor

- -
new foo(); 
-
- -

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

- -

Explicit setting of this

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

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

- -

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

- - - -

Common pitfalls

- -

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

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

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

- -

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

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

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

- -

Assigning methods

- -

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

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

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

- -

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

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

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

Closures and References

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

- -

Emulating private variables

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

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

- -

Why private variables work

- -

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

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

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

- -

Closures inside loops

- -

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

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

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

- -

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

- -

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

- -

Avoiding the reference problem

- -

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

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

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

- -

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

- -

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

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

The arguments Object

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

- - - -

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

- -

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

- -

Converting to an array

- -

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

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

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

- -

Passing arguments

- -

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

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

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

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

Formal parameters and arguments indexes

- -

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

- -

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

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

Performance myths and truths

- -

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

- -

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

- - - -

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

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

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

- -

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

- -

Constructors

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

- -

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

- -

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

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

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

- -

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

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

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

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

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

- -

Factories

- -

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

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

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

- -

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

- -

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

- -

Creating new objects via factories

- -

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

- -

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

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

While the above is robust against a missing new keyword and certainly makes -the use of private variables easier, it comes with some -downsides.

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

In conclusion

- -

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

Scopes and Namespaces

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

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

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

- -

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

- -

The bane of global variables

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

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

- -

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

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

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

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

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

- -

Local variables

- -

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

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

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

- -

Hoisting

- -

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

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

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

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

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

- -

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

- -

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

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

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

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

Name resolution order

- -

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

- -

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

- -

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

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

Namespaces

- -

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

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

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

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

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

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

In conclusion

- -

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

- -

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

Arrays

Array Iteration and Properties

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

- - - -

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

- -

Iteration

- -

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

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

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

- -

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

- -

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

- -

The length property

- -

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

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

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

- -

In conclusion

- -

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

The Array Constructor

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

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

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

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

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

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

In conclusion

- -

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

Types

Equality and comparisons

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

- -

The equals operator

- -

The equals operator consists of two equal signs: ==

- -

JavaScript features weak typing, that means, that the equals 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 equals operator

- -

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

- -

Other than the normal equals operator, the strict equals operator does not -perform type coercion between its operands.

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

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

- -

Comparing objects

- -

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

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

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

- -

In conclusion

- -

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

The typeof operator

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

- -

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

- - - -

The JavaScript type table

- -
Value               Class      Type
--------------------------------------
-"foo"               String     string
-new String("foo")   String     object
-1.2                 Number     number
-new Number(1.2)     Number     object
-true                Boolean    boolean
-new Boolean(true)   Boolean    object
-new Date()          Date       object
-new Error()         Error      object
-[1,2,3]             Array      object
-new Array(1, 2, 3)  Array      object
-new Function("")    Function   function
-/abc/g              RegExp     object (function in Nitro/V8)
-new RegExp("meow")  RegExp     object (function in Nitro/V8)
-{}                  Object     object
-new Object()        Object     object
-
- -

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

- -

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

- - - -

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

- -

The Class of an object

- -

The specification gives exactly one way of accessing the [[Class]] value, -with the use of Object.prototype.toString.

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

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

- - - -

Testing for undefined variables

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

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

- -

In conclusion

- -

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

- -

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

The instanceof operator

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

- -

Comparing custom objects

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

Using instanceof with native types

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

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

- -

In conclusion

- -

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

Type casting

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

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

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

- -

Constructors of built-in types

- -

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

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

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

- -

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

- -

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

- -

Casting to a string

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

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

- -

Casting to a number

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

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

- -

Casting to a boolean

- -

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

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

Core

Why not to use eval

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

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

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

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

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

- -

eval in disguise

- -

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

- -

Security issues

- -

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

- -

In conclusion

- -

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

undefined and null

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

- -

The value undefined

- -

undefined is a type with exactly one value: undefined.

- -

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

- - - -

Some examples for when the value undefined is returned:

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

Handling changes to the value of undefined

- -

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

- -

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

- -

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

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

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

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

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

- -

Uses of null

- -

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

- -

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

Automatic semicolon insertion

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

- -

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

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

Insertion happens, and the parser tries again.

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

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

- -

How it works

- -

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

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

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

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

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

- -

Leading parenthesis

- -

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

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

This code gets transformed into one line.

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

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

- -

In conclusion

- -

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

Other

setTimeout and setInterval

Since JavaScript is asynchronous, it is possible to schedule the execution of a -function by using the setTimeout and setInterval functions.

- - - -
function foo() {}
-var id = setTimeout(foo, 1000); // returns a Number > 0
-
- -

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

- -

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

- -

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

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

Stacking calls with setInterval

- -

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

- -

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

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

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

- -

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

- -

Dealing with possible blocking code

- -

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

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

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

- -

Manually clearing timeouts

- -

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

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

Clearing all timeouts

- -

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

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

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

- -

Hidden use of eval

- -

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

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

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

- -

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

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

In conclusion

- -

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

- -

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

\ No newline at end of file From fbfa79d1e276608102158bf4bee27e0989f57e47 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Mon, 14 Mar 2011 01:40:55 +0800 Subject: [PATCH 042/641] Some commenting, stuff --- html/js/garden.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/html/js/garden.js b/html/js/garden.js index 32ee9abc..ce1826a1 100644 --- a/html/js/garden.js +++ b/html/js/garden.js @@ -73,19 +73,24 @@ $(document).ready(function() { Mobile, and small screen stuff */ + var $w = $(window), + width = $w.width(); + if($.mobile) { + var nav = $('nav')[0]; $('body').addClass('mobile'); - var $w = $(window), - width = $w.width(); - if(width > 1000) { $w.scroll(function(){ - + nav.top.style = window.pageYOffset + 'px'; }); } } + if(width < 1000) { + + } + prettyPrint(); }); From 4ef1f3ffc4380752b189a7b8b9abf1911b043457 Mon Sep 17 00:00:00 2001 From: Yi Jiang Date: Sun, 13 Mar 2011 18:56:19 -0700 Subject: [PATCH 043/641] Fix for issue #46 --- garden.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/garden.jade b/garden.jade index 4c4eebdf..9501ce24 100644 --- a/garden.jade +++ b/garden.jade @@ -14,7 +14,7 @@ html(lang='en') link(rel='stylesheet', href='../style/print.css', media='print') body From 1b3a48104122202bb02ca451b83c2e3abd8aeaf2 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Mon, 14 Mar 2011 14:03:57 +0800 Subject: [PATCH 044/641] Fixed heading capitalization and other minor language issues --- doc/en/array/constructor.md | 2 +- doc/en/array/general.md | 4 ++-- doc/en/core/eval.md | 8 ++++---- doc/en/core/semicolon.md | 8 ++++---- doc/en/core/undefined.md | 4 ++-- doc/en/function/arguments.md | 8 ++++---- doc/en/function/closures.md | 6 +++--- doc/en/function/constructors.md | 4 ++-- doc/en/function/general.md | 6 +++--- doc/en/function/this.md | 14 +++++++------- doc/en/object/forinloop.md | 4 ++-- doc/en/object/general.md | 8 ++++---- doc/en/object/hasownproperty.md | 4 ++-- doc/en/object/prototype.md | 10 +++++----- doc/en/other/timeouts.md | 16 ++++++++-------- doc/en/types/casting.md | 10 +++++----- doc/en/types/equality.md | 24 ++++++++++++------------ doc/en/types/instanceof.md | 8 ++++---- doc/en/types/typeof.md | 10 +++++----- 19 files changed, 79 insertions(+), 79 deletions(-) diff --git a/doc/en/array/constructor.md b/doc/en/array/constructor.md index 8004168a..763f55fb 100644 --- a/doc/en/array/constructor.md +++ b/doc/en/array/constructor.md @@ -27,7 +27,7 @@ handy in a few cases, like repeating a string, in which it avoids the use of a new Array(count + 1).join(stringToRepeat); -### In conclusion +### In Conclusion The use of the `Array` constructor should be avoided as much as possible. Literals are definitely preferred. They are shorter and have a clearer syntax; diff --git a/doc/en/array/general.md b/doc/en/array/general.md index 7e0ce1d1..7c8f07ab 100644 --- a/doc/en/array/general.md +++ b/doc/en/array/general.md @@ -34,7 +34,7 @@ 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 +### 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 @@ -50,7 +50,7 @@ elements that are contained in the array, the *setter* can be used to Assigning a smaller length does truncate the array, but increasing the length does not have any effect on the array. -### In conclusion +### 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 diff --git a/doc/en/core/eval.md b/doc/en/core/eval.md index 9ec79eed..ecf7ba90 100644 --- a/doc/en/core/eval.md +++ b/doc/en/core/eval.md @@ -1,4 +1,4 @@ -## Why not to use `eval` +## Why Not to Use `eval` The `eval` function will execute a string of JavaScript code in the local scope. @@ -27,18 +27,18 @@ the name of the called function is actually `eval`. The use of `eval` should be avoided at **all costs**. 99.9% of its "uses" can be achieved **without** it. -### `eval` in disguise +### `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 +### Security Issues `eval` also is a security problem as it executes **any** code given to it, it should **never** be used with strings of unknown or untrusted origins. -### In conclusion +### In Conclusion `eval` should never be used, any code that makes use of it is to be questioned in its workings, performance and security. In case something requires `eval` in diff --git a/doc/en/core/semicolon.md b/doc/en/core/semicolon.md index d3b9c5b5..61d74fd9 100644 --- a/doc/en/core/semicolon.md +++ b/doc/en/core/semicolon.md @@ -1,4 +1,4 @@ -## Automatic semicolon insertion +## Automatic Semicolon Insertion Although JavaScript has C style syntax, it does **not** enforce the use of semicolons in the source code, it is possible to omit them. @@ -21,7 +21,7 @@ Insertion happens, and the parser tries again. The automatic insertion of semicolon is considered to be one of **biggest** design flaws in the language, as it *can* change the behavior of code. -### How it works +### How it Works The code below has no semicolons in it, so it is up to the parser to decide where to insert them. @@ -90,7 +90,7 @@ Below is the result of the parser's "guessing" game. The parser drastically changed the behavior of the code above, in certain cases it does the **wrong thing**. -### Leading parenthesis +### Leading Parenthesis In case of a leading parenthesis, the parser will **not** insert a semicolon. @@ -104,7 +104,7 @@ This code gets transformed into one line. 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 +### 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 diff --git a/doc/en/core/undefined.md b/doc/en/core/undefined.md index e4e0e49f..b29da3b4 100644 --- a/doc/en/core/undefined.md +++ b/doc/en/core/undefined.md @@ -3,7 +3,7 @@ JavaScript has two distinct values for `nothing`, the more useful of these two being `undefined`. -### The value `undefined` +### The Value `undefined` `undefined` is a type with exactly one value: `undefined`. @@ -25,7 +25,7 @@ Some examples for when the value `undefined` is returned: - Function parameters which do not had any explicit value passed. - Anything that has been set to the value of `undefined`. -### Handling changes to the value of `undefined` +### 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 diff --git a/doc/en/function/arguments.md b/doc/en/function/arguments.md index 915afdf0..3d551ad5 100644 --- a/doc/en/function/arguments.md +++ b/doc/en/function/arguments.md @@ -16,7 +16,7 @@ Due to this, it is **not** possible to use standard array methods like `push`, 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 +### Converting to an Array The code below will return a new `Array` containing all the elements of the `arguments` object. @@ -26,7 +26,7 @@ The code below will return a new `Array` containing all the elements of the This conversion is **slow**, it is **not recommended** to use it in performance critical sections of code. -### Passing arguments +### Passing Arguments The following is the recommended way of passing arguments from one function to another. @@ -56,7 +56,7 @@ wrappers. }; -### Formal parameters and arguments indexes +### 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. @@ -77,7 +77,7 @@ of the corresponding property on the `arguments` object, and the other way aroun } foo(1, 2, 3); -### Performance myths and truths +### Performance Myths and Truths The `arguments` object is always created with the only two exceptions being the cases where it is declared as a name inside of a function or one of its formal diff --git a/doc/en/function/closures.md b/doc/en/function/closures.md index 0bb6d66d..17554dcb 100644 --- a/doc/en/function/closures.md +++ b/doc/en/function/closures.md @@ -29,7 +29,7 @@ the function `get`. Both of these functions keep a **reference** to the scope of `Counter` and, therefore, always keep access to the `count` variable that was defined in that very scope. -### Why private variables work +### 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 @@ -44,7 +44,7 @@ The above code will **not** change the variable `count` in the scope of `Counter since `foo.hack` was not defined in **that** scope. It will instead create - or override - the *global* variable `count`. -### Closures inside loops +### Closures Inside Loops One often made mistake is to use closures inside of loops, as if they were copying the value of the loops index variable. @@ -65,7 +65,7 @@ The *anonymous* function keeps a **reference** to `i` and at the time In order to get the desired behavior, it is necessary to create a **copy** of the value of `i`. -### Avoiding the reference problem +### 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). diff --git a/doc/en/function/constructors.md b/doc/en/function/constructors.md index 52935451..ad90b028 100644 --- a/doc/en/function/constructors.md +++ b/doc/en/function/constructors.md @@ -84,7 +84,7 @@ In the above example, there is no functional difference between using and not using the `new` keyword. -### Creating new objects via factories +### Creating New Objects via Factories An often made recommendation is to **not** use `new` since forgetting its use may lead to bugs. @@ -118,7 +118,7 @@ downsides. 3. Dropping the prototype chain just because of a left out `new` keyword somehow goes against the spirit of the language. -### In conclusion +### 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 diff --git a/doc/en/function/general.md b/doc/en/function/general.md index 5bfd6cd8..aae432ba 100644 --- a/doc/en/function/general.md +++ b/doc/en/function/general.md @@ -4,7 +4,7 @@ Functions in JavaScript are first class objects, that means that they can be passed around like any other value. One common use of this feature is to pass an *anonymous function* as a callback to another, possibly asynchronous function. -### The `function` declaration +### The `function` Declaration function foo() {} @@ -15,7 +15,7 @@ in, even if called before the actual definition in the source. foo(); // Works because foo was created before this code runs function foo() {} -### The `function` expression +### The `function` Expression var foo = function() {}; @@ -32,7 +32,7 @@ the script gets executed. But since assignments only happens at runtime, the value of `foo` will default to [undefined](#core.undefined) before the corresponding code is executed. -### Named function expression +### Named Function Expression Another special case is the assignment of named functions. diff --git a/doc/en/function/this.md b/doc/en/function/this.md index f1927e24..0cff9ee4 100644 --- a/doc/en/function/this.md +++ b/doc/en/function/this.md @@ -4,14 +4,14 @@ JavaScript has a different concept of what the special name `this` refers to than most other programming languages do. There are exactly **five** different ways in which the value of `this` can be bound in the language. -### The global scope +### The Global Scope this; When using `this` in global scope, it will simply refer to the *global* object. -### Calling a function +### Calling a Function foo(); @@ -20,13 +20,13 @@ 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 +### Calling a Method test.foo(); In this example `this` will refer to `test`. -### Calling a constructor +### Calling a Constructor new foo(); @@ -34,7 +34,7 @@ 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` +### Explicit Setting of `this` function foo(a, b, c) {} @@ -53,7 +53,7 @@ inside of `foo` will be set to `bar`. > 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 +### Common Pitfalls While most of these cases make sense, the first one is to be considered another mis-design of the language, as it **never** has any practical use. @@ -83,7 +83,7 @@ local variable inside of `method` which refers to `Foo`. outer `this`. In combination with [closures](#function.closures), it can also be used to pass `this` values around. -### Assigning methods +### Assigning Methods Another thing that does **not** work in JavaScript is function aliasing, that is, **assigning** a method to a variable. diff --git a/doc/en/object/forinloop.md b/doc/en/object/forinloop.md index 6150e259..cd1cb64b 100644 --- a/doc/en/object/forinloop.md +++ b/doc/en/object/forinloop.md @@ -23,7 +23,7 @@ this is done by using the [`hasOwnProperty`](#object.hasownproperty) method of > **Note:** Since the `for in` always traverses the complete prototype chain, it > will get slower with each additional layer of inheritance added to an object. -### Using `hasOwnProperty` for filtering +### Using `hasOwnProperty` for Filtering // still the foo from above for(var i in foo) { @@ -41,7 +41,7 @@ One widely used framework which does this is [Prototype][1]. When this framework is included, `for in` loops that do not use `hasOwnProperty` are guaranteed to break. -### Best practices +### In Conclusion It is recommended to **always** use `hasOwnProperty`. Never should any assumptions be made about the environment the code is running in, or whether the diff --git a/doc/en/object/general.md b/doc/en/object/general.md index dd749fd7..4dce53ea 100644 --- a/doc/en/object/general.md +++ b/doc/en/object/general.md @@ -23,7 +23,7 @@ literals act as objects too. 2 .toString(); // note the space left to the dot (2).toString(); // 2 is evaluated first -### Objects as a data type +### Objects as a Data Type Objects in JavaScript can also be used as a [*Hashmap*][1], they mainly consist of named properties mapping to values. @@ -37,7 +37,7 @@ has no [own properties](#object.hasownproperty) defined on it. // a new object with a property called 'test' with value 12 var bar = {test: 12}; -### Accessing properties +### Accessing Properties The properties of an object can be accessed in two ways, via either the dot notation, or the square bracket notation. @@ -56,7 +56,7 @@ Both notations are identical in their workings, with the only difference being t the square bracket notation allows for dynamic setting of properties, as well as the use of property names that would otherwise lead to a syntax error. -### Deleting properties +### Deleting Properties The only way to actually remove a property from an object is to use the `delete` operator; setting the property to `undefined` or `null` **only** remove the @@ -80,7 +80,7 @@ value associated with the property, but not the key. The above outputs both `bar undefined` and `foo null` - only `baz` was removed and is therefore missing from the output. -### Notation of keys +### Notation of Keys var test = { 'case': 'I am a keyword so I must be notated as a string', diff --git a/doc/en/object/hasownproperty.md b/doc/en/object/hasownproperty.md index 6b18353d..ea224109 100644 --- a/doc/en/object/hasownproperty.md +++ b/doc/en/object/hasownproperty.md @@ -26,7 +26,7 @@ essential when iterating over the properties of any object. There is **no** othe way to exclude properties that are not defined on the object *itself*, but somewhere on its prototype chain. -### `hasOwnProperty` as a property +### `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 @@ -44,7 +44,7 @@ necessary to use an *external* `hasOwnProperty` in order to get correct results. // Use another hasOwnProperty and call it with 'this' set to foo {}.hasOwnProperty.call(foo, 'bar'); // true -### In conclusion +### In Conclusion When checking for the existence of a property on a object, `hasOwnProperty` is the **only** method of doing so. It is also recommended to make `hasOwnProperty` diff --git a/doc/en/object/prototype.md b/doc/en/object/prototype.md index 55ca096e..f780eba2 100644 --- a/doc/en/object/prototype.md +++ b/doc/en/object/prototype.md @@ -14,7 +14,7 @@ differences between the two models. The first major difference is that inheritance in JavaScript is done by using so called *prototype chains*. - + > **Note:** Simply using `Bar.prototype = Foo.prototype` will result in both objects > sharing the **same** prototype. Therefore, changes to either object's prototype > will affect the prototype of the other as well, which in most cases is not the @@ -59,7 +59,7 @@ its prototype; thus, all `Bar` instances will share the **same** `value` propert > prototype chain will go over `Function.prototype` and not `Foo.prototype`; > therefore, `method` will not be on the prototype chain. -### Property lookup +### 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. @@ -68,7 +68,7 @@ When it reaches the top of the chain - namely `Object.prototype` - and still hasn't found the specified property, it will return the value [undefined](#core.undefined) instead. -### The prototype property +### The Prototype Property While the prototype property is used by the language to build the prototype chains, it is still possible to assign **any** given value to it. Although @@ -89,7 +89,7 @@ access non-existent properties will always traverse the full prototype chain. Also, when [iterating](#object.forinloop) over the properties of an object **every** property that is on the prototype chain will get enumerated. -### Extension of native prototypes +### Extension of Native Prototypes One mis-feature that is often used is to extend `Object.prototype` or one of the other built in prototypes. @@ -102,7 +102,7 @@ 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 +### In Conclusion It is a **must** to understand the prototypal inheritance model completely before writing complex code which makes use of it. Also, watch the length of diff --git a/doc/en/other/timeouts.md b/doc/en/other/timeouts.md index c988702e..d196a3b7 100644 --- a/doc/en/other/timeouts.md +++ b/doc/en/other/timeouts.md @@ -3,7 +3,7 @@ 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 +> **Note:** Timeouts are **not** part of the ECMAScript Standard. They are > implemented as part of the [DOM][1]. function foo() {} @@ -39,7 +39,7 @@ refers to that very object. > a silent error, since when the function returns `undefined` `setTimeout` will > **not** raise any error. -### Stacking calls with `setInterval` +### 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 @@ -60,7 +60,7 @@ 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 +### Dealing with Possible Blocking Code The easiest as well as most controllable solution, is to use `setTimeout` within the function itself. @@ -75,7 +75,7 @@ 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 +### Manually Clearing Timeouts Clearing timeouts and intervals works by passing the respective ID to `clearTimeout` or `clearInterval`, depending which `set` function was used in @@ -105,7 +105,7 @@ This feature should **never** be used, since it internally makes use of `eval`. > **Note:** Since the timeout functions are **not** specified by the ECMAScript > standard, the exact workings when a string is passed to them might differ in -> various JavaScript implementations. As a fact, Microsoft's JScript makes use of +> various JavaScript implementations. For example, Microsoft's JScript makes use of > the `Function` constructor in place of `eval`. function foo() { @@ -139,9 +139,9 @@ function that will get called by either of the timeout functions. > **Note:** While it is also possible to use the syntax > `setTimeout(foo, 1000, a, b, c)`, it is not recommended, as its use may lead -> to subtle errors when used with [methods](#function.this). +> to subtle errors when used with [methods](#function.this). -### In conclusion +### 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 @@ -151,5 +151,5 @@ be passed that then takes care of the actual call. Further, the use of `setInterval` should be avoided since its scheduler is not blocked by executing JavaScript. -[1]: http://en.wikipedia.org/wiki/Document_Object_Model +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" diff --git a/doc/en/types/casting.md b/doc/en/types/casting.md index 34b320d0..15d84e74 100644 --- a/doc/en/types/casting.md +++ b/doc/en/types/casting.md @@ -1,4 +1,4 @@ -## Type casting +## Type Casting JavaScript is a *weakly typed* language, so it will apply *type coercion* **wherever** possible. @@ -25,7 +25,7 @@ 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 +### 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. @@ -43,19 +43,19 @@ more type coercion. The best option is to cast to one of the three possible types **explicitly**. -### Casting to a string +### Casting to a String '' + 10 === '10'; // true By prepending a empty string a value can easily be casted to a string. -### Casting to a number +### Casting to a Number +'10' === 10; // true Using the **unary** plus operator it is possible to cast to a number. -### Casting to a boolean +### Casting to a Boolean By using the **not** operator twice, a value can be converted a boolean. diff --git a/doc/en/types/equality.md b/doc/en/types/equality.md index 1bc4c9cf..a578b19c 100644 --- a/doc/en/types/equality.md +++ b/doc/en/types/equality.md @@ -1,12 +1,12 @@ -## Equality and comparisons +## Equality and Comparisons JavaScript has two different ways of comparing the values of objects for equality. -### The equals operator +### The Equality Operator -The equals operator consists of two equal signs: `==` +The equality operator consists of two equal signs: `==` -JavaScript features *weak typing*, that means, that the equals operator +JavaScript features *weak typing*. This means that the equality operator **coerces** types in order to compare them. "" == "0" // false @@ -27,12 +27,12 @@ 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 equals operator +### The Strict Equality Operator -The strict equals operator consists of **three** equal signs: `===` +The strict equality operator consists of **three** equal signs: `===` -Other than the normal equals operator, the strict equals operator does **not** -perform type coercion between its operands. +It works exactly like the normal equality operator, except that strict equality +operator does **not** perform type coercion between its operands. "" === "0" // false 0 === "" // false @@ -48,7 +48,7 @@ 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 +### Comparing Objects While both `==` and `===` are stated as **equality** operators, they behave different when at least one of their operands happens to be an `Object`. @@ -61,11 +61,11 @@ different when at least one of their operands happens to be an `Object`. 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 a pointer comparison in C do. +and pointer comparison in C. -### In conclusion +### In Conclusion -It is highly recommended to only use the **strict equals** operator. In cases +It is highly recommended to only use the **strict equality** operator. In cases where types need to be coerced, it should be done [explicitly](#types.casting) and not left to the language's complicated coercion rules. diff --git a/doc/en/types/instanceof.md b/doc/en/types/instanceof.md index 6fe0d63e..8711331a 100644 --- a/doc/en/types/instanceof.md +++ b/doc/en/types/instanceof.md @@ -1,10 +1,10 @@ -## The `instanceof` operator +## 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 +### Comparing Custom Objects function Foo() {} function Bar() {} @@ -18,7 +18,7 @@ nearly as useless as the [typeof operator](#types.typeof). Bar.prototype = Foo; new Bar() instanceof Foo; // false -### Using `instanceof` with native types +### Using `instanceof` with Native Types new String('foo') instanceof String; // true new String('foo') instanceof Object; // true @@ -30,7 +30,7 @@ One important thing to note here is, that `instanceof` does not work on objects that origin from different JavaScript contexts (e.g. different documents in a web browser), since their constructors will not be the exact same object. -### In conclusion +### In Conclusion The `instanceof` operator should **only** be used when dealing with custom made objects that origin from the same JavaScript context. Just like the diff --git a/doc/en/types/typeof.md b/doc/en/types/typeof.md index 200b94d4..e4b28d7f 100644 --- a/doc/en/types/typeof.md +++ b/doc/en/types/typeof.md @@ -1,4 +1,4 @@ -## The `typeof` operator +## The `typeof` Operator The `typeof` operator (together with [`instanceof`](#types.instanceof)) is probably the biggest @@ -13,7 +13,7 @@ object. > 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 +### The JavaScript Type Table Value Class Type ------------------------------------- @@ -45,7 +45,7 @@ The *Class* refers to the value of the internal `[[Class]]` property of an objec 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 Class of an Object The specification gives exactly one way of accessing the `[[Class]]` value, with the use of `Object.prototype.toString`. @@ -66,7 +66,7 @@ retrieved. > for both `null` and `undefined` was **changed** from `Object` to `Null` and > `Undefined` in ECMAScript 5. -### Testing for undefined variables +### Testing for Undefined Variables typeof foo !== 'undefined' @@ -74,7 +74,7 @@ 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 Conclusion In order to check the type of an object, it is highly recommended to use `Object.prototype.toString`; as this is the only reliable way of doing so. From 7c18991295e2069e74de111fbf1de1c1b51f2ab6 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Mon, 14 Mar 2011 16:57:16 +0800 Subject: [PATCH 045/641] Mobile styles, ready for testing --- garden.jade | 4 ++ site/javascript/garden.js | 23 ++++++-- site/style/garden.css | 107 +++++++++++++++++++++++++++++++++----- 3 files changed, 117 insertions(+), 17 deletions(-) diff --git a/garden.jade b/garden.jade index 9501ce24..5fd1e3a2 100644 --- a/garden.jade +++ b/garden.jade @@ -30,6 +30,8 @@ html(lang='en') - else a(href= '/JavaScript-Garden/' + lang.id, title=lang.title) #{lang.id} + a(id='top', href='#intro', title='Back to top') #top + a(id='hide_menu', class='tablet') Hide Menu ul - each section in navigation @@ -51,6 +53,8 @@ html(lang='en') a(id='nav_next_section', href='#') next section span(class='nav_section_name') section name + + a(id='show_menu') show menu // Sections - each section in sections diff --git a/site/javascript/garden.js b/site/javascript/garden.js index 74489060..5c7a5212 100644 --- a/site/javascript/garden.js +++ b/site/javascript/garden.js @@ -9,7 +9,6 @@ Sections.prototype = { init: function(attribute) { this.heights = this.page.nav.find('ul').map(function(idx, ele) { return $(this).outerHeight(); - }).get(); this.links = { @@ -140,7 +139,8 @@ function Page() { Page.prototype = { init: function() { - var that = this; + var that = this, + mainNav = $('#nav_main'); this.scrollLast = 0; this.window.scroll(function() { @@ -155,18 +155,31 @@ Page.prototype = { that.sections.map(); setTimeout(function() { that.sections.highlight(); - }, 10); // Mobile, for position: fixed if ($.mobile) { - $('#nav_mobile').css('position', 'absolute'); + var navs = $('#nav_mobile, #nav_main'); + navs.css('position', 'absolute'); this.window.scroll(function(){ - $('#nav_mobile').offset({ + navs.offset({ top: that.window.scrollTop() }); }); } + + // Show menu for tablets + $('#show_menu').click(function (){ + var scrollTop = $.mobile ? that.window.scrollTop() : 0; + + mainNav.slideDown(300).css('top', scrollTop); + return false; + }); + + $('#nav_main').click(function(){ + if(that.window.width() < 1000) + mainNav.slideUp(300); + }); }, onScroll: function() { diff --git a/site/style/garden.css b/site/style/garden.css index 5e6c6c14..4dbcaeb6 100644 --- a/site/style/garden.css +++ b/site/style/garden.css @@ -86,13 +86,14 @@ nav > div li { margin-right: 4px; } -nav > div li a { - padding: 7px 10px 5px 10px; +nav > div li a, #top, #hide_menu { + padding: 7px 10px 9px 10px; + cursor: pointer; } nav > div li.active a { border-top: 4px solid #0D2E4C; - padding: 3px 10px 5px 10px; + padding-top: 3px; color: #FFC76D; } @@ -100,13 +101,23 @@ nav > div ul { height: 40px; } +#top { + position: absolute; + top: 0; + right: 0; +} + +#hide_menu { + display: none; +} + nav li { font-size: 16px; margin: 0px; list-style: none; } -nav ul li:last-child a { +nav > ul li:last-child a { padding-bottom: 9px; } @@ -262,18 +273,23 @@ section:after { text-align: center; } -#object:after { content: '{ }'; } -#array:after { content: '[ ]'; } +/* 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'; } -#function:after { +#function:after, nav li.nav_function h1 a:after { content: 'f(x)'; font-style: italic; } -#types:after { content: '='; font-weight: bold; } -#core:after { content: 'js'; } - -#other:after { +#other:after, nav li.nav_other h1 a:after { content: '?'; font-size: 100px; font-family: Arial, Helvetica, sans-serif; @@ -387,6 +403,15 @@ a abbr { .dec { color: #606; } .pln { color: #FFAA3E; } +/* Desktop override */ + +@media screen and (min-width: 1000px) { + #nav_main { + display: block !important; + top: 0 !important; + } +} + /* "Tablet" Styles */ @media screen and (max-width: 1000px) { @@ -402,6 +427,59 @@ a abbr { #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: 70px; + padding: 20px 0 30px; + } + + #nav_main li h1 a:after { + display: block; + font-family: Georgia; + font-size: 40px; + font-weight: normal; + margin-top: 10px; + } + + #nav_main > div ul { + height: auto; + overflow: hidden; + border-bottom: 1px solid rgba(255, 255, 255, 0.3); + } + + nav a:hover, + nav li.active, + nav li.active a { + background-color: #0D2E4C; + } + + #hide_menu { + display: block; + position: absolute; + top: 0; + right: 45px; } #nav_mobile { @@ -410,7 +488,7 @@ a abbr { right: 0; height: 0; display: block; - z-index: 1000; + z-index: 50; } #nav_mobile a { @@ -438,6 +516,7 @@ a abbr { #show_menu { font-weight: bold; + cursor: pointer; } pre { @@ -487,6 +566,10 @@ screen and (-webkit-min-device-pixel-ratio: 2) { font-size: 13px; } + #nav_main > ul > li { + width: 25%; + } + #nav_mobile { width: 160px; } From 07f23c9de9a6245dd3d2986bf285d00a7072fdc8 Mon Sep 17 00:00:00 2001 From: Yi Jiang Date: Mon, 14 Mar 2011 21:17:46 -0700 Subject: [PATCH 046/641] Shifted emphasise off 'only' to 'value' and 'key' --- doc/en/object/general.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/object/general.md b/doc/en/object/general.md index dd749fd7..b4fc816b 100644 --- a/doc/en/object/general.md +++ b/doc/en/object/general.md @@ -59,8 +59,8 @@ the use of property names that would otherwise lead to a syntax error. ### Deleting properties The only way to actually remove a property from an object is to use the `delete` -operator; setting the property to `undefined` or `null` **only** remove the -value associated with the property, but not the key. +operator; setting the property to `undefined` or `null` only remove the +*value* associated with the property, but not the *key*. var obj = { bar: 1, From f9e5b4ef9005ae413ca1096d9df96786d02f8777 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 15 Mar 2011 14:31:14 +0200 Subject: [PATCH 047/641] initial russian translatin commit --- doc/ru/array/constructor.md | 35 + doc/ru/array/general.md | 58 + doc/ru/core/eval.md | 48 + doc/ru/core/semicolon.md | 114 ++ doc/ru/core/undefined.md | 72 ++ doc/ru/function/arguments.md | 119 ++ doc/ru/function/closures.md | 98 ++ doc/ru/function/constructors.md | 128 +++ doc/ru/function/general.md | 48 + doc/ru/function/scopes.md | 231 ++++ doc/ru/function/this.md | 111 ++ doc/ru/index.json | 72 ++ doc/ru/intro/authors.md | 8 + doc/ru/intro/contributors.md | 8 + doc/ru/intro/index.md | 15 + doc/ru/intro/license.md | 12 + doc/ru/object/forinloop.md | 51 + doc/ru/object/general.md | 99 ++ doc/ru/object/hasownproperty.md | 53 + doc/ru/object/prototype.md | 116 ++ doc/ru/other/timeouts.md | 155 +++ doc/ru/types/casting.md | 70 ++ doc/ru/types/equality.md | 71 ++ doc/ru/types/instanceof.md | 38 + doc/ru/types/typeof.md | 87 ++ site/ru/index.html | 1859 +++++++++++++++++++++++++++++++ 26 files changed, 3776 insertions(+) create mode 100644 doc/ru/array/constructor.md create mode 100644 doc/ru/array/general.md create mode 100644 doc/ru/core/eval.md create mode 100644 doc/ru/core/semicolon.md create mode 100644 doc/ru/core/undefined.md create mode 100644 doc/ru/function/arguments.md create mode 100644 doc/ru/function/closures.md create mode 100644 doc/ru/function/constructors.md create mode 100644 doc/ru/function/general.md create mode 100644 doc/ru/function/scopes.md create mode 100644 doc/ru/function/this.md create mode 100644 doc/ru/index.json create mode 100644 doc/ru/intro/authors.md create mode 100644 doc/ru/intro/contributors.md create mode 100644 doc/ru/intro/index.md create mode 100644 doc/ru/intro/license.md create mode 100644 doc/ru/object/forinloop.md create mode 100644 doc/ru/object/general.md create mode 100644 doc/ru/object/hasownproperty.md create mode 100644 doc/ru/object/prototype.md create mode 100644 doc/ru/other/timeouts.md create mode 100644 doc/ru/types/casting.md create mode 100644 doc/ru/types/equality.md create mode 100644 doc/ru/types/instanceof.md create mode 100644 doc/ru/types/typeof.md create mode 100644 site/ru/index.html diff --git a/doc/ru/array/constructor.md b/doc/ru/array/constructor.md new file mode 100644 index 00000000..8004168a --- /dev/null +++ b/doc/ru/array/constructor.md @@ -0,0 +1,35 @@ +## The `Array` Constructor + +Since the `Array` constructor is ambiguous in how it deals with its parameters, +it is highly recommended to always use the array literals - `[]` notation - +when creating new arrays. + + [1, 2, 3]; // Result: [1, 2, 3] + new Array(1, 2, 3); // Result: [1, 2, 3] + + [3]; // Result: [3] + new Array(3); // Result: [] + new Array('3') // Result: ['3'] + +In cases when there is only one argument passed to the `Array` constructor, +and that argument is a `Number`, the constructor will return a new *sparse* +array with the `length` property set to the value of the argument. It should be +noted that **only** the `length` property of the new array will be set this way, +the actual indexes of the array will not be initialized. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, the index was not set + +The behavior of being able to set the length of the array upfront only comes in +handy in a few cases, like repeating a string, in which it avoids the use of a +`for loop` code. + + new Array(count + 1).join(stringToRepeat); + +### In conclusion + +The use of the `Array` constructor should be avoided as much as possible. +Literals are definitely preferred. They are shorter and have a clearer syntax; +therefore, they also increase the readability of the code. + diff --git a/doc/ru/array/general.md b/doc/ru/array/general.md new file mode 100644 index 00000000..7e0ce1d1 --- /dev/null +++ b/doc/ru/array/general.md @@ -0,0 +1,58 @@ +## Array Iteration and Properties + +Although arrays in JavaScript are objects, there are no good reasons to use +the [`for in loop`](#object.forinloop) in for iteration on them. In fact there +are a number of good reasons **against** the use of `for in` on arrays. + +> **Note:** JavaScript arrays are **not** *associative arrays*. JavaScript only +> has [objects](#object.general) for mapping keys to values. And while associative +> arrays **preserve** order, objects **do not**. + +Since the `for in` loop enumerates all the properties that are on the prototype +chain and the only way to exclude those properties is to use +[`hasOwnProperty`](#object.hasownproperty), it is already up to **twenty times** +slower than a normal `for` loop. + +### Iteration + +In order to achieve the best performance when iterating over arrays, it is best +to use the classic `for` loop. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +There is one extra catch in the above example, that is the caching of the +length of the array via `l = list.length`. + +Although the `length` property is defined on the array itself, there is still an +overhead for doing the lookup on each iteration of the loop. And while recent +JavaScript engines **may** apply optimization in this case, there is no way of +telling whether the code will run on one of these newer engines or not. + +In fact, leaving out the caching may result in the loop being only **half as +fast** as with the cached length. + +### The `length` property + +While the *getter* of the `length` property simply returns the number of +elements that are contained in the array, the *setter* can be used to +**truncate** the array. + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo; // [1, 2, 3] + +Assigning a smaller length does truncate the array, but increasing the length +does not have any effect on the array. + +### In conclusion + +For the best performance it is recommended to always use the plain `for` loop +and cache the `length` property. The use of `for in` on an array is a sign of +badly written code that is prone to bugs and bad performance. + diff --git a/doc/ru/core/eval.md b/doc/ru/core/eval.md new file mode 100644 index 00000000..9ec79eed --- /dev/null +++ b/doc/ru/core/eval.md @@ -0,0 +1,48 @@ +## Why not to use `eval` + +The `eval` function will execute a string of JavaScript code in the local scope. + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +But `eval` only executes in local scope when it is being called **directly** *and* +the name of the called function is actually `eval`. + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +The use of `eval` should be avoided at **all costs**. 99.9% of its "uses" can be +achieved **without** it. + +### `eval` in disguise + +The [timeout functions](#other.timeouts) `setTimeout` and `setInterval` can both +take a string as their first argument. This string will **always** get executed +in the global scope since `eval` is not being called directly in that case. + +### Security issues + +`eval` also is a security problem as it executes **any** code given to it, +it should **never** be used with strings of unknown or untrusted origins. + +### In conclusion + +`eval` should never be used, any code that makes use of it is to be questioned in +its workings, performance and security. In case something requires `eval` in +order to work, its design is to be questioned and should **not** be used in the +first place, a *better design* should be used, that does not require the use of +`eval`. + diff --git a/doc/ru/core/semicolon.md b/doc/ru/core/semicolon.md new file mode 100644 index 00000000..d3b9c5b5 --- /dev/null +++ b/doc/ru/core/semicolon.md @@ -0,0 +1,114 @@ +## Automatic semicolon insertion + +Although JavaScript has C style syntax, it does **not** enforce the use of +semicolons in the source code, it is possible to omit them. + +But JavaScript is not a semicolon-less language, it in fact needs the +semicolons in order to understand the source code. Therefore the JavaScript +parser **automatically** inserts them whenever it encounters a parse +error due to a missing semicolon. + + var foo = function() { + } // parse error, semicolon expected + test() + +Insertion happens, and the parser tries again. + + var foo = function() { + }; // no error, parser continues + test() + +The automatic insertion of semicolon is considered to be one of **biggest** +design flaws in the language, as it *can* change the behavior of code. + +### How it works + +The code below has no semicolons in it, so it is up to the parser to decide where +to insert them. + + (function(window, undefined) { + function test(options) { + log('testing!') + + (options.list || []).forEach(function(i) { + + }) + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ) + + return + { + foo: function() {} + } + } + window.test = test + + })(window) + + (function(window) { + window.someLibrary = {} + + })(window) + +Below is the result of the parser's "guessing" game. + + (function(window, undefined) { + function test(options) { + + // Not inserted, lines got merged + log('testing!')(options.list || []).forEach(function(i) { + + }); // <- inserted + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ); // <- inserted + + return; // <- inserted, breaks the return statement + { // treated as a block + + // a label and a single expression statement + foo: function() {} + }; // <- inserted + } + window.test = test; // <- inserted + + // The lines got merged again + })(window)(function(window) { + window.someLibrary = {}; // <- inserted + + })(window); //<- inserted + +> **Note:** The JavaScript parser does not "correctly" handle return statements +> which are followed by a new line, while this is not neccessarily the fault of +> the automatic semicolon insertion, it can still be an unwanted side-effect. + +The parser drastically changed the behavior of the code above, in certain cases +it does the **wrong thing**. + +### Leading parenthesis + +In case of a leading parenthesis, the parser will **not** insert a semicolon. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +This code gets transformed into one line. + + log('testing!')(options.list || []).forEach(function(i) {}) + +Chances are **very** high that `log` does **not** return a function; therefore, +the above will yield a `TypeError` stating that `undefined is not a function`. + +### In conclusion + +It is highly recommended to **never** omit semicolons, it is also advocated to +keep braces on the same line with their corresponding statements and to never omit +them for one single-line `if` / `else` statements. Both of these measures will +not only improve the consistency of the code, they will also prevent the +JavaScript parser from changing its behavior. + diff --git a/doc/ru/core/undefined.md b/doc/ru/core/undefined.md new file mode 100644 index 00000000..e4e0e49f --- /dev/null +++ b/doc/ru/core/undefined.md @@ -0,0 +1,72 @@ +## `undefined` and `null` + +JavaScript has two distinct values for `nothing`, the more useful of these two +being `undefined`. + +### The value `undefined` + +`undefined` is a type with exactly one value: `undefined`. + +The language also defines a global variable that has the value of `undefined`, +this variable is also called `undefined`. But this variable is **not** a constant, +nor is it a keyword of the language. This means that its *value* can be easily +overwritten. + +> **ES5 Note:** `undefined` in ECMAScript 5 is **no longer** *writable* in strict +> mode, but its name can still be shadowed by for example a function with the name +> `undefined`. + +Some examples for when the value `undefined` is returned: + + - Accessing the (unmodified) global variable `undefined`. + - Implicit returns of functions due to missing `return` statements. + - `return` statements which do not explicitly return anything. + - Lookups of non-existent properties. + - Function parameters which do not had any explicit value passed. + - Anything that has been set to the value of `undefined`. + +### Handling changes to the value of `undefined` + +Since the global variable `undefined` only holds a copy of the actual *value* of +`undefined`, assigning a new value to it does **not** change the value of the +*type* `undefined`. + +Still, in order to compare something against the value of `undefined` it is +necessary to retrieve the value of `undefined` first. + +In order to protect code against a possible overwritten `undefined` variable, a +common technique used is to add an additional parameter to an +[anonymous wrapper](#function.scopes), that gets no argument passed to it. + + var undefined = 123; + (function(something, foo, undefined) { + // undefined in the local scope does + // now again refer to the value + + })('Hello World', 42); + +Another way to achieve the same effect would be to use a declaration inside the +wrapper. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +The only difference being here, that this version results in 4 more bytes being +used in case it is minified and there is no other `var` statement inside the +anonymous wrapper. + +### Uses of `null` + +While `undefined` in the context of the JavaScript language is mostly used in +the sense of a traditional *null*, the actual `null` (both a literal and a type) +is more or less just another data type. + +It is used in some JavaScript internals (like declaring the end of the +prototype chain by setting `Foo.prototype = null`), but in almost all cases it +can be replaced by `undefined`. + + diff --git a/doc/ru/function/arguments.md b/doc/ru/function/arguments.md new file mode 100644 index 00000000..915afdf0 --- /dev/null +++ b/doc/ru/function/arguments.md @@ -0,0 +1,119 @@ +## The `arguments` Object + +Every function scope in JavaScript can access the special variable `arguments`. +This variable holds a list of all the arguments that were passed to the function. + +> **Note:** In case `arguments` has already been defined inside the function's +> scope either via a `var` statement or being the name of a formal parameter, +> the `arguments` object will not be created. + +The `arguments` object is **not** an `Array`. While it has some of the +semantics of an array - namely the `length` property - it does not inherit from +`Array.prototype` and is in fact an `Object`. + +Due to this, it is **not** possible to use standard array methods like `push`, +`pop` or `slice` on `arguments`. While iteration with a plain `for` loop works +just fine, it is necessary to convert it to a real `Array` in order to use the +standard `Array` methods on it. + +### Converting to an array + +The code below will return a new `Array` containing all the elements of the +`arguments` object. + + Array.prototype.slice.call(arguments); + +This conversion is **slow**, it is **not recommended** to use it in performance +critical sections of code. + +### Passing arguments + +The following is the recommended way of passing arguments from one function to +another. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // do stuff here + } + +Another trick is to use both `call` and `apply` together to create fast, unbound +wrappers. + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Create an unbound version of "method" + // It takes the parameters: this, arg1, arg2...argN + Foo.method = function() { + + // Result: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +### Formal parameters and arguments indexes + +The `arguments` object creates *getter* and *setter* functions for both its +properties as well as the function's formal parameters. + +As a result, changing the value of a formal parameter will also change the value +of the corresponding property on the `arguments` object, and the other way around. + + function foo(a, b, c) { + arguments[0] = 2; + a; // 2 + + b = 4; + arguments[1]; // 4 + + var d = c; + d = 9; + c; // 3 + } + foo(1, 2, 3); + +### Performance myths and truths + +The `arguments` object is always created with the only two exceptions being the +cases where it is declared as a name inside of a function or one of its formal +parameters. It does not matter whether it is used or not. + +Both *getters* and *setters* are **always** created; thus, using it has nearly +no performance impact at all, especially not in real world code where there is +more than a simple access to the `arguments` object's properties. + +> **ES5 Note:** These *getters* and *setters* are not created in strict mode. + +However, there is one case which will drastically reduce the performance in +modern JavaScript engines. That case is the use of `arguments.callee`. + + function foo() { + arguments.callee; // do something with this function object + arguments.callee.caller; // and the calling function object + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // Would normally be inlined... + } + } + +In the above code, `foo` can no longer be a subject to [inlining][1] since it +needs to know about both itself and its caller. This not only defeats possible +performance gains that would arise from inlining, it also breaks encapsulation +since the function may now be dependent on a specific calling context. + +It is **highly recommended** to **never** make use of `arguments.callee` or any of +its properties. + +> **ES5 Note:** In strict mode, `arguments.callee` will throw a `TypeError` since +> its use has been deprecated. + +[1]: http://en.wikipedia.org/wiki/Inlining + + diff --git a/doc/ru/function/closures.md b/doc/ru/function/closures.md new file mode 100644 index 00000000..0bb6d66d --- /dev/null +++ b/doc/ru/function/closures.md @@ -0,0 +1,98 @@ +## Closures and References + +One of JavaScript's most powerful features is the availability of *closures*, +this means that scopes **always** keep access to the outer scope they were +defined in. Since the only scoping that JavaScript has is +[function scope](#function.scopes), all functions, by default, act as closures. + +### Emulating private variables + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Here, `Counter` returns **two** closures. The function `increment` as well as +the function `get`. Both of these functions keep a **reference** to the scope of +`Counter` and, therefore, always keep access to the `count` variable that was +defined in that very scope. + +### Why private variables work + +Since it is not possible to reference or assign scopes in JavaScript, there is +**no** way of accessing the variable `count` from the outside. The only way to +interact with it is via the two closures. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +The above code will **not** change the variable `count` in the scope of `Counter`, +since `foo.hack` was not defined in **that** scope. It will instead create - or +override - the *global* variable `count`. + +### Closures inside loops + +One often made mistake is to use closures inside of loops, as if they were +copying the value of the loops index variable. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +The above will **not** output the numbers `0` through `9`, but will simply print +the number `10` ten times. + +The *anonymous* function keeps a **reference** to `i` and at the time +`console.log` gets called, the `for loop` has already finished and the value of +`i` as been set to `10`. + +In order to get the desired behavior, it is necessary to create a **copy** of +the value of `i`. + +### Avoiding the reference problem + +In order to copy the value of the loop's index variable, it is best to use an +[anonymous wrapper](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +The anonymous outer function gets called immediately with `i` as its first +argument and will receive a copy of the **value** of `i` as its parameter `e`. + +The anonymous function that gets passed to `setTimeout` now has a reference to +`e`, whose value does **not** get changed by the loop. + +There is another possible way of achieving this; that is to return a function +from the anonymous wrapper, that will then have the same behavior as the code +above. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + diff --git a/doc/ru/function/constructors.md b/doc/ru/function/constructors.md new file mode 100644 index 00000000..52935451 --- /dev/null +++ b/doc/ru/function/constructors.md @@ -0,0 +1,128 @@ +## Constructors + +Constructors in JavaScript are yet again different from many other languages. Any +function call that is preceded by the `new` keyword acts as a constructor. + +Inside the constructor - the called function - the value of `this` refers to a +newly created `Object`. The [`prototype`](#object.prototype) of this **new** +object is set to the `prototype` of the function object that was invoked as the +constructor. + +If the function that was called has no explicit `return` statement, then it +implicitly returns the value of `this` - the new object. + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +The above calls `Foo` as constructor and sets the `prototype` of the newly +created object to `Foo.prototype`. + +In case of an explicit `return` statement the function returns the value +specified that statement, **but only** if the return value is an `Object`. + + function Bar() { + return 2; + } + new Bar(); // a new object + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // the returned object + +When the `new` keyword is omitted, the function will **not** return a new object. + + function Foo() { + this.bla = 1; // gets set on the global object + } + Foo(); // undefined + +While the above example might still appear to work in some cases, due to the +workings of [`this`](#function.this) in JavaScript, it will use the +*global object* as the value of `this`. + +### Factories + +In order to be able to omit the `new` keyword, the constructor function has to +explicitly return a value. + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +Both calls to `Bar` return the exact same thing, a newly create object which +has a property called `method` on it, that is a +[Closure](#function.closures). + +It is also to note that the call `new Bar()` does **not** affect the prototype +of the returned object. While the prototype will be set on the newly created +object, `Bar` never returns that new object. + +In the above example, there is no functional difference between using and +not using the `new` keyword. + + +### Creating new objects via factories + +An often made recommendation is to **not** use `new` since forgetting its use +may lead to bugs. + +In order to create new object, one should rather use a factory and construct a +new object inside of that factory. + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +While the above is robust against a missing `new` keyword and certainly makes +the use of [private variables](#function.closures) easier, it comes with some +downsides. + + 1. It uses more memory since the created objects do **not** share the methods + on a prototype. + 2. In order to inherit the factory needs to copy all the methods from another + object or put that object on the prototype of the new object. + 3. Dropping the prototype chain just because of a left out `new` keyword + somehow goes against the spirit of the language. + +### In conclusion + +While omitting the `new` keyword might lead to bugs, it is certainly **not** a +reason to drop the use of prototypes altogether. In the end it comes down to +which solution is better suited for the needs of the application, it is +especially important to choose a specific style of object creation **and stick** +with it. + diff --git a/doc/ru/function/general.md b/doc/ru/function/general.md new file mode 100644 index 00000000..5bfd6cd8 --- /dev/null +++ b/doc/ru/function/general.md @@ -0,0 +1,48 @@ +## Function Declarations and Expressions + +Functions in JavaScript are first class objects, that means that they can be +passed around like any other value. One common use of this feature is to pass +an *anonymous function* as a callback to another, possibly asynchronous function. + +### The `function` declaration + + function foo() {} + +The above function gets [hoisted](#function.scopes) before the execution of the +program starts; thus, it is available *everywhere* in the scope it was *defined* +in, even if called before the actual definition in the source. + + foo(); // Works because foo was created before this code runs + function foo() {} + +### The `function` expression + + var foo = function() {}; + +This example assigns the unnamed and *anonymous* function to the variable `foo`. + + foo; // 'undefined' + foo(); // this raises a TypeError + var foo = function() {}; + +Due to the fact that `var` is a declaration, that hoists the variable name `foo` +before the actual execution of the code starts, `foo` is already defined when +the script gets executed. + +But since assignments only happens at runtime, the value of `foo` will default +to [undefined](#core.undefined) before the corresponding code is executed. + +### Named function expression + +Another special case is the assignment of named functions. + + var foo = function bar() { + bar(); // Works + } + bar(); // ReferenceError + +Here `bar` is not available in the outer scope, since the function only gets +assigned to `foo`; however, inside of `bar` it is available. This is due to +how [name resolution](#function.scopes) in JavaScript works, the name of the +function is *always* made available in the local scope of the function itself. + diff --git a/doc/ru/function/scopes.md b/doc/ru/function/scopes.md new file mode 100644 index 00000000..95735b07 --- /dev/null +++ b/doc/ru/function/scopes.md @@ -0,0 +1,231 @@ +## Scopes and Namespaces + +Although JavaScript deals fine with the syntax of two matching curly +braces for blocks, it does **not** support block scope; hence, all that is left +is in the language is *function scope*. + + function test() { // a scope + for(var i = 0; i < 10; i++) { // not a scope + // count + } + console.log(i); // 10 + } + +> **Note:** When not used in an assignment, return statement or as a function +> argument, the `{...}` notation will get interpreted as a block statement and +> **not** as an object literal. This, in conjunction with +> [automatic insertion of semicolons](#core.semicolon), can lead to subtle errors. + +There are also no distinct namespaces in JavaScript, that means that everything +gets defined in one *globally shared* namespace. + +Each time a variable is referenced, JavaScript will traverse upwards through all +the scopes until it finds it. In the case that it reaches the global scope and +still has not found the requested name, it will raise a `ReferenceError`. + +### The bane of global variables + + // script A + foo = '42'; + + // script B + var foo = '42' + +The above two scripts do **not** have the same effect. Script A defines a +variable called `foo` in the *global* scope and script B defines a `foo` in the +*current* scope. + +Again, that is **not** at all the *same effect*, not using `var` can have major +implications. + + // global scope + var foo = 42; + function test() { + // local scope + foo = 21; + } + test(); + foo; // 21 + +Leaving out the `var` statement inside the function `test` will override the +value of `foo`. While this might not seem like a big deal at first, having +thousands of lines of JavaScript and not using `var` will introduce horrible and +hard to track down bugs. + + // global scope + var items = [/* some list */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // scope of subLoop + for(i = 0; i < 10; i++) { // missing var statement + // do amazing stuff! + } + } + +The outer loop will terminate after the first call to `subLoop`, since `subLoop` +overwrites the global value of `i`. Using a `var` for the second `for` loop would +have easily avoided this error. The `var` statement should **never** be left out +unless the *desired effect* is to affect the outer scope. + +### Local variables + +The only source for local variables in JavaScript are +[function](#function.general) parameters and variables that were declared via the +`var` statement. + + // global scope + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // local scope of the function test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +While `foo` and `i` are local variables inside the scope of the function `test`, +the assignment of `bar` will override the global variable with the same name. + +### Hoisting + +JavaScript **hoists** declarations. This means that both `var` statements and +`function` declarations will be moved to the top of their enclosing scope. + + bar(); + var bar = function() {}; + var someValue = 42; + + test(); + function test(data) { + if (false) { + goo = 1; + + } else { + var goo = 2; + } + for(var i = 0; i < 100; i++) { + var e = data[i]; + } + } + +The above code gets transformed before any execution is started. JavaScript moves +the `var` statements as well as the `function` declarations to the top of the +nearest surrounding scope. + + // var statements got moved here + var bar, someValue; // default to 'undefined' + + // the function declartion got moved up too + function test(data) { + var goo, i, e; // missing block scope moves these here + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // fails with a TypeError since bar is still 'undefined' + someValue = 42; // assignments are not affected by hoisting + bar = function() {}; + + test(); + +Missing block scoping will not only move `var` statements out of loops and +their bodies, it will also make the results of certain `if` constructs +non-intuitive. + +In the original code the `if` statement seemed to modify the *global +variable* `goo`, while actually it modifies the *local variable* - after hoisting +has been applied. + +Without the knowledge about *hoisting*, below code might seem to raise a +`ReferenceError`. + + // check whether SomeImportantThing has been initiliazed + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +But of course, the above works due to the fact that the `var` statement is being +moved to the top of the *global scope*. + + var SomeImportantThing; + + // other code might initiliaze SomeImportantThing here, or not + + // make sure it's there + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Name resolution order + +All scopes in JavaScript, including the *global scope*, have the special name +[`this`](#function.this) defined in them, which refers to the *current object*. + +Function scopes also have the name [`arguments`](#function.arguments) defined in +them which contains the arguments that were passed to a function. + +For example, when trying to access a variable named `foo` inside the scope of a +function, JavaScript will lookup the name in the following order: + + 1. In case there is a `var foo` statement in the current scope use that. + 2. If one of the function parameters is named `foo` use that. + 3. If the function itself is called `foo` use that. + 4. Go to the next outer scope and start with **#1** again. + +> **Note:** Having a parameter called `arguments` will **prevent** the creation +> of the default `arguments` object. + +### Namespaces + +A common problem of having only one global namespace is the likeliness of running +into problems where variable names clash. In JavaScript, this problem can +easily be avoided with the help of *anonymous wrappers*. + + (function() { + // a self contained "namespace" + + window.foo = function() { + // an exposed closure + }; + + })(); // execute the function immediately + + +Unnamed functions are considered [expressions](#function.general); so in order to +being callable, they must first be evaluated. + + ( // evaluate the function inside the paranthesis + function() {} + ) // and return the function object + () // call the result of the evaluation + +There are other ways for evaluating and calling the function expression; which, +while different in syntax, do behave the exact same way. + + // Two other ways + +function(){}(); + (function(){}()); + +### In conclusion + +It is recommended to always use an *anonymous wrapper* for encapsulating code in +its own namespace. This does not only protect code against name clashes, it +also allows for better modularization of programs. + +Additionally, the use of global variables is considered **bad practice**. **Any** +use of them indicates badly written code that is prone to errors and hard to maintain. + diff --git a/doc/ru/function/this.md b/doc/ru/function/this.md new file mode 100644 index 00000000..f1927e24 --- /dev/null +++ b/doc/ru/function/this.md @@ -0,0 +1,111 @@ +## How `this` Works + +JavaScript has a different concept of what the special name `this` refers to +than most other programming languages do. There are exactly **five** different +ways in which the value of `this` can be bound in the language. + +### The global scope + + this; + +When using `this` in global scope, it will simply refer to the *global* object. + + +### Calling a function + + foo(); + +Here `this` will again refer to the *global* object. + +> **ES5 Note:** In strict mode, the global case **no longer** exists. +> `this` will instead have the value of `undefined` in that case. + +### Calling a method + + test.foo(); + +In this example `this` will refer to `test`. + +### Calling a constructor + + new foo(); + +A function call that is preceded by the `new` keyword acts as +a [constructor](#function.constructors). Inside the function `this` will refer +to a *newly created* `Object`. + +### Explicit setting of `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // array will expand to the below + foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3 + +When using the `call` or `apply` methods of `Function.prototype`, the value of +`this` inside the called function gets **explicitly set** to the first argument +of the corresponding function call. + +As a result, the above example the *method case* does **not** apply, and `this` +inside of `foo` will be set to `bar`. + +> **Note:** `this` **cannot** be used to refer to the object inside of an `Object` +> literal. So `var obj = {me: this}` will **not** result in `me` referring to +> `obj`, since `this` only gets bound by one of the five listed cases. + +### Common pitfalls + +While most of these cases make sense, the first one is to be considered another +mis-design of the language, as it **never** has any practical use. + + Foo.method = function() { + function test() { + // this is set to the global object + } + test(); + } + +A common misconception is that `this` inside of `test` refers to `Foo`, while in +fact it **does not**. + +In order to gain access to `Foo` from within `test` it is necessary to create a +local variable inside of `method` which refers to `Foo`. + + Foo.method = function() { + var that = this; + function test() { + // Use that instead of this here + } + test(); + } + +`that` is just a normal name, but it is commonly used for the reference to an +outer `this`. In combination with [closures](#function.closures), it can also +be used to pass `this` values around. + +### Assigning methods + +Another thing that does **not** work in JavaScript is function aliasing, that is, +**assigning** a method to a variable. + + var test = someObject.methodTest; + test(); + +Due to the first case `test` now acts like like a plain function call; therefore, +`this` inside it will no longer refer to `someObject`. + +While the late binding of `this` might seem like a bad idea at first, it is in +fact what makes [prototypal inheritance](#object.prototype) work. + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +When `method` gets called on a instance of `Bar`, `this` will now refer to that +very instance. + + diff --git a/doc/ru/index.json b/doc/ru/index.json new file mode 100644 index 00000000..cffddf23 --- /dev/null +++ b/doc/ru/index.json @@ -0,0 +1,72 @@ +{ + "title": "JavaScript Гарден", + "langTitle": "JavaScript Гарден по-русски", + "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/doc/ru/intro/authors.md b/doc/ru/intro/authors.md new file mode 100644 index 00000000..f88f94a7 --- /dev/null +++ b/doc/ru/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/doc/ru/intro/contributors.md b/doc/ru/intro/contributors.md new file mode 100644 index 00000000..73fb98ea --- /dev/null +++ b/doc/ru/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/doc/ru/intro/index.md b/doc/ru/intro/index.md new file mode 100644 index 00000000..f2d5d31b --- /dev/null +++ b/doc/ru/intro/index.md @@ -0,0 +1,15 @@ +## Intro + +**JavaScript Garden** is a growing collection of documentation about the most +quirky parts of the JavaScript programming language. It gives advice to +avoid common mistakes, subtle bugs, as well as performance issues and bad +practices that non-expert JavaScript programmers may encounter on their +endeavours into the depths of the language. + +JavaScript Garden does **not** aim to teach you JavaScript. Former knowledge +of the language is strongly recommended in order to understand the topics covered +in this guide. In order to learn the basics of the language, please head over to +the excellent [guide][1] on the Mozilla Developer Network. + +[1]: https://developer.mozilla.org/en/JavaScript/Guide + diff --git a/doc/ru/intro/license.md b/doc/ru/intro/license.md new file mode 100644 index 00000000..cd398699 --- /dev/null +++ b/doc/ru/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/doc/ru/object/forinloop.md b/doc/ru/object/forinloop.md new file mode 100644 index 00000000..6150e259 --- /dev/null +++ b/doc/ru/object/forinloop.md @@ -0,0 +1,51 @@ +## The `for in` Loop + +Just like the `in` operator, the `for in` loop also traverses the prototype +chain when iterating over the properties of an object. + +> **Note:** The `for in` loop will **not** iterate over any properties that +> have their `enumerable` attribute set to `false`; for example, the `length` +> property of an array. + + // Poisoning Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // prints both bar and moo + } + +Since it is not possible to change the behavior of the `for in` loop itself, it +is necessary to filter out the unwanted properties inside the loop body , +this is done by using the [`hasOwnProperty`](#object.hasownproperty) method of +`Object.prototype`. + +> **Note:** Since the `for in` always traverses the complete prototype chain, it +> will get slower with each additional layer of inheritance added to an object. + +### Using `hasOwnProperty` for filtering + + // still the foo from above + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +This version is the only correct one to use. Due to the use of `hasOwnPropery` it +will **only** print out `moo`. When `hasOwnProperty` is left out, the code is +prone to errors in cases where the native prototypes - e.g. `Object.prototype` - +have been extended. + +One widely used framework which does this is [Prototype][1]. When this +framework is included, `for in` loops that do not use `hasOwnProperty` are +guaranteed to break. + +### Best practices + +It is recommended to **always** use `hasOwnProperty`. Never should any +assumptions be made about the environment the code is running in, or whether the +native prototypes have been extended or not. + +[1]: http://www.prototypejs.org/ + diff --git a/doc/ru/object/general.md b/doc/ru/object/general.md new file mode 100644 index 00000000..b4fc816b --- /dev/null +++ b/doc/ru/object/general.md @@ -0,0 +1,99 @@ +## Object Usage and Properties + +Everything in JavaScript acts like an object, with the only two exceptions being +[`null`](#core.undefined) and [`undefined`](#core.undefined). + + false.toString() // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +A common misconception is that number literals cannot be used as +objects. That is because a flaw in JavaScript's parser tries to parse the *dot +notation* on a number as a floating point literal. + + 2.toString(); // raises SyntaxError + +There are a couple of workarounds which can be used in order make number +literals act as objects too. + + 2..toString(); // the second point is correctly recognized + 2 .toString(); // note the space left to the dot + (2).toString(); // 2 is evaluated first + +### Objects as a data type + +Objects in JavaScript can also be used as a [*Hashmap*][1], they mainly consist +of named properties mapping to values. + +Using a object literal - `{}` notation - it is possible to create a +plain object. This new object [inherits](#object.prototype) from `Object.prototype` and +has no [own properties](#object.hasownproperty) defined on it. + + var foo = {}; // a new empty object + + // a new object with a property called 'test' with value 12 + var bar = {test: 12}; + +### Accessing properties + +The properties of an object can be accessed in two ways, via either the dot +notation, or the square bracket notation. + + var foo = {name: 'Kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // works + +Both notations are identical in their workings, with the only difference being that +the square bracket notation allows for dynamic setting of properties, as well as +the use of property names that would otherwise lead to a syntax error. + +### Deleting properties + +The only way to actually remove a property from an object is to use the `delete` +operator; setting the property to `undefined` or `null` only remove the +*value* associated with the property, but not the *key*. + + var obj = { + bar: 1, + foo: 2, + baz: 3 + }; + obj.bar = undefined; + obj.foo = null; + delete obj.baz; + + for(var i in obj) { + if (obj.hasOwnProperty(i)) { + console.log(i, '' + obj[i]); + } + } + +The above outputs both `bar undefined` and `foo null` - only `baz` was +removed and is therefore missing from the output. + +### Notation of keys + + var test = { + 'case': 'I am a keyword so I must be notated as a string', + delete: 'I am a keyword too so me' // raises SyntaxError + }; + +Object properties can be both notated as plain characters and as strings. Due to +another mis-design in JavaScript's parser, the above will throw +a `SyntaxError` prior to ECMAScript 5. + +This error arises from the fact that `delete` is a *keyword*; therefore, it must be +notated as a *string literal* to ensure that it will be correctly interpreted by +older JavaScript engines. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/doc/ru/object/hasownproperty.md b/doc/ru/object/hasownproperty.md new file mode 100644 index 00000000..6b18353d --- /dev/null +++ b/doc/ru/object/hasownproperty.md @@ -0,0 +1,53 @@ +## `hasOwnProperty` + +In order to check whether a object has a property defined on *itself* and **not** +somewhere on its [prototype chain](#object.prototype), it is necessary to use the +`hasOwnProperty` method which all objects inherit from `Object.prototype`. + +> **Note:** It is **not** enough to check whether a property is `undefined`. The +> property might very well exist, but its value just happens to be set to +> `undefined`. + +`hasOwnProperty` is the only thing in JavaScript which deals with properties and +does **not** traverse the prototype chain. + + // Poisoning Object.prototype + Object.prototype.bar = 1; + var foo = {goo: undefined}; + + foo.bar; // 1 + 'bar' in foo; // true + + foo.hasOwnProperty('bar'); // false + foo.hasOwnProperty('goo'); // true + +Only `hasOwnProperty` will give the correct and expected result, this is +essential when iterating over the properties of any object. There is **no** other +way to exclude properties that are not defined on the object *itself*, but +somewhere on its prototype chain. + +### `hasOwnProperty` as a property + +JavaScript does **not** protect the property name `hasOwnProperty`; thus, if the +possibility exists that an object might have a property with this name, it is +necessary to use an *external* `hasOwnProperty` in order to get correct results. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // always returns false + + // Use another hasOwnProperty and call it with 'this' set to foo + {}.hasOwnProperty.call(foo, 'bar'); // true + +### In conclusion + +When checking for the existence of a property on a object, `hasOwnProperty` is +the **only** method of doing so. It is also recommended to make `hasOwnProperty` +part of **every** [`for in` loop](#object.forinloop), this will avoid errors from +extended native [prototypes](#object.prototype). + diff --git a/doc/ru/object/prototype.md b/doc/ru/object/prototype.md new file mode 100644 index 00000000..55ca096e --- /dev/null +++ b/doc/ru/object/prototype.md @@ -0,0 +1,116 @@ +## The Prototype + +JavaScript does not feature a classical inheritance model, instead it uses a +*prototypal* one. + +While this is often considered to be one of JavaScript's weaknesses, the +prototypal inheritance model is in fact more powerful than the classic model. +It is for example fairly trivial to build a classic model on top of it, while the +other way around is a far more difficult task. + +Due to the fact that JavaScript is basically the only widely used language that +features prototypal inheritance, it takes some time to adjust to the +differences between the two models. + +The first major difference is that inheritance in JavaScript is done by using so +called *prototype chains*. + +> **Note:** Simply using `Bar.prototype = Foo.prototype` will result in both objects +> sharing the **same** prototype. Therefore, changes to either object's prototype +> will affect the prototype of the other as well, which in most cases is not the +> desired effect. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Set Bar's prototype to a new instance of Foo + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Make sure to list Bar as the actual constructor + Bar.prototype.constructor = Bar; + + var test = new Bar() // create a new bar instance + + // The resulting prototype chain + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World' } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +In the above, the object `test` will inherit from both `Bar.prototype` and +`Foo.prototype`; hence, it will have access to the function `method` that was +defined on `Foo`. It will also have access to the property `value` of the +**one** `Foo` instance that is its prototype. It is important to note that `new +Bar()` does **not** create a new `Foo` instance, but reuses the one assigned to +its prototype; thus, all `Bar` instances will share the **same** `value` property. + +> **Note:** Do **not** use `Bar.prototype = Foo`, since it will not point to +> the prototype of `Foo` but rather to the function object `Foo`. So the +> prototype chain will go over `Function.prototype` and not `Foo.prototype`; +> therefore, `method` will not be on the prototype chain. + +### Property lookup + +When accessing the properties of an object, JavaScript will traverse the +prototype chain **upwards** until it finds a property with the requested name. + +When it reaches the top of the chain - namely `Object.prototype` - and still +hasn't found the specified property, it will return the value +[undefined](#core.undefined) instead. + +### The prototype property + +While the prototype property is used by the language to build the prototype +chains, it is still possible to assign **any** given value to it. Although +primitives will simply get ignored when assigned as a prototype. + + function Foo() {} + Foo.prototype = 1; // no effect + +Assigning objects, as shown in the example above, will work, and allows for dynamic +creation of prototype chains. + +### Performance + +The lookup time for properties that are high up on the prototype chain can have a +negative impact on performance critical sections of code. Additionally, trying to +access non-existent properties will always traverse the full prototype chain. + +Also, when [iterating](#object.forinloop) over the properties of an object +**every** property that is on the prototype chain will get enumerated. + +### Extension of native prototypes + +One mis-feature that is often used is to extend `Object.prototype` or one of the +other built in prototypes. + +This technique is called [monkey patching][1] and breaks *encapsulation*. While +used by widely spread frameworks such as [Prototype][2], there is still no good +reason for cluttering built-in types with additional *non-standard* functionality. + +The **only** good reason for extending a built-in prototype is to backport +the features of newer JavaScript engines; for example, +[`Array.forEach`][3]. + +### In conclusion + +It is a **must** to understand the prototypal inheritance model completely +before writing complex code which makes use of it. Also, watch the length of +the prototype chains and break them up if necessary to avoid possible +performance issues. Further, the native prototypes should **never** be extended +unless it is for the sake of compatibility with newer JavaScript features. + +[1]: http://en.wikipedia.org/wiki/Monkey_patch +[2]: http://prototypejs.org/ +[3]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach + diff --git a/doc/ru/other/timeouts.md b/doc/ru/other/timeouts.md new file mode 100644 index 00000000..c988702e --- /dev/null +++ b/doc/ru/other/timeouts.md @@ -0,0 +1,155 @@ +### `setTimeout` and `setInterval` + +Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the `setTimeout` and `setInterval` functions. + +> **Note:** Timeouts are **not** part of the ECMAScript Standard, they are +> implemented as part of the [DOM][1]. + + function foo() {} + var id = setTimeout(foo, 1000); // returns a Number > 0 + +When `setTimeout` gets called, it will return the ID of the timeout and schedule +`foo` to run in **approximately** one thousand milliseconds in the future. +`foo` will then get executed exactly **once**. + +Depending on the timer resolution of the JavaScript engine that is running the +code, as well as the fact that JavaScript is single threaded and other code that +gets executed might block the thread, it is by **no means** a safe bet that one +will get the exact delay that was specified in the `setTimeout` call. + +The function that was passed as the first parameter will get called by the +*global object*, that means, that [`this`](#function.this) inside the called function +refers to that very object. + + function Foo() { + this.value = 42; + this.method = function() { + // this refers to the global object + console.log(this.value); // will log undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **Note:** As `setTimeout` takes a **function object** as its first parameter, an +> often made mistake is to use `setTimeout(foo(), 1000)`, which will use the +> **return value** of the call `foo` and **not** `foo`. This is, most of the time, +> a silent error, since when the function returns `undefined` `setTimeout` will +> **not** raise any error. + +### Stacking calls with `setInterval` + +While `setTimeout` only runs the function once, `setInterval` - as the name +suggests - will execute the function **every** `X` milliseconds. But its use is +discouraged. + +When code that is being executed blocks the timeout call, `setInterval` will +still issue more calls to the specified function. This can, especially with small +intervals, result in function calls stacking up. + + function foo(){ + // something that blocks for 1 second + } + setInterval(foo, 100); + +In the above code `foo` will get called once and will then block for one second. + +While `foo` blocks the code `setInterval` will still schedule further calls to +it. Now, when `foo` has finished, there will already be **ten** further calls to +it waiting for execution. + +### Dealing with possible blocking code + +The easiest as well as most controllable solution, is to use `setTimeout` within +the function itself. + + function foo(){ + // something that blocks for 1 second + setTimeout(foo, 100); + } + foo(); + +Not only does this encapsulate the `setTimeout` call, but it also prevents the +stacking of calls and it gives additional control.`foo` itself can now decide +whether it wants to run again or not. + +### Manually clearing timeouts + +Clearing timeouts and intervals works by passing the respective ID to +`clearTimeout` or `clearInterval`, depending which `set` function was used in +the first place. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Clearing all timeouts + +As there is no built-in method for clearing all timeouts and/or intervals, +it is necessary to use brute force in order to achieve this functionality. + + // clear "all" timeouts + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +There might still be timeouts that are unaffected by this arbitrary number; +therefore, is is instead recommended to keep track of all the timeout IDs, so +they can be cleared specifically. + +### Hidden use of `eval` + +`setTimeout` and `setInterval` can also take a string as their first parameter. +This feature should **never** be used, since it internally makes use of `eval`. + +> **Note:** Since the timeout functions are **not** specified by the ECMAScript +> standard, the exact workings when a string is passed to them might differ in +> various JavaScript implementations. As a fact, Microsoft's JScript makes use of +> the `Function` constructor in place of `eval`. + + function foo() { + // will get called + } + + function bar() { + function foo() { + // never gets called + } + setTimeout('foo()', 1000); + } + bar(); + +Since `eval` is not getting called [directly](#core.eval) in this case, the string +passed to `setTimeout` will get executed in the *global scope*; thus, it will +not use the local variable `foo` from the scope of `bar`. + +It is further recommended to **not** use a string for passing arguments to the +function that will get called by either of the timeout functions. + + function foo(a, b, c) {} + + // NEVER use this + setTimeout('foo(1,2, 3)', 1000) + + // Instead use an anonymous function + setTimeout(function() { + foo(a, b, c); + }, 1000) + +> **Note:** While it is also possible to use the syntax +> `setTimeout(foo, 1000, a, b, c)`, it is not recommended, as its use may lead +> to subtle errors when used with [methods](#function.this). + +### In conclusion + +**Never** should a string be used as the parameter of `setTimeout` or +`setInterval`. It is a clear sign of **really** bad code, when arguments need +to be supplied to the function that gets called. An *anonymous function* should +be passed that then takes care of the actual call. + +Further, the use of `setInterval` should be avoided since its scheduler is not +blocked by executing JavaScript. + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model + diff --git a/doc/ru/types/casting.md b/doc/ru/types/casting.md new file mode 100644 index 00000000..34b320d0 --- /dev/null +++ b/doc/ru/types/casting.md @@ -0,0 +1,70 @@ +## Type casting + +JavaScript is a *weakly typed* language, so it will apply *type coercion* +**wherever** possible. + + // These are true + new Number(10) == 10; // Number.toString() is converted + // back to a number + + 10 == '10'; // Strings gets converted to Number + 10 == '+10 '; // More string madness + 10 == '010'; // And more + isNaN(null) == false; // null converts to 0 + // which of course is not NaN + + // These are false + 10 == 010; + 10 == '-10'; + +> **ES5 Note:** Number literals that start with a `0` are interpreted as octal +> (Base 8). Octal support for these has been **removed** in ECMAScript 5 strict +> mode. + +In order to avoid the above, use of the [strict equal operator](#types.equality) +is **highly** recommended. Although this avoids a lot of common pitfalls, there +are still many further issues that arise from JavaScript's weak typing system. + +### Constructors of built-in types + +The constructors of the built in types like `Number` and `String` behave +differently when being used with the `new` keyword and without it. + + new Number(10) === 10; // False, Object and Number + Number(10) === 10; // True, Number and Number + new Number(10) + 0 === 10; // True, due to implicit conversion + +Using a built-in type like `Number` as a constructor will create a new `Number` +object, but leaving out the `new` keyword will make the `Number` function behave +like a converter. + +In addition, having literals or non-object values in there will result in even +more type coercion. + +The best option is to cast to one of the three possible types **explicitly**. + +### Casting to a string + + '' + 10 === '10'; // true + +By prepending a empty string a value can easily be casted to a string. + +### Casting to a number + + +'10' === 10; // true + +Using the **unary** plus operator it is possible to cast to a number. + +### Casting to a boolean + +By using the **not** operator twice, a value can be converted a boolean. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + + diff --git a/doc/ru/types/equality.md b/doc/ru/types/equality.md new file mode 100644 index 00000000..1bc4c9cf --- /dev/null +++ b/doc/ru/types/equality.md @@ -0,0 +1,71 @@ +## Equality and comparisons + +JavaScript has two different ways of comparing the values of objects for equality. + +### The equals operator + +The equals operator consists of two equal signs: `==` + +JavaScript features *weak typing*, that means, that the equals 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 equals operator + +The strict equals operator consists of **three** equal signs: `===` + +Other than the normal equals operator, the strict equals operator does **not** +perform type coercion between its operands. + + "" === "0" // false + 0 === "" // false + 0 === "0" // false + false === "false" // false + false === "0" // false + false === undefined // false + false === null // false + null === undefined // false + " \t\r\n" === 0 // false + +The above results are a lot clearer and allow for early breakage of code. This +hardens code to a certain degree and also gives performance improvements in case +the operands are of different types. + +### Comparing objects + +While both `==` and `===` are stated as **equality** operators, they behave +different when at least one of their operands happens to be an `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Here both operators compare for **identity** and **not** equality; that is, they +will compare for the same **instance** of the object, much like `is` in Python +and a pointer comparison in C do. + +### In conclusion + +It is highly recommended to only use the **strict equals** operator. In cases +where types need to be coerced, it should be done [explicitly](#types.casting) +and not left to the language's complicated coercion rules. + diff --git a/doc/ru/types/instanceof.md b/doc/ru/types/instanceof.md new file mode 100644 index 00000000..6fe0d63e --- /dev/null +++ b/doc/ru/types/instanceof.md @@ -0,0 +1,38 @@ +## The `instanceof` operator + +The `instanceof` operator compares the constructors of its two operands. It is +only useful when comparing custom made objects. Used on built-in types, it is +nearly as useless as the [typeof operator](#types.typeof). + +### Comparing custom objects + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // This just sets Bar.prototype to the function object Foo + // But not to an actual instance of Foo + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### Using `instanceof` with native types + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +One important thing to note here is, that `instanceof` does not work on objects +that origin from different JavaScript contexts (e.g. different documents +in a web browser), since their constructors will not be the exact same object. + +### In conclusion + +The `instanceof` operator should **only** be used when dealing with custom made +objects that origin from the same JavaScript context. Just like the +[`typeof`](#types.typeof) operator, every other use of it should be **avoided**. + diff --git a/doc/ru/types/typeof.md b/doc/ru/types/typeof.md new file mode 100644 index 00000000..200b94d4 --- /dev/null +++ b/doc/ru/types/typeof.md @@ -0,0 +1,87 @@ +## The `typeof` operator + +The `typeof` operator (together with +[`instanceof`](#types.instanceof)) is probably the biggest +design flaw of JavaScript, as it is near of being **completely broken**. + +Although `instanceof` still has its limited uses, `typeof` really has only one +practical use case, which does **not** happen to be checking the type of an +object. + +> **Note:** While `typeof` can also be called with a function like syntax +> i.e. `typeof(obj)`, this is not a function call. The two parenthesis will +> behave like normal and the return value will be used as the operand of the +> `typeof` operator. There is **no** `typeof` function. + +### The JavaScript type table + + Value Class Type + ------------------------------------- + "foo" String string + new String("foo") String object + 1.2 Number number + new Number(1.2) Number object + true Boolean boolean + new Boolean(true) Boolean object + new Date() Date object + new Error() Error object + [1,2,3] Array object + new Array(1, 2, 3) Array object + new Function("") Function function + /abc/g RegExp object (function in Nitro/V8) + new RegExp("meow") RegExp object (function in Nitro/V8) + {} Object object + new Object() Object object + +In the above table *Type* refers to the value, that the `typeof` operator returns. +As can be clearly seen, this value is anything but consistent. + +The *Class* refers to the value of the internal `[[Class]]` property of an object. + +> **From the Specification:** The value of `[[Class]]` can be one of the +> following strings. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +In order to retrieve the value of `[[Class]]` one has to make use of the +`toString` method of `Object.prototype`. + +### The Class of an object + +The specification gives exactly one way of accessing the `[[Class]]` value, +with the use of `Object.prototype.toString`. + + function is(type, obj) { + var clas = Object.prototype.toString.call(obj).slice(8, -1); + return obj !== undefined && obj !== null && clas === type; + } + + is('String', 'test'); // true + is('String', new String('test')); // true + +In the above example, `Object.prototype.toString` gets called with the value of +[this](#function.this) being set to the object whose `[[Class]]` value should be +retrieved. + +> **ES5 Note:** For convenience the return value of `Object.prototype.toString` +> for both `null` and `undefined` was **changed** from `Object` to `Null` and +> `Undefined` in ECMAScript 5. + +### Testing for undefined variables + + typeof foo !== 'undefined' + +The above will check whether `foo` was actually declared or not; just +referencing it would result in a `ReferenceError`. This is the only thing +`typeof` is actually useful for. + +### In conclusion + +In order to check the type of an object, it is highly recommended to use +`Object.prototype.toString`; as this is the only reliable way of doing so. +As shown in the above type table, some return values of `typeof` are not defined +in the specification; thus, they can differ across various implementations. + +Unless checking whether a variable is defined, `typeof` should be avoided at +**all costs**. + + diff --git a/site/ru/index.html b/site/ru/index.html new file mode 100644 index 00000000..0a9b6635 --- /dev/null +++ b/site/ru/index.html @@ -0,0 +1,1859 @@ +JavaScript Гарден +

Вступление

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

+ +

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

The Authors

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

License

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

Объекты

Object Usage and Properties

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

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

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

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

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

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

Objects as a data type

+ +

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

+ +

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

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

Accessing properties

+ +

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

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

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

+ +

Deleting properties

+ +

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

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

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

+ +

Notation of keys

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

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

+ +

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

The Prototype

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

+ +

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

+ +

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

+ +

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

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

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

+ + + +

Property lookup

+ +

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

+ +

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

+ +

The prototype property

+ +

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

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

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

+ +

Performance

+ +

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

+ +

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

+ +

Extension of native prototypes

+ +

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

+ +

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

+ +

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

+ +

In conclusion

+ +

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

hasOwnProperty

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

+ + + +

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

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

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

+ +

hasOwnProperty as a property

+ +

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

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

In conclusion

+ +

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

The for in Loop

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

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

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

+ + + +

Using hasOwnProperty for filtering

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

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

+ +

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

+ +

Best practices

+ +

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

Функции

Function Declarations and Expressions

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

+ +

The function declaration

+ +
function foo() {}
+
+ +

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

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

The function expression

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

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

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

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

+ +

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

+ +

Named function expression

+ +

Another special case is the assignment of named functions.

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

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

How this Works

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

+ +

The global scope

+ +
this;
+
+ +

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

+ +

Calling a function

+ +
foo();
+
+ +

Here this will again refer to the global object.

+ + + +

Calling a method

+ +
test.foo(); 
+
+ +

In this example this will refer to test.

+ +

Calling a constructor

+ +
new foo(); 
+
+ +

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

+ +

Explicit setting of this

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

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

+ +

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

+ + + +

Common pitfalls

+ +

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

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

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

+ +

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

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

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

+ +

Assigning methods

+ +

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

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

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

+ +

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

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

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

Closures and References

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

+ +

Emulating private variables

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

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

+ +

Why private variables work

+ +

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

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

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

+ +

Closures inside loops

+ +

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

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

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

+ +

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

+ +

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

+ +

Avoiding the reference problem

+ +

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

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

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

+ +

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

+ +

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

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

The arguments Object

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

+ + + +

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

+ +

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

+ +

Converting to an array

+ +

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

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

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

+ +

Passing arguments

+ +

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

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

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

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

Formal parameters and arguments indexes

+ +

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

+ +

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

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

Performance myths and truths

+ +

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

+ +

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

+ + + +

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

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

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

+ +

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

+ +

Constructors

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

+ +

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

+ +

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

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

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

+ +

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

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

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

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

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

+ +

Factories

+ +

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

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

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

+ +

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

+ +

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

+ +

Creating new objects via factories

+ +

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

+ +

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

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

While the above is robust against a missing new keyword and certainly makes +the use of private variables easier, it comes with some +downsides.

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

In conclusion

+ +

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

Scopes and Namespaces

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

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

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

+ +

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

+ +

The bane of global variables

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

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

+ +

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

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

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

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

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

+ +

Local variables

+ +

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

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

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

+ +

Hoisting

+ +

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

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

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

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

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

+ +

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

+ +

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

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

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

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

Name resolution order

+ +

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

+ +

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

+ +

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

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

Namespaces

+ +

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

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

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

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

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

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

In conclusion

+ +

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

+ +

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

Массивы

Array Iteration and Properties

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

+ + + +

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

+ +

Iteration

+ +

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

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

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

+ +

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

+ +

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

+ +

The length property

+ +

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

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

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

+ +

In conclusion

+ +

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

The Array Constructor

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

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

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

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

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

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

In conclusion

+ +

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

Типы

Equality and comparisons

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

+ +

The equals operator

+ +

The equals operator consists of two equal signs: ==

+ +

JavaScript features weak typing, that means, that the equals 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 equals operator

+ +

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

+ +

Other than the normal equals operator, the strict equals operator does not +perform type coercion between its operands.

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

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

+ +

Comparing objects

+ +

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

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

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

+ +

In conclusion

+ +

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

The typeof operator

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

+ +

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

+ + + +

The JavaScript type table

+ +
Value               Class      Type
+-------------------------------------
+"foo"               String     string
+new String("foo")   String     object
+1.2                 Number     number
+new Number(1.2)     Number     object
+true                Boolean    boolean
+new Boolean(true)   Boolean    object
+new Date()          Date       object
+new Error()         Error      object
+[1,2,3]             Array      object
+new Array(1, 2, 3)  Array      object
+new Function("")    Function   function
+/abc/g              RegExp     object (function in Nitro/V8)
+new RegExp("meow")  RegExp     object (function in Nitro/V8)
+{}                  Object     object
+new Object()        Object     object
+
+ +

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

+ +

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

+ + + +

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

+ +

The Class of an object

+ +

The specification gives exactly one way of accessing the [[Class]] value, +with the use of Object.prototype.toString.

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

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

+ + + +

Testing for undefined variables

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

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

+ +

In conclusion

+ +

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

+ +

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

The instanceof operator

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

+ +

Comparing custom objects

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

Using instanceof with native types

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

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

+ +

In conclusion

+ +

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

Type casting

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

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

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

+ +

Constructors of built-in types

+ +

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

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

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

+ +

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

+ +

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

+ +

Casting to a string

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

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

+ +

Casting to a number

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

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

+ +

Casting to a boolean

+ +

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

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

Нативности

Why not to use eval

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

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

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

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

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

+ +

eval in disguise

+ +

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

+ +

Security issues

+ +

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

+ +

In conclusion

+ +

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

undefined and null

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

+ +

The value undefined

+ +

undefined is a type with exactly one value: undefined.

+ +

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

+ + + +

Some examples for when the value undefined is returned:

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

Handling changes to the value of undefined

+ +

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

+ +

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

+ +

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

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

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

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

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

+ +

Uses of null

+ +

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

+ +

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

Automatic semicolon insertion

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

+ +

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

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

Insertion happens, and the parser tries again.

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

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

+ +

How it works

+ +

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

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

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

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

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

+ +

Leading parenthesis

+ +

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

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

This code gets transformed into one line.

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

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

+ +

In conclusion

+ +

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

Другое

setTimeout and setInterval

Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the setTimeout and setInterval functions.

+ + + +
function foo() {}
+var id = setTimeout(foo, 1000); // returns a Number > 0
+
+ +

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

+ +

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

+ +

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

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

Stacking calls with setInterval

+ +

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

+ +

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

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

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

+ +

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

+ +

Dealing with possible blocking code

+ +

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

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

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

+ +

Manually clearing timeouts

+ +

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

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

Clearing all timeouts

+ +

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

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

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

+ +

Hidden use of eval

+ +

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

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

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

+ +

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

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

In conclusion

+ +

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

+ +

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

Copyright © 2011. Built with +Node.jsusing a +jadetemplate. +

\ No newline at end of file From 8961aea1f7d0f05e302bd67621ef5b35a0615154 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 15 Mar 2011 17:00:06 +0200 Subject: [PATCH 048/641] translation for intro block and removed site/ru directory --- .gitignore | 1 + doc/ru/intro/authors.md | 6 +- doc/ru/intro/contributors.md | 6 +- doc/ru/intro/index.md | 17 +- doc/ru/intro/license.md | 8 +- site/ru/index.html | 1859 ---------------------------------- 6 files changed, 15 insertions(+), 1882 deletions(-) delete mode 100644 site/ru/index.html diff --git a/.gitignore b/.gitignore index 3a5a5726..851febb8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /build /html /site/de +*.md~ diff --git a/doc/ru/intro/authors.md b/doc/ru/intro/authors.md index f88f94a7..431b2815 100644 --- a/doc/ru/intro/authors.md +++ b/doc/ru/intro/authors.md @@ -1,7 +1,7 @@ -## The Authors +## Авторы -This guide is the work of two lovely Stack Overflow users, [Ivo Wetzel][1] -(Writing) and [Zhang Yi Jiang][2] (Design). +Это руководство является результатом работы двух очаровательных пользователей 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/doc/ru/intro/contributors.md b/doc/ru/intro/contributors.md index 73fb98ea..2dc82f79 100644 --- a/doc/ru/intro/contributors.md +++ b/doc/ru/intro/contributors.md @@ -1,7 +1,7 @@ -## Contributors +## Участники - - [Caio Romão][1] (Spelling corrections) - - [Andreas Blixt][2] (Language corrections) + - [Кайо Ромао /Caio Romão/][1] (Грамматические правки) + - [Андреас Бликст /Andreas Blixt/][2] (Языковые правки) [1]: https://github.com/caio [2]: https://github.com/blixt diff --git a/doc/ru/intro/index.md b/doc/ru/intro/index.md index f2d5d31b..303a43ab 100644 --- a/doc/ru/intro/index.md +++ b/doc/ru/intro/index.md @@ -1,15 +1,8 @@ -## Intro - -**JavaScript Garden** is a growing collection of documentation about the most -quirky parts of the JavaScript programming language. It gives advice to -avoid common mistakes, subtle bugs, as well as performance issues and bad -practices that non-expert JavaScript programmers may encounter on their -endeavours into the depths of the language. - -JavaScript Garden does **not** aim to teach you JavaScript. Former knowledge -of the language is strongly recommended in order to understand the topics covered -in this guide. In order to learn the basics of the language, please head over to -the excellent [guide][1] on the Mozilla Developer Network. +## Вступление + +**JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам, касающимся языка JavaScript. В ней вы найдёте советы о том, как перестать употреблять частые ошибки и научиться предсказывать появление тех или иных багов. Также в документации подробно освещены проблемы оптимизации и плохие практики, с которыми JavaScript-программисты, не ставшие ещё экспертами, могут столкнуться продираясь в глубины языка. + +JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Опыт в работе с языком необходим чтобы понимать темы, рассматриваемые в этом руководстве. Если вам нужно изучить основы языка, пожалуйста внимательно ознакомьтесь с великолепным [руководством][1] на сайте Mozilla Developer Network. [1]: https://developer.mozilla.org/en/JavaScript/Guide diff --git a/doc/ru/intro/license.md b/doc/ru/intro/license.md index cd398699..8be53b30 100644 --- a/doc/ru/intro/license.md +++ b/doc/ru/intro/license.md @@ -1,9 +1,7 @@ -## 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. +JavaScript Гарден распространяется под [лицензией MIT license][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 diff --git a/site/ru/index.html b/site/ru/index.html deleted file mode 100644 index 0a9b6635..00000000 --- a/site/ru/index.html +++ /dev/null @@ -1,1859 +0,0 @@ -JavaScript Гарден -

Вступление

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

- -

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

The Authors

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

License

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

Объекты

Object Usage and Properties

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

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

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

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

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

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

Objects as a data type

- -

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

- -

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

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

Accessing properties

- -

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

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

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

- -

Deleting properties

- -

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

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

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

- -

Notation of keys

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

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

- -

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

The Prototype

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

- -

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

- -

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

- -

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

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

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

- - - -

Property lookup

- -

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

- -

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

- -

The prototype property

- -

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

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

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

- -

Performance

- -

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

- -

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

- -

Extension of native prototypes

- -

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

- -

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

- -

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

- -

In conclusion

- -

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

hasOwnProperty

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

- - - -

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

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

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

- -

hasOwnProperty as a property

- -

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

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

In conclusion

- -

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

The for in Loop

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

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

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

- - - -

Using hasOwnProperty for filtering

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

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

- -

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

- -

Best practices

- -

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

Функции

Function Declarations and Expressions

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

- -

The function declaration

- -
function foo() {}
-
- -

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

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

The function expression

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

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

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

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

- -

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

- -

Named function expression

- -

Another special case is the assignment of named functions.

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

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

How this Works

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

- -

The global scope

- -
this;
-
- -

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

- -

Calling a function

- -
foo();
-
- -

Here this will again refer to the global object.

- - - -

Calling a method

- -
test.foo(); 
-
- -

In this example this will refer to test.

- -

Calling a constructor

- -
new foo(); 
-
- -

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

- -

Explicit setting of this

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

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

- -

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

- - - -

Common pitfalls

- -

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

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

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

- -

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

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

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

- -

Assigning methods

- -

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

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

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

- -

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

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

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

Closures and References

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

- -

Emulating private variables

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

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

- -

Why private variables work

- -

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

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

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

- -

Closures inside loops

- -

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

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

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

- -

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

- -

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

- -

Avoiding the reference problem

- -

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

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

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

- -

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

- -

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

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

The arguments Object

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

- - - -

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

- -

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

- -

Converting to an array

- -

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

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

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

- -

Passing arguments

- -

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

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

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

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

Formal parameters and arguments indexes

- -

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

- -

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

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

Performance myths and truths

- -

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

- -

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

- - - -

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

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

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

- -

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

- -

Constructors

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

- -

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

- -

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

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

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

- -

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

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

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

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

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

- -

Factories

- -

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

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

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

- -

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

- -

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

- -

Creating new objects via factories

- -

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

- -

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

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

While the above is robust against a missing new keyword and certainly makes -the use of private variables easier, it comes with some -downsides.

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

In conclusion

- -

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

Scopes and Namespaces

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

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

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

- -

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

- -

The bane of global variables

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

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

- -

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

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

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

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

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

- -

Local variables

- -

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

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

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

- -

Hoisting

- -

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

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

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

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

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

- -

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

- -

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

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

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

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

Name resolution order

- -

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

- -

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

- -

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

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

Namespaces

- -

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

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

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

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

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

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

In conclusion

- -

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

- -

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

Массивы

Array Iteration and Properties

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

- - - -

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

- -

Iteration

- -

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

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

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

- -

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

- -

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

- -

The length property

- -

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

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

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

- -

In conclusion

- -

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

The Array Constructor

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

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

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

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

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

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

In conclusion

- -

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

Типы

Equality and comparisons

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

- -

The equals operator

- -

The equals operator consists of two equal signs: ==

- -

JavaScript features weak typing, that means, that the equals 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 equals operator

- -

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

- -

Other than the normal equals operator, the strict equals operator does not -perform type coercion between its operands.

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

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

- -

Comparing objects

- -

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

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

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

- -

In conclusion

- -

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

The typeof operator

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

- -

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

- - - -

The JavaScript type table

- -
Value               Class      Type
--------------------------------------
-"foo"               String     string
-new String("foo")   String     object
-1.2                 Number     number
-new Number(1.2)     Number     object
-true                Boolean    boolean
-new Boolean(true)   Boolean    object
-new Date()          Date       object
-new Error()         Error      object
-[1,2,3]             Array      object
-new Array(1, 2, 3)  Array      object
-new Function("")    Function   function
-/abc/g              RegExp     object (function in Nitro/V8)
-new RegExp("meow")  RegExp     object (function in Nitro/V8)
-{}                  Object     object
-new Object()        Object     object
-
- -

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

- -

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

- - - -

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

- -

The Class of an object

- -

The specification gives exactly one way of accessing the [[Class]] value, -with the use of Object.prototype.toString.

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

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

- - - -

Testing for undefined variables

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

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

- -

In conclusion

- -

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

- -

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

The instanceof operator

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

- -

Comparing custom objects

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

Using instanceof with native types

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

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

- -

In conclusion

- -

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

Type casting

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

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

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

- -

Constructors of built-in types

- -

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

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

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

- -

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

- -

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

- -

Casting to a string

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

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

- -

Casting to a number

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

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

- -

Casting to a boolean

- -

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

- -
!!'foo';   // true
-!!'';      // false
-!!'0';     // true
-!!'1';     // true
-!!'-1'     // true
-!!{};      // true
-!!true;    // true
-

Нативности

Why not to use eval

The eval function will execute a string of JavaScript code in the local scope.

- -
var foo = 1;
-function test() {
-    var foo = 2;
-    eval('foo = 3');
-    return foo;
-}
-test(); // 3
-foo; // 1
-
- -

But eval only executes in local scope when it is being called directly and -the name of the called function is actually eval.

- -
var foo = 1;
-function test() {
-    var foo = 2;
-    var bar = eval;
-    bar('foo = 3');
-    return foo;
-}
-test(); // 2
-foo; // 3
-
- -

The use of eval should be avoided at all costs. 99.9% of its "uses" can be -achieved without it.

- -

eval in disguise

- -

The timeout functions setTimeout and setInterval can both -take a string as their first argument. This string will always get executed -in the global scope since eval is not being called directly in that case.

- -

Security issues

- -

eval also is a security problem as it executes any code given to it, -it should never be used with strings of unknown or untrusted origins.

- -

In conclusion

- -

eval should never be used, any code that makes use of it is to be questioned in -its workings, performance and security. In case something requires eval in -order to work, its design is to be questioned and should not be used in the -first place, a better design should be used, that does not require the use of -eval.

undefined and null

JavaScript has two distinct values for nothing, the more useful of these two -being undefined.

- -

The value undefined

- -

undefined is a type with exactly one value: undefined.

- -

The language also defines a global variable that has the value of undefined, -this variable is also called undefined. But this variable is not a constant, -nor is it a keyword of the language. This means that its value can be easily -overwritten.

- - - -

Some examples for when the value undefined is returned:

- -
    -
  • Accessing the (unmodified) global variable undefined.
  • -
  • Implicit returns of functions due to missing return statements.
  • -
  • return statements which do not explicitly return anything.
  • -
  • Lookups of non-existent properties.
  • -
  • Function parameters which do not had any explicit value passed.
  • -
  • Anything that has been set to the value of undefined.
  • -
- -

Handling changes to the value of undefined

- -

Since the global variable undefined only holds a copy of the actual value of -undefined, assigning a new value to it does not change the value of the -type undefined.

- -

Still, in order to compare something against the value of undefined it is -necessary to retrieve the value of undefined first.

- -

In order to protect code against a possible overwritten undefined variable, a -common technique used is to add an additional parameter to an -anonymous wrapper, that gets no argument passed to it.

- -
var undefined = 123;
-(function(something, foo, undefined) {
-    // undefined in the local scope does 
-    // now again refer to the value
-
-})('Hello World', 42);
-
- -

Another way to achieve the same effect would be to use a declaration inside the -wrapper.

- -
var undefined = 123;
-(function(something, foo) {
-    var undefined;
-    ...
-
-})('Hello World', 42);
-
- -

The only difference being here, that this version results in 4 more bytes being -used in case it is minified and there is no other var statement inside the -anonymous wrapper.

- -

Uses of null

- -

While undefined in the context of the JavaScript language is mostly used in -the sense of a traditional null, the actual null (both a literal and a type) -is more or less just another data type.

- -

It is used in some JavaScript internals (like declaring the end of the -prototype chain by setting Foo.prototype = null), but in almost all cases it -can be replaced by undefined.

Automatic semicolon insertion

Although JavaScript has C style syntax, it does not enforce the use of -semicolons in the source code, it is possible to omit them.

- -

But JavaScript is not a semicolon-less language, it in fact needs the -semicolons in order to understand the source code. Therefore the JavaScript -parser automatically inserts them whenever it encounters a parse -error due to a missing semicolon.

- -
var foo = function() {
-} // parse error, semicolon expected
-test()
-
- -

Insertion happens, and the parser tries again.

- -
var foo = function() {
-}; // no error, parser continues
-test()
-
- -

The automatic insertion of semicolon is considered to be one of biggest -design flaws in the language, as it can change the behavior of code.

- -

How it works

- -

The code below has no semicolons in it, so it is up to the parser to decide where -to insert them.

- -
(function(window, undefined) {
-    function test(options) {
-        log('testing!')
-
-        (options.list || []).forEach(function(i) {
-
-        })
-
-        options.value.test(
-            'long string to pass here',
-            'and another long string to pass'
-        )
-
-        return
-        {
-            foo: function() {}
-        }
-    }
-    window.test = test
-
-})(window)
-
-(function(window) {
-    window.someLibrary = {}
-
-})(window)
-
- -

Below is the result of the parser's "guessing" game.

- -
(function(window, undefined) {
-    function test(options) {
-
-        // Not inserted, lines got merged
-        log('testing!')(options.list || []).forEach(function(i) {
-
-        }); // <- inserted
-
-        options.value.test(
-            'long string to pass here',
-            'and another long string to pass'
-        ); // <- inserted
-
-        return; // <- inserted, breaks the return statement
-        { // treated as a block
-
-            // a label and a single expression statement
-            foo: function() {} 
-        }; // <- inserted
-    }
-    window.test = test; // <- inserted
-
-// The lines got merged again
-})(window)(function(window) {
-    window.someLibrary = {}; // <- inserted
-
-})(window); //<- inserted
-
- - - -

The parser drastically changed the behavior of the code above, in certain cases -it does the wrong thing.

- -

Leading parenthesis

- -

In case of a leading parenthesis, the parser will not insert a semicolon.

- -
log('testing!')
-(options.list || []).forEach(function(i) {})
-
- -

This code gets transformed into one line.

- -
log('testing!')(options.list || []).forEach(function(i) {})
-
- -

Chances are very high that log does not return a function; therefore, -the above will yield a TypeError stating that undefined is not a function.

- -

In conclusion

- -

It is highly recommended to never omit semicolons, it is also advocated to -keep braces on the same line with their corresponding statements and to never omit -them for one single-line if / else statements. Both of these measures will -not only improve the consistency of the code, they will also prevent the -JavaScript parser from changing its behavior.

Другое

setTimeout and setInterval

Since JavaScript is asynchronous, it is possible to schedule the execution of a -function by using the setTimeout and setInterval functions.

- - - -
function foo() {}
-var id = setTimeout(foo, 1000); // returns a Number > 0
-
- -

When setTimeout gets called, it will return the ID of the timeout and schedule -foo to run in approximately one thousand milliseconds in the future. -foo will then get executed exactly once.

- -

Depending on the timer resolution of the JavaScript engine that is running the -code, as well as the fact that JavaScript is single threaded and other code that -gets executed might block the thread, it is by no means a safe bet that one -will get the exact delay that was specified in the setTimeout call.

- -

The function that was passed as the first parameter will get called by the -global object, that means, that this inside the called function -refers to that very object.

- -
function Foo() {
-    this.value = 42;
-    this.method = function() {
-        // this refers to the global object
-        console.log(this.value); // will log undefined
-    };
-    setTimeout(this.method, 500);
-}
-new Foo();
-
- - - -

Stacking calls with setInterval

- -

While setTimeout only runs the function once, setInterval - as the name -suggests - will execute the function every X milliseconds. But its use is -discouraged.

- -

When code that is being executed blocks the timeout call, setInterval will -still issue more calls to the specified function. This can, especially with small -intervals, result in function calls stacking up.

- -
function foo(){
-    // something that blocks for 1 second
-}
-setInterval(foo, 100);
-
- -

In the above code foo will get called once and will then block for one second.

- -

While foo blocks the code setInterval will still schedule further calls to -it. Now, when foo has finished, there will already be ten further calls to -it waiting for execution.

- -

Dealing with possible blocking code

- -

The easiest as well as most controllable solution, is to use setTimeout within -the function itself.

- -
function foo(){
-    // something that blocks for 1 second
-    setTimeout(foo, 100);
-}
-foo();
-
- -

Not only does this encapsulate the setTimeout call, but it also prevents the -stacking of calls and it gives additional control.foo itself can now decide -whether it wants to run again or not.

- -

Manually clearing timeouts

- -

Clearing timeouts and intervals works by passing the respective ID to -clearTimeout or clearInterval, depending which set function was used in -the first place.

- -
var id = setTimeout(foo, 1000);
-clearTimeout(id);
-
- -

Clearing all timeouts

- -

As there is no built-in method for clearing all timeouts and/or intervals, -it is necessary to use brute force in order to achieve this functionality.

- -
// clear "all" timeouts
-for(var i = 1; i < 1000; i++) {
-    clearTimeout(i);
-}
-
- -

There might still be timeouts that are unaffected by this arbitrary number; -therefore, is is instead recommended to keep track of all the timeout IDs, so -they can be cleared specifically.

- -

Hidden use of eval

- -

setTimeout and setInterval can also take a string as their first parameter. -This feature should never be used, since it internally makes use of eval.

- - - -
function foo() {
-    // will get called
-}
-
-function bar() {
-    function foo() {
-        // never gets called
-    }
-    setTimeout('foo()', 1000);
-}
-bar();
-
- -

Since eval is not getting called directly in this case, the string -passed to setTimeout will get executed in the global scope; thus, it will -not use the local variable foo from the scope of bar.

- -

It is further recommended to not use a string for passing arguments to the -function that will get called by either of the timeout functions.

- -
function foo(a, b, c) {}
-
-// NEVER use this
-setTimeout('foo(1,2, 3)', 1000)
-
-// Instead use an anonymous function
-setTimeout(function() {
-    foo(a, b, c);
-}, 1000)
-
- - - -

In conclusion

- -

Never should a string be used as the parameter of setTimeout or -setInterval. It is a clear sign of really bad code, when arguments need -to be supplied to the function that gets called. An anonymous function should -be passed that then takes care of the actual call.

- -

Further, the use of setInterval should be avoided since its scheduler is not -blocked by executing JavaScript.

Copyright © 2011. Built with -Node.jsusing a -jadetemplate. -

\ No newline at end of file From 846cacbec1bbc639e5069c90617a59dc8fc85dd7 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 15 Mar 2011 17:00:35 +0200 Subject: [PATCH 049/641] modified gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 851febb8..80f4f3b3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /build /html /site/de +/site/ru *.md~ From 0cba3c4bbcea7bb8ec17133a96b8d1584aec34f3 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 15 Mar 2011 17:09:38 +0200 Subject: [PATCH 050/641] some fixes --- doc/ru/intro/index.md | 2 +- doc/ru/intro/license.md | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/ru/intro/index.md b/doc/ru/intro/index.md index 303a43ab..38020106 100644 --- a/doc/ru/intro/index.md +++ b/doc/ru/intro/index.md @@ -1,6 +1,6 @@ ## Вступление -**JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам, касающимся языка JavaScript. В ней вы найдёте советы о том, как перестать употреблять частые ошибки и научиться предсказывать появление тех или иных багов. Также в документации подробно освещены проблемы оптимизации и плохие практики, с которыми JavaScript-программисты, не ставшие ещё экспертами, могут столкнуться продираясь в глубины языка. +**JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам, касающимся языка JavaScript. В ней вы найдёте советы о том, как перестать употреблять частые ошибки и как предсказывать появление тех или иных багов. Также в документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми могут столкнуться, продираясь к глубинам языка, не ставшие ещё экспертами JavaScript-программисты. JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Опыт в работе с языком необходим чтобы понимать темы, рассматриваемые в этом руководстве. Если вам нужно изучить основы языка, пожалуйста внимательно ознакомьтесь с великолепным [руководством][1] на сайте Mozilla Developer Network. diff --git a/doc/ru/intro/license.md b/doc/ru/intro/license.md index 8be53b30..82df413d 100644 --- a/doc/ru/intro/license.md +++ b/doc/ru/intro/license.md @@ -1,7 +1,6 @@ ## Лицензия -JavaScript Гарден распространяется под [лицензией MIT license][1] и распологается на [GitHub][2]. Если вы найдёте ошибки или опечатки, пожалуйста [сообщите об этом][3] или запросите права на загрузку репозитория. Кроме того, вы можете найти нас в [комнате JavaScript][4], среди чатов -Stack Overflow. +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 From d496454bd62442370ea71b1fde52c9a11711dabe Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 15 Mar 2011 22:45:48 +0200 Subject: [PATCH 051/641] object/general --- doc/ru/intro/authors.md | 3 +- doc/ru/intro/contributors.md | 4 +- doc/ru/intro/index.md | 4 +- doc/ru/object/general.md | 79 ++++++++++++++---------------------- doc/ru/object/prototype.md | 54 ++++++++++++------------ 5 files changed, 63 insertions(+), 81 deletions(-) diff --git a/doc/ru/intro/authors.md b/doc/ru/intro/authors.md index 431b2815..c6a454ec 100644 --- a/doc/ru/intro/authors.md +++ b/doc/ru/intro/authors.md @@ -1,7 +1,6 @@ ## Авторы -Это руководство является результатом работы двух очаровательных пользователей Stack Overflow: [Иво Ветцеля /Ivo Wetzel/][1] -(Автор текста) и [Чжан И Цзян /Zhang Yi Jiang/][2] (Дизайн). +Это руководство является результатом работы двух очаровательных пользователей 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/doc/ru/intro/contributors.md b/doc/ru/intro/contributors.md index 2dc82f79..594acc12 100644 --- a/doc/ru/intro/contributors.md +++ b/doc/ru/intro/contributors.md @@ -1,7 +1,7 @@ ## Участники - - [Кайо Ромао /Caio Romão/][1] (Грамматические правки) - - [Андреас Бликст /Andreas Blixt/][2] (Языковые правки) + - [Кайо Ромао /Caio Romão/][1] (корректировка орфографии) + - [Андреас Бликст /Andreas Blixt/][2] (корректировка грамматики) [1]: https://github.com/caio [2]: https://github.com/blixt diff --git a/doc/ru/intro/index.md b/doc/ru/intro/index.md index 38020106..b66302fa 100644 --- a/doc/ru/intro/index.md +++ b/doc/ru/intro/index.md @@ -1,8 +1,8 @@ ## Вступление -**JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам, касающимся языка JavaScript. В ней вы найдёте советы о том, как перестать употреблять частые ошибки и как предсказывать появление тех или иных багов. Также в документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми могут столкнуться, продираясь к глубинам языка, не ставшие ещё экспертами JavaScript-программисты. +**JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам, касающимся языка JavaScript. В ней вы найдёте советы о том, как перестать употреблять распространённые ошибки и как предсказывать появление тех или иных багов. Также в документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми могут столкнуться, продираясь к глубинам языка, не ставшие ещё экспертами JavaScript-программисты. -JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Опыт в работе с языком необходим чтобы понимать темы, рассматриваемые в этом руководстве. Если вам нужно изучить основы языка, пожалуйста внимательно ознакомьтесь с великолепным [руководством][1] на сайте Mozilla Developer Network. +JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Опыт в работе с языком необходим чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста внимательно ознакомьтесь с великолепным [руководством][1] на сайте Mozilla Developer Network. [1]: https://developer.mozilla.org/en/JavaScript/Guide diff --git a/doc/ru/object/general.md b/doc/ru/object/general.md index b4fc816b..b00db18b 100644 --- a/doc/ru/object/general.md +++ b/doc/ru/object/general.md @@ -1,66 +1,54 @@ -## Object Usage and Properties +## Свойства и использование объектов -Everything in JavaScript acts like an object, with the only two exceptions being -[`null`](#core.undefined) and [`undefined`](#core.undefined). +В JavaScript всё является объектом, лишь за двумя исключениями — [`null`](#core.undefined) и [`undefined`](#core.undefined). false.toString() // 'false' [1, 2, 3].toString(); // '1,2,3' - + function Foo(){} Foo.bar = 1; Foo.bar; // 1 -A common misconception is that number literals cannot be used as -objects. That is because a flaw in JavaScript's parser tries to parse the *dot -notation* on a number as a floating point literal. +Неверно считать, что числовые литералы нельзя использовать в качестве объектов — это распространённое заблуждение. Его причиной является упущение в парсере JavaScript, благодаря которому он пытается распознать применение *точечной нотации* над числом как литерал с плавающей точкой. + + 2.toString(); // вызывает SyntaxError - 2.toString(); // raises SyntaxError +Есть пара способов обойти этот недостаток и все их можно использовать для работы с числами как с объектами: -There are a couple of workarounds which can be used in order make number -literals act as objects too. + 2..toString(); // вторая точка распознаётся корректно + 2 .toString(); // обратите внимание на пробел перед точкой + (2).toString(); // двойка вычисляется заранее - 2..toString(); // the second point is correctly recognized - 2 .toString(); // note the space left to the dot - (2).toString(); // 2 is evaluated first +### Объекты как тип данных -### Objects as a data type +Объекты в JavaScript могут использоваться как [*хеш-таблицы*][1]: большей частью они состоят из именованных свойств (ключей) привязанных к значениям. -Objects in JavaScript can also be used as a [*Hashmap*][1], they mainly consist -of named properties mapping to values. +Используя объектный литерал — нотацию `{}` — можно создать простой объект. Новый объект [наследуется](#object.prototype) от `Object.prototype` и не имеет [собственных свойств](#object.hasownproperty). -Using a object literal - `{}` notation - it is possible to create a -plain object. This new object [inherits](#object.prototype) from `Object.prototype` and -has no [own properties](#object.hasownproperty) defined on it. + var foo = {}; // новый пустой объект - var foo = {}; // a new empty object + // новый объект со свойством по имени 'test' имеющим значение 12 + var bar = {test: 12}; - // a new object with a property called 'test' with value 12 - var bar = {test: 12}; +### Доступ к свойствам -### Accessing properties +Получить доступ к свойствам объекта можно двумя способами: используя либо точечную нотацию, либо запись с квадратными скобками. -The properties of an object can be accessed in two ways, via either the dot -notation, or the square bracket notation. - var foo = {name: 'Kitten'} foo.name; // kitten foo['name']; // kitten - + var get = 'name'; foo[get]; // kitten - + foo.1234; // SyntaxError - foo['1234']; // works + foo['1234']; // работает -Both notations are identical in their workings, with the only difference being that -the square bracket notation allows for dynamic setting of properties, as well as -the use of property names that would otherwise lead to a syntax error. +Обе нотации идентичны по принципу работы — одна лишь разница в том, что использование квадратных скобок позволяет устанавливать свойства динамически и использовать такие имена свойств, какие в других случаях могли бы привести к синтаксической ошибке. -### Deleting properties +### Удаление свойств -The only way to actually remove a property from an object is to use the `delete` -operator; setting the property to `undefined` or `null` only remove the -*value* associated with the property, but not the *key*. +Единственный способ удалить свойство у объекта — использовать оператор `delete`; устанавливая свойство в `undefined` или `null`, вы удаляете только связанное с ним *значение*, но не *ключ*. var obj = { bar: 1, @@ -77,23 +65,18 @@ operator; setting the property to `undefined` or `null` only remove the } } -The above outputs both `bar undefined` and `foo null` - only `baz` was -removed and is therefore missing from the output. +Приведённый код выведет и `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 too so me' // raises SyntaxError + 'case': 'Я — ключевое слово, поэтому меня надо записывать строкой', + delete: 'Я тоже ключевое слово, так что я' // бросаю 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. +Свойства объектов могут записываться и напрямую символами, и как строки. В соответствии с другим упущением в парсере JavaScript, этот код выбросит `SyntaxError` во всех версиях ранее 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. +Источником ошибки является факт, что `delete` — это *ключевое слово* и поэтому его необходимо записывать как *строчный литерал* для уверенности в том, что оно будет корректно опознано более старыми движками JavaScript. -[1]: http://en.wikipedia.org/wiki/Hashmap +[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 diff --git a/doc/ru/object/prototype.md b/doc/ru/object/prototype.md index 55ca096e..e04f8358 100644 --- a/doc/ru/object/prototype.md +++ b/doc/ru/object/prototype.md @@ -1,23 +1,23 @@ -## The Prototype +## Великий Прототип -JavaScript does not feature a classical inheritance model, instead it uses a -*prototypal* one. +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. +While this is often considered to be one of JavaScript's weaknesses, the +prototypal inheritance model is in fact more powerful than the classic model. It is for example fairly trivial to build a classic model on top of it, while the other way around is a far more difficult task. Due to the fact that JavaScript is basically the only widely used language that -features prototypal inheritance, it takes some time to adjust to the -differences between the two models. +features prototypal inheritance, it takes some time to adjust to the +differences between the two models. The first major difference is that inheritance in JavaScript is done by using so called *prototype chains*. - + > **Note:** Simply using `Bar.prototype = Foo.prototype` will result in both objects -> sharing the **same** prototype. Therefore, changes to either object's prototype -> will affect the prototype of the other as well, which in most cases is not the +> 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() { @@ -40,7 +40,7 @@ called *prototype chains*. // The resulting prototype chain test [instance of Bar] - Bar.prototype [instance of Foo] + Bar.prototype [instance of Foo] { foo: 'Hello World' } Foo.prototype { method: ... } @@ -48,14 +48,14 @@ called *prototype chains*. { toString: ... /* etc. */ } In the above, the object `test` will inherit from both `Bar.prototype` and -`Foo.prototype`; hence, it will have access to the function `method` that was +`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 +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 +> **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. @@ -71,7 +71,7 @@ hasn't found the specified property, it will return the value ### The prototype property While the prototype property is used by the language to build the prototype -chains, it is still possible to assign **any** given value to it. Although +chains, it is still possible to assign **any** given value to it. Although primitives will simply get ignored when assigned as a prototype. function Foo() {} @@ -83,10 +83,10 @@ creation of prototype chains. ### Performance The lookup time for properties that are high up on the prototype chain can have a -negative impact on performance critical sections of code. Additionally, trying to -access non-existent properties will always traverse the full prototype chain. +negative impact on performance critical sections of code. Additionally, trying to +access non-existent properties will always traverse the full prototype chain. -Also, when [iterating](#object.forinloop) over the properties of an object +Also, when [iterating](#object.forinloop) over the properties of an object **every** property that is on the prototype chain will get enumerated. ### Extension of native prototypes @@ -94,20 +94,20 @@ Also, when [iterating](#object.forinloop) over the properties of an object One mis-feature that is often used is to extend `Object.prototype` or one of the other built in prototypes. -This technique is called [monkey patching][1] and breaks *encapsulation*. While -used by widely spread frameworks such as [Prototype][2], there is still no good +This technique is called [monkey patching][1] and breaks *encapsulation*. While +used by widely spread frameworks such as [Prototype][2], there is still no good reason for cluttering built-in types with additional *non-standard* functionality. -The **only** good reason for extending a built-in prototype is to backport -the features of newer JavaScript engines; for example, +The **only** good reason for extending a built-in prototype is to backport +the features of newer JavaScript engines; for example, [`Array.forEach`][3]. ### In conclusion -It is a **must** to understand the prototypal inheritance model completely -before writing complex code which makes use of it. Also, watch the length of -the prototype chains and break them up if necessary to avoid possible -performance issues. Further, the native prototypes should **never** be extended +It is a **must** to understand the prototypal inheritance model completely +before writing complex code which makes use of it. Also, watch the length of +the prototype chains and break them up if necessary to avoid possible +performance issues. Further, the native prototypes should **never** be extended unless it is for the sake of compatibility with newer JavaScript features. [1]: http://en.wikipedia.org/wiki/Monkey_patch From 33f794c6b060118a8c7f2e4942a86e64a62bf5d5 Mon Sep 17 00:00:00 2001 From: Tiago Fassoni Leite Date: Tue, 15 Mar 2011 18:27:45 -0300 Subject: [PATCH 052/641] Fixed minor typo --- doc/en/object/forinloop.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/object/forinloop.md b/doc/en/object/forinloop.md index 6150e259..88411e97 100644 --- a/doc/en/object/forinloop.md +++ b/doc/en/object/forinloop.md @@ -32,7 +32,7 @@ this is done by using the [`hasOwnProperty`](#object.hasownproperty) method of } } -This version is the only correct one to use. Due to the use of `hasOwnPropery` it +This version is the only correct one to use. Due to the use of `hasOwnProperty` it will **only** print out `moo`. When `hasOwnProperty` is left out, the code is prone to errors in cases where the native prototypes - e.g. `Object.prototype` - have been extended. From c9e3df32127b663bcbacb9b8cf1eda2d4aa791e5 Mon Sep 17 00:00:00 2001 From: Tiago Fassoni Leite Date: Tue, 15 Mar 2011 18:54:06 -0300 Subject: [PATCH 053/641] More typo hunting, some grammatical changes --- doc/en/function/general.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/function/general.md b/doc/en/function/general.md index 5bfd6cd8..5940d5d2 100644 --- a/doc/en/function/general.md +++ b/doc/en/function/general.md @@ -1,6 +1,6 @@ ## Function Declarations and Expressions -Functions in JavaScript are first class objects, that means that they can be +Functions in JavaScript are first class objects. That means they can be passed around like any other value. One common use of this feature is to pass an *anonymous function* as a callback to another, possibly asynchronous function. @@ -29,7 +29,7 @@ Due to the fact that `var` is a declaration, that hoists the variable name `foo` before the actual execution of the code starts, `foo` is already defined when the script gets executed. -But since assignments only happens at runtime, the value of `foo` will default +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 From bf6054829f2291ae7ee8b7a278489c17253a7fa6 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 15 Mar 2011 23:55:55 +0200 Subject: [PATCH 054/641] object/prototype first part translation --- doc/ru/object/general.md | 16 +++++------ doc/ru/object/prototype.md | 55 +++++++++++++------------------------- 2 files changed, 27 insertions(+), 44 deletions(-) diff --git a/doc/ru/object/general.md b/doc/ru/object/general.md index b00db18b..1827184f 100644 --- a/doc/ru/object/general.md +++ b/doc/ru/object/general.md @@ -1,4 +1,4 @@ -## Свойства и использование объектов +## Использование объектов и их свойств В JavaScript всё является объектом, лишь за двумя исключениями — [`null`](#core.undefined) и [`undefined`](#core.undefined). @@ -9,11 +9,11 @@ Foo.bar = 1; Foo.bar; // 1 -Неверно считать, что числовые литералы нельзя использовать в качестве объектов — это распространённое заблуждение. Его причиной является упущение в парсере JavaScript, благодаря которому он пытается распознать применение *точечной нотации* над числом как литерал с плавающей точкой. +Неверно считать, что числовые литералы нельзя использовать в качестве объектов — это распространённое заблуждение. Его причиной является упущение в парсере JavaScript, благодаря которому применение *точечной нотации* над числом он считает литералом с плавающей точкой. 2.toString(); // вызывает SyntaxError -Есть пара способов обойти этот недостаток и все их можно использовать для работы с числами как с объектами: +Есть несколько способов обойти этот недостаток и все их можно использовать для работы с числами как с объектами: 2..toString(); // вторая точка распознаётся корректно 2 .toString(); // обратите внимание на пробел перед точкой @@ -21,13 +21,13 @@ ### Объекты как тип данных -Объекты в JavaScript могут использоваться как [*хеш-таблицы*][1]: большей частью они состоят из именованных свойств (ключей) привязанных к значениям. +Объекты в JavaScript могут использоваться как [*хеш-таблицы*][1]: в основном они состоят из именованных свойств (ключей) привязанных к значениям. Используя объектный литерал — нотацию `{}` — можно создать простой объект. Новый объект [наследуется](#object.prototype) от `Object.prototype` и не имеет [собственных свойств](#object.hasownproperty). var foo = {}; // новый пустой объект - // новый объект со свойством по имени 'test' имеющим значение 12 + // новый объект со свойством с именем 'test', имеющим значение 12 var bar = {test: 12}; ### Доступ к свойствам @@ -65,7 +65,7 @@ } } -Приведённый код выведет и `bar undefined` и `foo null` - было удалено только свойство `baz` и поэтому оно будет отсутствовать в выводе. +Приведённый код выведет и `bar undefined` и `foo null` — удалено было только свойство `baz` и поэтому только оно будет отсутствовать в выводе. ### Запись ключей @@ -74,9 +74,9 @@ delete: 'Я тоже ключевое слово, так что я' // бросаю SyntaxError }; -Свойства объектов могут записываться и напрямую символами, и как строки. В соответствии с другим упущением в парсере JavaScript, этот код выбросит `SyntaxError` во всех версиях ранее ECMAScript 5. +Свойства объектов могут записываться и напрямую символами и в виде строк. В соответствии с другим упущением в парсере JavaScript, этот код выбросит `SyntaxError` во всех версиях ранее ECMAScript 5. -Источником ошибки является факт, что `delete` — это *ключевое слово* и поэтому его необходимо записывать как *строчный литерал* для уверенности в том, что оно будет корректно опознано более старыми движками JavaScript. +Источником ошибки является факт, что `delete` — это *ключевое слово* и поэтому его необходимо записывать как *строчный литерал* ради уверенности в том, что оно будет корректно опознано более старыми движками JavaScript. [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 diff --git a/doc/ru/object/prototype.md b/doc/ru/object/prototype.md index e04f8358..fa12a1a6 100644 --- a/doc/ru/object/prototype.md +++ b/doc/ru/object/prototype.md @@ -1,24 +1,14 @@ ## Великий Прототип -JavaScript does not feature a classical inheritance model, instead it uses a -*prototypal* one. +В JavaScript отсутствует классическая модель наследования — вместо неё использользуется [*прототипная модель*][1]. -While this is often considered to be one of JavaScript's weaknesses, the -prototypal inheritance model is in fact more powerful than the classic model. -It is for example fairly trivial to build a classic model on top of it, while the -other way around is a far more difficult task. +Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё предельно легко построить классическую модель, при этом воспроизведение других видов окажется более трудной задачей. -Due to the fact that JavaScript is basically the only widely used language that -features prototypal inheritance, it takes some time to adjust to the -differences between the two models. +Из-за того, что JavaScript — практически единственный широко используемый язык с прототипным наследованием, приходится потратить некоторое время на осознание различий между этими двумя моделями. -The first major difference is that inheritance in JavaScript is done by using so -called *prototype chains*. +Первое важное отличие заключается в том, что наследование в JavaScript производится с использованием так называемых *цепочек прототипов*. -> **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. +> **Замечание:** В результате выполнения конструкции `Bar.prototype = Foo.prototype` оба объекта будут делить друг с другом **один и тот же** прототип. Так что изменение прототипа одного из объектов повлечёт за собой изменение прототипа другого и наоборот — вряд ли это окажется тем, чего вы ожидали. function Foo() { this.value = 42; @@ -29,35 +19,27 @@ called *prototype chains*. function Bar() {} - // Set Bar's prototype to a new instance of Foo + // Установим значением прототипа Bar новый экземпляр Foo Bar.prototype = new Foo(); Bar.prototype.foo = 'Hello World'; - // Make sure to list Bar as the actual constructor + // Убедимся, что Bar является действующим конструктором Bar.prototype.constructor = Bar; - var test = new Bar() // create a new bar instance + var test = new Bar() // создадим новый экземпляр bar - // The resulting prototype chain + // Цепочка прототипов, которая получится в результате test [instance of Bar] Bar.prototype [instance of Foo] { foo: 'Hello World' } Foo.prototype { method: ... } Object.prototype - { toString: ... /* etc. */ } + { toString: ... /* и т.д. */ } -In the above, the object `test` will inherit from both `Bar.prototype` and -`Foo.prototype`; hence, it will have access to the function `method` that was -defined on `Foo`. It will also have access to the property `value` of the -**one** `Foo` instance that is its prototype. It is important to note that `new -Bar()` does **not** create a new `Foo` instance, but reuses the one assigned to -its prototype; thus, all `Bar` instances will share the **same** `value` property. +В приведённом коде объект `test` наследует оба прототипа: `Bar.prototype` и `Foo.prototype`; следовательно, он имеет доступ к функции `method` которую мы определили в прототипе `Foo`. Также у него есть доступ к свойству `value` **одного уникального** экземпляра `Foo`, который является его протипом. Важно заметить, что код `new Bar()` **не** создаёт новый экземпляр `Foo`, а переиспользует тот, который был назначен его прототипом: таким образом все новые экземпляры `Bar` будут иметь **одинаковое** свойство `value`. -> **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. +> **Замечание:** Никогда **не** используйте конструкцию `Bar.prototype = Foo`, поскольку ссылка будет указывать не на прототип `Foo`, а на объект функции `Foo`. Из-за этого цепочка прототипов будет проходить через `Function.prototype`, а не через `Foo.prototype` и в результате функция `method` не будет содержаться в цепочке прототипов. ### Property lookup @@ -94,13 +76,13 @@ Also, when [iterating](#object.forinloop) over the properties of an object One mis-feature that is often used is to extend `Object.prototype` or one of the other built in prototypes. -This technique is called [monkey patching][1] and breaks *encapsulation*. While -used by widely spread frameworks such as [Prototype][2], there is still no good +This technique is called [monkey patching][2] and breaks *encapsulation*. While +used by widely spread frameworks such as [Prototype][3], 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]. +[`Array.forEach`][4]. ### In conclusion @@ -110,7 +92,8 @@ the prototype chains and break them up if necessary to avoid possible performance issues. Further, the native prototypes should **never** be extended unless it is for the sake of compatibility with newer JavaScript features. -[1]: http://en.wikipedia.org/wiki/Monkey_patch -[2]: http://prototypejs.org/ -[3]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach +[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 From bd4587aa5aae56e8eb4b178d5a8db714f54eb661 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Wed, 16 Mar 2011 18:07:24 +0200 Subject: [PATCH 055/641] some fixes I've made thanks to alternative translation from AntonShevchuk & Co. --- doc/ru/intro/contributors.md | 4 ++-- doc/ru/intro/index.md | 4 ++-- doc/ru/object/general.md | 8 ++++---- doc/ru/object/prototype.md | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/ru/intro/contributors.md b/doc/ru/intro/contributors.md index 594acc12..41afeaf6 100644 --- a/doc/ru/intro/contributors.md +++ b/doc/ru/intro/contributors.md @@ -1,7 +1,7 @@ ## Участники - - [Кайо Ромао /Caio Romão/][1] (корректировка орфографии) - - [Андреас Бликст /Andreas Blixt/][2] (корректировка грамматики) + - [Кайо Ромао /Caio Romão/][1] (правка орфографии) + - [Андреас Бликст /Andreas Blixt/][2] (правка грамматики) [1]: https://github.com/caio [2]: https://github.com/blixt diff --git a/doc/ru/intro/index.md b/doc/ru/intro/index.md index b66302fa..8ab31b12 100644 --- a/doc/ru/intro/index.md +++ b/doc/ru/intro/index.md @@ -1,8 +1,8 @@ ## Вступление -**JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам, касающимся языка JavaScript. В ней вы найдёте советы о том, как перестать употреблять распространённые ошибки и как предсказывать появление тех или иных багов. Также в документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми могут столкнуться, продираясь к глубинам языка, не ставшие ещё экспертами JavaScript-программисты. +**JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам языка JavaScript. В ней вы найдёте советы о том, как избежать распространённых ошибок и предсказать появление тех или иных багов. Также в документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми могут столкнуться, продираясь к глубинам языка, не ставшие ещё экспертами JavaScript-программисты. -JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Опыт в работе с языком необходим чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста внимательно ознакомьтесь с великолепным [руководством][1] на сайте Mozilla Developer Network. +JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Вам потребуется реальный опыт в работе с языком чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста внимательно ознакомьтесь с замечательным [руководством][1] на сайте Mozilla Developer Network. [1]: https://developer.mozilla.org/en/JavaScript/Guide diff --git a/doc/ru/object/general.md b/doc/ru/object/general.md index 1827184f..d529c48c 100644 --- a/doc/ru/object/general.md +++ b/doc/ru/object/general.md @@ -9,11 +9,11 @@ Foo.bar = 1; Foo.bar; // 1 -Неверно считать, что числовые литералы нельзя использовать в качестве объектов — это распространённое заблуждение. Его причиной является упущение в парсере JavaScript, благодаря которому применение *точечной нотации* над числом он считает литералом с плавающей точкой. +Неверно считать, что числовые литералы нельзя использовать в качестве объектов — это распространённое заблуждение. Его причиной является упущение в парсере JavaScript, благодаря которому применение *точечной нотации* к числу воспринимается им как литерал числа с плавающей точкой. 2.toString(); // вызывает SyntaxError -Есть несколько способов обойти этот недостаток и все их можно использовать для работы с числами как с объектами: +Есть несколько способов обойти этот недостаток и любой из них можно использовать для того, чтобы работать с числами, как с объектами: 2..toString(); // вторая точка распознаётся корректно 2 .toString(); // обратите внимание на пробел перед точкой @@ -32,7 +32,7 @@ ### Доступ к свойствам -Получить доступ к свойствам объекта можно двумя способами: используя либо точечную нотацию, либо запись с квадратными скобками. +Получить доступ к свойствам объекта можно двумя способами: используя либо точечную нотацию, либо запись квадратными скобками. var foo = {name: 'Kitten'} foo.name; // kitten @@ -65,7 +65,7 @@ } } -Приведённый код выведет и `bar undefined` и `foo null` — удалено было только свойство `baz` и поэтому только оно будет отсутствовать в выводе. +Приведённый код выведет две строки: `bar undefined` и `foo null` — на самом деле удалено было только свойство `baz` и посему только оно будет отсутствовать в выводе. ### Запись ключей diff --git a/doc/ru/object/prototype.md b/doc/ru/object/prototype.md index fa12a1a6..34a13027 100644 --- a/doc/ru/object/prototype.md +++ b/doc/ru/object/prototype.md @@ -2,9 +2,9 @@ В JavaScript отсутствует классическая модель наследования — вместо неё использользуется [*прототипная модель*][1]. -Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё предельно легко построить классическую модель, при этом воспроизведение других видов окажется более трудной задачей. +Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё предельно легко реализовать классическое наследование, а наоборот — тут уж придется сильно потрудиться. -Из-за того, что JavaScript — практически единственный широко используемый язык с прототипным наследованием, приходится потратить некоторое время на осознание различий между этими двумя моделями. +Из-за того, что JavaScript — практически единственный широко используемый язык с прототипным наследованием, придётся потратить некоторое время на осознание различий между этими двумя моделями. Первое важное отличие заключается в том, что наследование в JavaScript производится с использованием так называемых *цепочек прототипов*. From ce02e3846ee370c3dae05a049a2d3b7d14a010c5 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Wed, 16 Mar 2011 20:58:05 +0200 Subject: [PATCH 056/641] injected and adapted object/prototype chapter from AntonShevchuk &Co. translation --- doc/ru/intro/authors.md | 2 +- doc/ru/intro/contributors.md | 15 ++++++++-- doc/ru/intro/index.md | 4 +-- doc/ru/intro/license.md | 2 +- doc/ru/object/general.md | 20 ++++++++++++-- doc/ru/object/prototype.md | 53 +++++++++++++++--------------------- 6 files changed, 56 insertions(+), 40 deletions(-) diff --git a/doc/ru/intro/authors.md b/doc/ru/intro/authors.md index c6a454ec..ac04aea0 100644 --- a/doc/ru/intro/authors.md +++ b/doc/ru/intro/authors.md @@ -1,6 +1,6 @@ ## Авторы -Это руководство является результатом работы двух очаровательных пользователей Stack Overflow: [Иво Ветцель /Ivo Wetzel/][1] (автор текста) и [Чжан И Цзян /Zhang Yi Jiang/][2] (дизайн). +Это руководство является результатом работы двух очаровательных пользователей 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/doc/ru/intro/contributors.md b/doc/ru/intro/contributors.md index 41afeaf6..4f9f35ef 100644 --- a/doc/ru/intro/contributors.md +++ b/doc/ru/intro/contributors.md @@ -1,8 +1,19 @@ ## Участники - - [Кайо Ромао /Caio Romão/][1] (правка орфографии) - - [Андреас Бликст /Andreas Blixt/][2] (правка грамматики) + - [Кайо Ромао /Caio Romão/][1] (проверка орфографии) + - [Андреас Бликст /Andreas Blixt/][2] (проверка грамматики) + +## Перевод на русский + + - [Ulric 'shaman.sir' Wilfred][3] + - [Антон Шевчук][4] + - [Максим Лозовой][5] + - [Елена Пашкова][6] [1]: https://github.com/caio [2]: https://github.com/blixt +[3]: http://shamansir.madfire.net/ +[4]: http://anton.shevchuk.name/ +[5]: http://nixsolutions.com/ +[6]: http://nixsolutions.com/ diff --git a/doc/ru/intro/index.md b/doc/ru/intro/index.md index 8ab31b12..8b47ee95 100644 --- a/doc/ru/intro/index.md +++ b/doc/ru/intro/index.md @@ -1,8 +1,8 @@ ## Вступление -**JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам языка JavaScript. В ней вы найдёте советы о том, как избежать распространённых ошибок и предсказать появление тех или иных багов. Также в документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми могут столкнуться, продираясь к глубинам языка, не ставшие ещё экспертами JavaScript-программисты. +**JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам языка JavaScript. В ней вы найдёте советы о том, как избежать распространённых ошибок и предсказать появление тех или иных багов. В документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми, продираясь к глубинам языка, могут столкнуться даже просвещённые JavaScript-программисты. -JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Вам потребуется реальный опыт в работе с языком чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста внимательно ознакомьтесь с замечательным [руководством][1] на сайте Mozilla Developer Network. +JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Вам потребуется реальный опыт работы с языком чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста внимательно ознакомьтесь с замечательным [руководством][1] на сайте Mozilla Developer Network. [1]: https://developer.mozilla.org/en/JavaScript/Guide diff --git a/doc/ru/intro/license.md b/doc/ru/intro/license.md index 82df413d..5cdd8256 100644 --- a/doc/ru/intro/license.md +++ b/doc/ru/intro/license.md @@ -1,6 +1,6 @@ ## Лицензия -JavaScript Гарден распространяется под [лицензией MIT][1] и распологается на [GitHub][2]. Если вы найдёте ошибку или опечатку, пожалуйста [сообщите нам о ней][3] или запросите права на загрузку репозитория. Кроме того, вы можете найти нас в [комнате JavaScript][4] среди чатов Stack Overflow. +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 diff --git a/doc/ru/object/general.md b/doc/ru/object/general.md index d529c48c..e976da27 100644 --- a/doc/ru/object/general.md +++ b/doc/ru/object/general.md @@ -1,4 +1,4 @@ -## Использование объектов и их свойств +## Объекты и их свойства В JavaScript всё является объектом, лишь за двумя исключениями — [`null`](#core.undefined) и [`undefined`](#core.undefined). @@ -27,7 +27,7 @@ var foo = {}; // новый пустой объект - // новый объект со свойством с именем 'test', имеющим значение 12 + // новый объект со свойством 'test', имеющим значение 12 var bar = {test: 12}; ### Доступ к свойствам @@ -76,7 +76,21 @@ Свойства объектов могут записываться и напрямую символами и в виде строк. В соответствии с другим упущением в парсере JavaScript, этот код выбросит `SyntaxError` во всех версиях ранее ECMAScript 5. -Источником ошибки является факт, что `delete` — это *ключевое слово* и поэтому его необходимо записывать как *строчный литерал* ради уверенности в том, что оно будет корректно опознано более старыми движками JavaScript. +Источником ошибки является факт, что `delete` — это *ключевое слово* и поэтому его необходимо записывать как *строчный литерал* — ради уверенности в том, что оно будет корректно опознано более старыми движками JavaScript. + +И еще один пример в пользу строковой нотации, это относится к JSON: + + // валидный JS и валидный JSON + { + "foo":"oof", + "bar":"rab" + } + + // валидный JS и НЕ валидный 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 diff --git a/doc/ru/object/prototype.md b/doc/ru/object/prototype.md index 34a13027..898f4d2e 100644 --- a/doc/ru/object/prototype.md +++ b/doc/ru/object/prototype.md @@ -1,8 +1,8 @@ ## Великий Прототип -В JavaScript отсутствует классическая модель наследования — вместо неё использользуется [*прототипная модель*][1]. +В JavaScript отсутствует классическая модель наследования — вместо неё используется [*прототипная модель*][1]. -Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё предельно легко реализовать классическое наследование, а наоборот — тут уж придется сильно потрудиться. +Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё предельно легко реализовать классическое наследование, а наоборот — тут уж придётся сильно потрудиться. Из-за того, что JavaScript — практически единственный широко используемый язык с прототипным наследованием, придётся потратить некоторое время на осознание различий между этими двумя моделями. @@ -41,50 +41,41 @@ > **Замечание:** Никогда **не** используйте конструкцию `Bar.prototype = Foo`, поскольку ссылка будет указывать не на прототип `Foo`, а на объект функции `Foo`. Из-за этого цепочка прототипов будет проходить через `Function.prototype`, а не через `Foo.prototype` и в результате функция `method` не будет содержаться в цепочке прототипов. -### 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. +При обращении к какому-либо свойству объекта, JavaScript проходит **вверх** по цепочке прототипов этого объекта, пока не найдет свойство c запрашиваемым именем. -When it reaches the top of the chain - namely `Object.prototype` - and still -hasn't found the specified property, it will return the value -[undefined](#core.undefined) instead. +Если он достигнет верхушки этой цепочки (`Object.prototype`) и при этом так и не найдёт указанное свойство, вместо него вернётся значение [undefined](#core.undefined). -### The prototype property +### Свойство `prototype` -While the prototype property is used by the language to build the prototype -chains, it is still possible to assign **any** given value to it. Although -primitives will simply get ignored when assigned as a prototype. +То, что свойство `prototype` используется языком для построения цепочек прототипов, даёт нам возможность присвоить **любое** значение этому свойству. Однако, обычные примитивы будут просто-напросто игнорироваться при назначении в качестве прототипа. function Foo() {} - Foo.prototype = 1; // no effect + Foo.prototype = 1; // ничего не произойдёт + Foo.prototype = { + "foo":"bar" + }; + +При этом присвоение объектов, как в примере выше, позволит динамически создавать цепочки прототипов. -Assigning objects, as shown in the example above, will work, and allows for dynamic -creation of prototype chains. +### Производительность -### Performance +Поиск свойств, располагающихся относительно высоко по цепочке прототипов, может негативно сказаться на производительности, особенно в критических местах кода. Если же мы попытаемся найти несуществующее свойство, то поиск будет осуществлён вообще по всей цепочке, со всеми вытекающими последствиями. -The lookup time for properties that are high up on the prototype chain can have a -negative impact on performance critical sections of code. Additionally, trying to -access non-existent properties will always traverse the full prototype chain. +Кроме этого, при [переборе](#object.forinloop) свойств объекта, будет обработано **каждое** свойство, существующее в цепочке прототипов. -Also, when [iterating](#object.forinloop) over the properties of an object -**every** property that is on the prototype chain will get enumerated. +### Расширение встроенных прототипов -### Extension of native prototypes +Часто встречается неверное применение прототипов — расширение прототипа `Object.prototype` или прототипов одного из встроенных объектов JavaScript. -One mis-feature that is often used is to extend `Object.prototype` or one of the -other built in prototypes. +Подобная практика нарушает принцип *инкапсуляции*, и имеет соответствующее название — [monkey patching][2]. К сожалению, в основу многих широко распространенных фреймворков, например Prototype, положен принцип изменения базовых прототипов. Вам же стоит запомнить — от хорошей жизни прототипы встроенных объектов не меняют. -This technique is called [monkey patching][2] and breaks *encapsulation*. While -used by widely spread frameworks such as [Prototype][3], there is still no good -reason for cluttering built-in types with additional *non-standard* functionality. +**Единственным** оправданием для расширения встроенных прототипов может быть только воссоздание возможностей более новых движков JavaScript, например функция [`Array.forEach`][4], которая появилась в версии 1.6. -The **only** good reason for extending a built-in prototype is to backport -the features of newer JavaScript engines; for example, -[`Array.forEach`][4]. +### Заключение -### In conclusion +Перед тем как разрабатывать сложные приложения на JavaScript, вы **должны** полностью понимать как работают прототипы, и как организовывать наследование на их основе. Так же, помните о зависимости между длиной цепочек прототипов и производительностью — разрывайте их при необходимости. Кроме того - **никогда** не расширяйте прототипы встроенных объектов (ну, если только для совместимости с новыми возможностями Javascript) It is a **must** to understand the prototypal inheritance model completely before writing complex code which makes use of it. Also, watch the length of From 81ae78edf2262de68131ba8c3b00fd1c3b39bfbb Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Wed, 16 Mar 2011 22:11:52 +0200 Subject: [PATCH 057/641] core/eval chapter --- doc/ru/core/eval.md | 31 +++++++++++-------------------- doc/ru/object/prototype.md | 6 ------ 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/doc/ru/core/eval.md b/doc/ru/core/eval.md index 9ec79eed..b7f08230 100644 --- a/doc/ru/core/eval.md +++ b/doc/ru/core/eval.md @@ -1,6 +1,6 @@ -## Why not to use `eval` +## Почему нельзя использовать `eval` -The `eval` function will execute a string of JavaScript code in the local scope. +Функция `eval` выполняет строчку кода JavaScript в локальном области видимости. var foo = 1; function test() { @@ -11,8 +11,7 @@ The `eval` function will execute a string of JavaScript code in the local scope. test(); // 3 foo; // 1 -But `eval` only executes in local scope when it is being called **directly** *and* -the name of the called function is actually `eval`. +Но `eval` исполняется в локальномй области видимости только когда он вызывается **напрямую** *и при этом* имя вызываемой функции именно `eval`. var foo = 1; function test() { @@ -24,25 +23,17 @@ the name of the called function is actually `eval`. test(); // 2 foo; // 3 -The use of `eval` should be avoided at **all costs**. 99.9% of its "uses" can be -achieved **without** it. - -### `eval` in disguise +**Любой ценой** избегайте использования функции `eval`. 99.9% случаев её "использования" могут достигаться **без её использования**. -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. +### `eval` под прикрытием -### Security issues +Обе [функции работы с интервалами времени](#other.timeouts) `setTimeout` и `setInterval` могут принимать строку в качестве первого аргумента. Эта строка **всегда** будет выполняться в глобальной области видимости, поскольку `eval` в этом случае не вызывается напрямую. -`eval` also is a security problem as it executes **any** code given to it, -it should **never** be used with strings of unknown or untrusted origins. +### Проблемы с безопасностью -### In conclusion +Кроме всего прочего, функция `eval` — это проблема в безопасности, поскольку исполняется **любой** переданный в неё код, **никогда** не следует использовать её со строками из неизвестных или недоверительных источников. -`eval` should never be used, any code that makes use of it is to be questioned in -its workings, performance and security. In case something requires `eval` in -order to work, its design is to be questioned and should **not** be used in the -first place, a *better design* should be used, that does not require the use of -`eval`. +### Заключение + +Никогда не стоит использовать `eval`: любое применение такого кода поднимает вопросы о качестве его работы, производительности и безопасности. Если вдруг для работы вам необходим `eval`, эта часть должна тут же ставиться под сомнение и **не** должна использоваться в первую очередь — нужно использовать *лучший способ* , которому не требуются вызовы `eval`. diff --git a/doc/ru/object/prototype.md b/doc/ru/object/prototype.md index 898f4d2e..65598818 100644 --- a/doc/ru/object/prototype.md +++ b/doc/ru/object/prototype.md @@ -77,12 +77,6 @@ Перед тем как разрабатывать сложные приложения на JavaScript, вы **должны** полностью понимать как работают прототипы, и как организовывать наследование на их основе. Так же, помните о зависимости между длиной цепочек прототипов и производительностью — разрывайте их при необходимости. Кроме того - **никогда** не расширяйте прототипы встроенных объектов (ну, если только для совместимости с новыми возможностями Javascript) -It is a **must** to understand the prototypal inheritance model completely -before writing complex code which makes use of it. Also, watch the length of -the prototype chains and break them up if necessary to avoid possible -performance issues. Further, the native prototypes should **never** be extended -unless it is for the sake of compatibility with newer JavaScript features. - [1]: http://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/ From 3dc2b57a1e59c9de158e877a9ed9f19e0bdbecf7 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Thu, 17 Mar 2011 00:14:51 +0200 Subject: [PATCH 058/641] core/semicolon --- doc/ru/core/semicolon.md | 90 +++++++++++++++++----------------------- doc/ru/intro/index.md | 2 +- 2 files changed, 39 insertions(+), 53 deletions(-) diff --git a/doc/ru/core/semicolon.md b/doc/ru/core/semicolon.md index d3b9c5b5..90a30edb 100644 --- a/doc/ru/core/semicolon.md +++ b/doc/ru/core/semicolon.md @@ -1,42 +1,36 @@ -## Automatic semicolon insertion +## Автоматическая вставка точек с запятой -Although JavaScript has C style syntax, it does **not** enforce the use of -semicolons in the source code, it is possible to omit them. +Хоть JavaScript и имеет синтаксис, подобный языкам семейства C, он при этом **не** принуждает вас ставить точки с запятой в исходных кодах — вы всегда можете их опустить. -But JavaScript is not a semicolon-less language, it in fact needs the -semicolons in order to understand the source code. Therefore the JavaScript -parser **automatically** inserts them whenever it encounters a parse -error due to a missing semicolon. +При этом JavaScript — не язык без точек с запятой, они на самом деле нужны ему, чтобы он мог разобраться в исходном коде. Поэтому парсер JavaScript **автоматически** вставляет их в тех местах, где обнаруживает ошибку парсинга из-за отсутствия точки с запятой. var foo = function() { - } // parse error, semicolon expected + } // ошибка разбора, ожидается точка с запятой test() -Insertion happens, and the parser tries again. +Происходит вставка и парсер пытается снова. var foo = function() { - }; // no error, parser continues + }; // ошибки нет, парсер продолжает test() -The automatic insertion of semicolon is considered to be one of **biggest** -design flaws in the language, as it *can* change the behavior of code. +Автоматическая вставка точек с запятой считается одним из **наибольших** упущений в проекте языка, поскольку она *может* изменять поведение кода. -### How it works +### Как это работает -The code below has no semicolons in it, so it is up to the parser to decide where -to insert them. +Приведённый код не содержит точек с запятой, так что места для их вставки остаются на совести парсера: (function(window, undefined) { function test(options) { - log('testing!') + log('тестируем!') (options.list || []).forEach(function(i) { }) options.value.test( - 'long string to pass here', - 'and another long string to pass' + 'здесь передадим длинную строчку', + 'и ещё одну на всякий случай' ) return @@ -53,62 +47,54 @@ to insert them. })(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) { + // не вставлена точка с запятой, строки были объединены + log('тестируем!')(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 + return; // <- вставлена, в результате оператор return разбит на два блока + { // теперь парсер считает этот блок отдельным - // a label and a single expression statement - foo: function() {} - }; // <- inserted + // метка и одинокое выражение + foo: function() {} + }; // <- вставлена } - window.test = test; // <- inserted + window.test = test; // <- вставлена - // The lines got merged again + // снова объединились строки })(window)(function(window) { - window.someLibrary = {}; // <- inserted + window.someLibrary = {}; // <- вставлена - })(window); //<- inserted + })(window); //<- вставлена -> **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. +> **Замечание:** Парсер JavaScript "некорректно" обрабатывает оператор return, за которым следует новая строка; кстати, причина может быть и не в автоматической вставке точек с запятой, но это нежелательный сайд-эффект в любом случае -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!') + log('тестируем!') (options.list || []).forEach(function(i) {}) -This code gets transformed into one line. +Такой код трасформируется в строку - log('testing!')(options.list || []).forEach(function(i) {}) + log('тестируем!')(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`. +**Чрезвычайно** высоки шансы, что `log` возращает **не** функцию; таким образом, эта строка вызовет `TypeError` с сообщением о том, что `undefined не является функцией`. -### In conclusion +### Заключение -It is highly recommended to **never** omit semicolons, it is also advocated to -keep braces on the same line with their corresponding statements and to never omit -them for one single-line `if` / `else` statements. Both of these measures will -not only improve the consistency of the code, they will also prevent the -JavaScript parser from changing its behavior. +Настоятельно рекомендуем **никогда** не забывать ставить или опускать точку с запятой; так же рекомендуется оставлять скобки на одной строке с соответствующим оператором и никогда не опускать их для выражений с использованием `if` / `else`. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят от изменения его поведения произведённого парсером втихую. diff --git a/doc/ru/intro/index.md b/doc/ru/intro/index.md index 8b47ee95..fa7c6636 100644 --- a/doc/ru/intro/index.md +++ b/doc/ru/intro/index.md @@ -2,7 +2,7 @@ **JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам языка JavaScript. В ней вы найдёте советы о том, как избежать распространённых ошибок и предсказать появление тех или иных багов. В документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми, продираясь к глубинам языка, могут столкнуться даже просвещённые JavaScript-программисты. -JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Вам потребуется реальный опыт работы с языком чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста внимательно ознакомьтесь с замечательным [руководством][1] на сайте Mozilla Developer Network. +JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Вам понадобится реальный опыт работы с языком чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста внимательно ознакомьтесь с замечательным [руководством][1] на сайте Mozilla Developer Network. [1]: https://developer.mozilla.org/en/JavaScript/Guide From 29d94536152f6f09dca4501e9073617429e3b8c2 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Thu, 17 Mar 2011 21:00:04 +0200 Subject: [PATCH 059/641] core/undefined --- doc/ru/core/undefined.md | 67 ++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 43 deletions(-) diff --git a/doc/ru/core/undefined.md b/doc/ru/core/undefined.md index e4e0e49f..3e2a0a00 100644 --- a/doc/ru/core/undefined.md +++ b/doc/ru/core/undefined.md @@ -1,52 +1,40 @@ -## `undefined` and `null` +## `undefined` и `null` -JavaScript has two distinct values for `nothing`, the more useful of these two -being `undefined`. +В JavaScript есть два отдельных типа для представления `ничего`, при этом более полезным из них является `undefined`. -### The value `undefined` +### Тип `undefined` -`undefined` is a type with exactly one value: `undefined`. +`undefined` — это тип с единственным возможным значением: `undefined`. -The language also defines a global variable that has the value of `undefined`, -this variable is also called `undefined`. But this variable is **not** a constant, -nor is it a keyword of the language. This means that its *value* can be easily -overwritten. +Кроме этого, в языке определена глобальная переменная со значением `undefined`, и эта переменная так и называется — `undefined`. Не являясь константой, она не является и ключевым словом. Из этого следует, что её значение можно с лёгкостью переопределить. -> **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`. +> **ES5 Замечание:** в ECMAScript 5 переменная `undefined` **больше не** *доступна на запись* в strict-режиме, однако она всё также может быть перегружена по имени, например - функцией с именем `undefined`. -Some examples for when the value `undefined` is returned: +Несколько случаев, когда возвращается `undefined`: - - Accessing the (unmodified) global variable `undefined`. - - Implicit returns of functions due to missing `return` statements. - - `return` statements which do not explicitly return anything. - - Lookups of non-existent properties. - - Function parameters which do not had any explicit value passed. - - Anything that has been set to the value of `undefined`. + - При попытке доступа к глобальной переменной `undefined` (если она не изменена). + - Неявный возврат из функции при отсутствия в ней оператора `return`. + - Из операторов `return`, которые ничего не возвращают. + - В результате поиска несуществующего свойства у объекта (и доступа к нему). + - Параметры, которые не были переданы в функцию явно. + - При доступе ко всему, чьим значением является `undefined`. -### Handling changes to the value of `undefined` +### Обработка изменений значения `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`. +Поскольку глобальная переменная `undefined` содержит копию настоящего *значения* `undefined`, присвоение этой переменной нового значения **не** изменяет значения или *типа* `undefined`. -Still, in order to compare something against the value of `undefined` it is -necessary to retrieve the value of `undefined` first. +Но при этом, чтобы сравнить что-либо со значением `undefined` прежде важно получить значение самой переменной `undefined`. -In order to protect code against a possible overwritten `undefined` variable, a -common technique used is to add an additional parameter to an -[anonymous wrapper](#function.scopes), that gets no argument passed to it. +Чтобы защитить код от переопределения переменной `undefined`, часто используется техника [анонимной обёртки](#function.scopes), которая принимает отсутствующий аргумент. 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) { @@ -55,18 +43,11 @@ wrapper. })('Hello World', 42); -The only difference being here, that this version results in 4 more bytes being -used in case it is minified and there is no other `var` statement inside the -anonymous wrapper. +Единственная разница между этими вариантами в том, что последняя версия будет больше на 4 байта при минификации, а в первом случае внутри анонимной обёртки нет дополнительного оператора `var`. -### Uses of `null` +### Использование `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`. +Хотя `undefined` в контексте языка JavaScript чаще используется в качестве традиционного *`null`*, настоящий `null` (и тип и литерал) является в большей или меньшей степени просто другим типом данных. +Он используется во внутренних механизмах JavaScript (например для определения конца цепочки прототипов засчёт присваивания `Foo.prototype = null`), но в большинстве случаев тип `null` может быть заменён на `undefined`. From e087d7361c0bd9fa8d35a685bc78a50c0a51e84d Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Thu, 17 Mar 2011 21:40:09 +0200 Subject: [PATCH 060/641] other/timeout, first part --- doc/ru/other/timeouts.md | 61 +++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/doc/ru/other/timeouts.md b/doc/ru/other/timeouts.md index c988702e..a1eaa91c 100644 --- a/doc/ru/other/timeouts.md +++ b/doc/ru/other/timeouts.md @@ -1,32 +1,23 @@ -### `setTimeout` and `setInterval` +### `setTimeout` и `setInterval` -Since JavaScript is asynchronous, it is possible to schedule the execution of a -function by using the `setTimeout` and `setInterval` functions. +Поскольку JavaScript поддерживает асинхронность, есть возможность запланировать выполнение функции, используя функции `setTimeout` и `setInterval`. -> **Note:** Timeouts are **not** part of the ECMAScript Standard, they are -> implemented as part of the [DOM][1]. +> **Замечание:** Таймауты **не** являются частью стандарта ECMAScript, они были разработаны как раздел [DOM][1]. function foo() {} - var id = setTimeout(foo, 1000); // returns a Number > 0 + var id = setTimeout(foo, 1000); // возвращает число > 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**. +Функция `setTimeout` возвращает идентификатор таймаута и планирует вызвать `foo` через, **примерно**, тысячу миллисекунд. Фунция `foo` будет вызвана ровно **один** раз. -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. +В зависимости от разрешения таймера в используемом для запуска кода движке JavaScript, а также с учётом факта, что JavaScript является однопоточным языком и посторонний код может заблокировать выполнение потока, нет **никакой** гарантии, что код переданный будет выполнен ровно через указанное в вызове `setTimeout` время. -The function that was passed as the first parameter will get called by the -*global object*, that means, that [`this`](#function.this) inside the called function -refers to that very object. +Переданная через первый параметр функция будет вызвана как *глобальный объект* — это значит что оператор [`this`](#function.this) в вызываемой функции будет ссылаться на этот самый объект. function Foo() { this.value = 42; this.method = function() { - // this refers to the global object - console.log(this.value); // will log undefined + // this ссылается на глобальный объект + console.log(this.value); // выведет в лог undefined }; setTimeout(this.method, 500); } @@ -34,18 +25,18 @@ refers to that very object. > **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 +> 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. +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 +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. @@ -72,7 +63,7 @@ the function itself. 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 +stacking of calls and it gives additional control.`foo` itself can now decide whether it wants to run again or not. ### Manually clearing timeouts @@ -86,7 +77,7 @@ the first place. ### Clearing all timeouts -As there is no built-in method for clearing all timeouts and/or intervals, +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 @@ -120,15 +111,15 @@ This feature should **never** be used, since it internally makes use of `eval`. } 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 +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 that will get called by either of the timeout functions. function foo(a, b, c) {} - + // NEVER use this setTimeout('foo(1,2, 3)', 1000) @@ -137,19 +128,19 @@ function that will get called by either of the timeout functions. foo(a, b, c); }, 1000) -> **Note:** While it is also possible to use the syntax +> **Note:** While it is also possible to use the syntax > `setTimeout(foo, 1000, a, b, c)`, it is not recommended, as its use may lead > to subtle errors when used with [methods](#function.this). ### In conclusion -**Never** should a string be used as the parameter of `setTimeout` or -`setInterval`. It is a clear sign of **really** bad code, when arguments need +**Never** should a string be used as the parameter of `setTimeout` or +`setInterval`. It is a clear sign of **really** bad code, when arguments need to be supplied to the function that gets called. An *anonymous function* should be passed that then takes care of the actual call. Further, the use of `setInterval` should be avoided since its scheduler is not blocked by executing JavaScript. -[1]: http://en.wikipedia.org/wiki/Document_Object_Model +[1]: http://en.wikipedia.org/wiki/Document_Object_Model From 43d91a745fb3f19b2b39fff1544249d8c7deab56 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Thu, 17 Mar 2011 21:46:48 +0200 Subject: [PATCH 061/641] translator section --- doc/language.json | 3 ++- doc/ru/index.json | 2 ++ doc/ru/intro/contributors.md | 11 ----------- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/doc/language.json b/doc/language.json index 677da532..335acf50 100644 --- a/doc/language.json +++ b/doc/language.json @@ -1,4 +1,5 @@ { "default": "en", - "listed": ["en"] + "listed": ["en", "ru"] } + diff --git a/doc/ru/index.json b/doc/ru/index.json index cffddf23..84327cdc 100644 --- a/doc/ru/index.json +++ b/doc/ru/index.json @@ -9,6 +9,7 @@ "articles": [ "authors", "contributors", + "translators", "license" ] }, @@ -70,3 +71,4 @@ } ] } + diff --git a/doc/ru/intro/contributors.md b/doc/ru/intro/contributors.md index 4f9f35ef..da825f6a 100644 --- a/doc/ru/intro/contributors.md +++ b/doc/ru/intro/contributors.md @@ -3,17 +3,6 @@ - [Кайо Ромао /Caio Romão/][1] (проверка орфографии) - [Андреас Бликст /Andreas Blixt/][2] (проверка грамматики) -## Перевод на русский - - - [Ulric 'shaman.sir' Wilfred][3] - - [Антон Шевчук][4] - - [Максим Лозовой][5] - - [Елена Пашкова][6] - [1]: https://github.com/caio [2]: https://github.com/blixt -[3]: http://shamansir.madfire.net/ -[4]: http://anton.shevchuk.name/ -[5]: http://nixsolutions.com/ -[6]: http://nixsolutions.com/ From ba876320e67867b66bb362ab77f1d66d57c9079e Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Thu, 17 Mar 2011 21:48:44 +0200 Subject: [PATCH 062/641] forgot translators file --- doc/ru/intro/translators.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 doc/ru/intro/translators.md diff --git a/doc/ru/intro/translators.md b/doc/ru/intro/translators.md new file mode 100644 index 00000000..c7c95cdc --- /dev/null +++ b/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/ + From b4bd69075341806933132efd163a4a90adabd0f1 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Thu, 17 Mar 2011 22:16:11 +0200 Subject: [PATCH 063/641] stilistic fixes --- doc/ru/core/eval.md | 8 ++++---- doc/ru/core/semicolon.md | 10 +++++----- doc/ru/core/undefined.md | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/ru/core/eval.md b/doc/ru/core/eval.md index b7f08230..795d2ee1 100644 --- a/doc/ru/core/eval.md +++ b/doc/ru/core/eval.md @@ -1,6 +1,6 @@ ## Почему нельзя использовать `eval` -Функция `eval` выполняет строчку кода JavaScript в локальном области видимости. +Функция `eval` выполняет строку кода JavaScript в локальном области видимости. var foo = 1; function test() { @@ -11,7 +11,7 @@ test(); // 3 foo; // 1 -Но `eval` исполняется в локальномй области видимости только когда он вызывается **напрямую** *и при этом* имя вызываемой функции именно `eval`. +Но `eval` исполняется в локальной области видимости только когда он вызывается **напрямую** *и при этом* имя вызываемой функции именно `eval`. var foo = 1; function test() { @@ -23,11 +23,11 @@ test(); // 2 foo; // 3 -**Любой ценой** избегайте использования функции `eval`. 99.9% случаев её "использования" могут достигаться **без её использования**. +**Любой ценой** избегайте использования функции `eval`. 99.9% случаев её "использования" могут достигаться **без её участия**. ### `eval` под прикрытием -Обе [функции работы с интервалами времени](#other.timeouts) `setTimeout` и `setInterval` могут принимать строку в качестве первого аргумента. Эта строка **всегда** будет выполняться в глобальной области видимости, поскольку `eval` в этом случае не вызывается напрямую. +Обе [функции работы с интервалами времени](#other.timeouts) `setTimeout` и `setInterval` могут принимать строку в качестве первого аргумента. Эта строка **всегда** будет выполняться в глобальной области видимости, поскольку `eval` в этом случае вызывается *не напрямую*. ### Проблемы с безопасностью diff --git a/doc/ru/core/semicolon.md b/doc/ru/core/semicolon.md index 90a30edb..ce0af172 100644 --- a/doc/ru/core/semicolon.md +++ b/doc/ru/core/semicolon.md @@ -1,8 +1,8 @@ ## Автоматическая вставка точек с запятой -Хоть JavaScript и имеет синтаксис, подобный языкам семейства C, он при этом **не** принуждает вас ставить точки с запятой в исходных кодах — вы всегда можете их опустить. +Хоть JavaScript и имеет синтаксис, подобный языкам семейства C, он при этом **не** принуждает вас ставить точки с запятой в исходном коде — вы всегда можете их опустить. -При этом JavaScript — не язык без точек с запятой, они на самом деле нужны ему, чтобы он мог разобраться в исходном коде. Поэтому парсер JavaScript **автоматически** вставляет их в тех местах, где обнаруживает ошибку парсинга из-за отсутствия точки с запятой. +При этом JavaScript — не язык без точек с запятой, они на самом деле нужны ему, чтобы он мог разобраться в вашем коде. Поэтому парсер JavaScript **автоматически** вставляет их в те места, где сталкивается с ошибкой парсинга из-за отсутствия точки с запятой. var foo = function() { } // ошибка разбора, ожидается точка с запятой @@ -14,7 +14,7 @@ }; // ошибки нет, парсер продолжает test() -Автоматическая вставка точек с запятой считается одним из **наибольших** упущений в проекте языка, поскольку она *может* изменять поведение кода. +Автоматическая вставка точек с запятой считается одним из **наибольших** упущений в проекте языка, поскольку она *может* изменить поведение кода. ### Как это работает @@ -77,7 +77,7 @@ })(window); //<- вставлена -> **Замечание:** Парсер JavaScript "некорректно" обрабатывает оператор return, за которым следует новая строка; кстати, причина может быть и не в автоматической вставке точек с запятой, но это нежелательный сайд-эффект в любом случае +> **Замечание:** Парсер JavaScript некорректно обрабатывает оператор return, за которым следует новая строка; кстати, причина может быть и не в автоматической вставке точек с запятой, но это в любом случае нежелательный побочный эффект Парсер радикально подменил поведение изначального кода, а в определённых случаях он сделал **абсолютно неправильные выводы**. @@ -96,5 +96,5 @@ ### Заключение -Настоятельно рекомендуем **никогда** не забывать ставить или опускать точку с запятой; так же рекомендуется оставлять скобки на одной строке с соответствующим оператором и никогда не опускать их для выражений с использованием `if` / `else`. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят от изменения его поведения произведённого парсером втихую. +Настоятельно рекомендуем **никогда** не забывать ставить или опускать точку с запятой; так же рекомендуется оставлять скобки на одной строке с соответствующим оператором и никогда не опускать их для выражений с использованием `if` / `else`. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят от изменения поведения этого кода, произведённого парсером втихую. diff --git a/doc/ru/core/undefined.md b/doc/ru/core/undefined.md index 3e2a0a00..7e2fd6d9 100644 --- a/doc/ru/core/undefined.md +++ b/doc/ru/core/undefined.md @@ -25,7 +25,7 @@ Но при этом, чтобы сравнить что-либо со значением `undefined` прежде важно получить значение самой переменной `undefined`. -Чтобы защитить код от переопределения переменной `undefined`, часто используется техника [анонимной обёртки](#function.scopes), которая принимает отсутствующий аргумент. +Чтобы защитить код от переопределения переменной `undefined`, часто используется техника [анонимной обёртки](#function.scopes), которая использует отсутствующий аргумент. var undefined = 123; (function(something, foo, undefined) { @@ -47,7 +47,7 @@ ### Использование `null` -Хотя `undefined` в контексте языка JavaScript чаще используется в качестве традиционного *`null`*, настоящий `null` (и тип и литерал) является в большей или меньшей степени просто другим типом данных. +Хотя `undefined` в контексте языка JavaScript чаще используется в качестве традиционного *null*, настоящий `null` (и тип и литерал) является в большей или меньшей степени просто другим типом данных. -Он используется во внутренних механизмах JavaScript (например для определения конца цепочки прототипов засчёт присваивания `Foo.prototype = null`), но в большинстве случаев тип `null` может быть заменён на `undefined`. +Он используется во внутренних механизмах JavaScript (например для определения конца цепочки прототипов засчёт присваивания `Foo.prototype = null`). Но в большинстве случаев тип `null` может быть заменён на `undefined`. From ea30e5ee21aa5e20f73b5844b81fcc4d0f553b4b Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Thu, 17 Mar 2011 23:42:50 +0200 Subject: [PATCH 064/641] other/timeouts, part 2 --- doc/ru/other/timeouts.md | 56 ++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/doc/ru/other/timeouts.md b/doc/ru/other/timeouts.md index a1eaa91c..909c681b 100644 --- a/doc/ru/other/timeouts.md +++ b/doc/ru/other/timeouts.md @@ -24,72 +24,54 @@ 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. +> **Замечание:** Поскольку `setTimeout` принимает **объект функции** в качестве первого параметра часто совершается ошибка в использовании `setTimeout(foo(), 1000)`, при котором будет использоваться **возвращённое значение** от вызова `foo`, а **не** вызвана функция `foo`. В большинстве случаев ошибка пройдёт незамеченной, а в случае если функция возвращает `undefined`, `setTimeout` вообще **не** породит никакой ошибки. -### Stacking calls with `setInterval` +### Поочерёдные вызовы с использованием `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. +`setTimeout` вызывает функцию единожды; `setInterval` — как и предполагает название — вызывает функцию **каждые** `X` миллисекунд. И его использование не рекомендуется. -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. +В то время, когда исполняющийся код будет блокироваться во время вызова с таймаутом, `setInterval` будет продолжать планировать последующие вызовы переданной функции. Это может, особенно в случае небольших интервалов, повлечь за собой выстраивание вызовов функций в очередь. 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. +В приведённом коде `foo` выполнится один раз и заблокирует этим главный поток на одну секунду. -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. +Пока `foo` блокирует код, `setInterval` продолжает планировать последующие её вызовы. Теперь, когда первая `foo` закончила выполнение, в очереди будут уже **десять** ожидающих выполнения вызовов `foo`. -### Dealing with possible blocking code +### Разбираемся с потенциальной блокировкой кода -The easiest as well as most controllable solution, is to use `setTimeout` within -the function itself. +Самый простой и контролируемый способ — использовать `setTimeout` внутри самой функции. 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. +Такой способ не только инкапсулирует вызов `setTimeout`, но и предотвращает от очередей блокирующих вызовов и обеспечивает дополнительный контроль. Сама функция `foo` теперь принимает решение, хочет ли она запускаться ещё раз или нет. -### 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. +Удаление таймаутов и интервалов работает через передачу соответствуюего идентификатора либо в функцию `clearTimeout`, либо в функцию `clearInterval` — в зависимости от того, какая функция `set...` использовалась для его получения. var id = setTimeout(foo, 1000); clearTimeout(id); -### Clearing all timeouts +### Очистка всех таймаутов -As there is no built-in method for clearing all timeouts and/or intervals, -it is necessary to use brute force in order to achieve this functionality. +Из-за того, что встроенного метода для удаления всех таймаутов и/или интервалов не существует, для достижения этой цели приходится использовать брутфорс. - // clear "all" timeouts + // удаляем "все" таймауты for(var i = 1; i < 1000; i++) { clearTimeout(i); } -There might still be timeouts that are unaffected by this arbitrary number; -therefore, is is instead recommended to keep track of all the timeout IDs, so -they can be cleared specifically. +Вполне могут остаться таймауты, которые не будут захвачены этим произвольным числом; так что рекомендуется следить за идентификаторами всех создающихся таймаутов, засчёт чего их можно будет удалять индивидуально. -### Hidden use of `eval` +### Скрытое использование `eval` `setTimeout` and `setInterval` can also take a string as their first parameter. This feature should **never** be used, since it internally makes use of `eval`. @@ -142,5 +124,5 @@ be passed that then takes care of the actual call. Further, the use of `setInterval` should be avoided since its scheduler is not blocked by executing JavaScript. -[1]: http://en.wikipedia.org/wiki/Document_Object_Model +[1]: http://ru.wikipedia.org/wiki/Document_Object_Model From b0e2837a4382fb3568480f9451ca09ebb5ff4eeb Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Thu, 17 Mar 2011 23:57:37 +0200 Subject: [PATCH 065/641] other/timeouts, part 2.2 --- doc/ru/other/timeouts.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/doc/ru/other/timeouts.md b/doc/ru/other/timeouts.md index 909c681b..531c91af 100644 --- a/doc/ru/other/timeouts.md +++ b/doc/ru/other/timeouts.md @@ -73,21 +73,17 @@ ### Скрытое использование `eval` -`setTimeout` and `setInterval` can also take a string as their first parameter. -This feature should **never** be used, since it internally makes use of `eval`. +`setTimeout` и `setInterval` могут принимать строку в качестве первого параметра. Эту возможность не следует использовать **никогда**, поскольку изнутри при этом производится скрытый вызов `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. As a fact, Microsoft's JScript makes use of -> the `Function` constructor in place of `eval`. +> **Замечание**: Поскольку функции работы с таймаутами **не** определены в стандарте ECMAScript, точная внутренняя механика их работы может различаться от движка к движку. Известен факт, то Microsoft JScript использует конструктор `Function` вместо `eval`. function foo() { - // will get called + // будет вызвана } function bar() { function foo() { - // never gets called + // никогда не будет вызывана } setTimeout('foo()', 1000); } @@ -102,10 +98,10 @@ function that will get called by either of the timeout functions. function foo(a, b, c) {} - // NEVER use this + // НИКОГДА не делайте такого setTimeout('foo(1,2, 3)', 1000) - // Instead use an anonymous function + // Вместо этого используйте анонимную функцию setTimeout(function() { foo(a, b, c); }, 1000) From 541083a727b7fb3e06d51fec9d3b40caafd36183 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Fri, 18 Mar 2011 19:21:24 +0200 Subject: [PATCH 066/641] finished core/timeouts --- doc/ru/other/timeouts.md | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/doc/ru/other/timeouts.md b/doc/ru/other/timeouts.md index 531c91af..29d74523 100644 --- a/doc/ru/other/timeouts.md +++ b/doc/ru/other/timeouts.md @@ -89,12 +89,9 @@ } 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`. +Поскольку `eval` в этом случае не вызывается [напрямую](#core.eval), переданная в `setTimeout` строка будет выполнена в *глобальной области видимости*; так что локальная переменная `foo` из области видимости `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) {} @@ -106,19 +103,13 @@ function that will get called by either of the timeout functions. foo(a, b, c); }, 1000) -> **Note:** While it is also possible to use the syntax -> `setTimeout(foo, 1000, a, b, c)`, it is not recommended, as its use may lead -> to subtle errors when used with [methods](#function.this). +> **Замечание:** При том, что синтаксис `setTimeout(foo, 1000, a, b, c)` разрешено использовать, это крайне не рекомендуется, поскольку может привести к сложно-разпознаваемым ошибкам при работе с [методами](#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. +**Никогда** не используйте строки как параметры `setTimeout` или `setInterval`. Это явный признак **действительно** плохого кода, если вызываемой функции необходимо передавать аргументы. Лучше передавать *анонимную функцию*, которая самостоятельно будет отвечать за сам вызов. -Further, the use of `setInterval` should be avoided since its scheduler is not -blocked by executing JavaScript. +Кроме того, избегайте использования `setInterval` в случаях, когда его планировщих может блокировать выполнение JavaScript. [1]: http://ru.wikipedia.org/wiki/Document_Object_Model From fd11cbcd1df75bbb6276532135de7c1388b7690a Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Fri, 18 Mar 2011 22:04:27 +0200 Subject: [PATCH 067/641] appendix/fromtranslators + object/hasownproperty --- doc/ru/appendix/fromtranslators.md | 6 ++++ doc/ru/index.json | 8 ++++++ doc/ru/intro/license.md | 2 +- doc/ru/object/general.md | 19 +++++++------ doc/ru/object/hasownproperty.md | 44 +++++++++++------------------- doc/ru/object/prototype.md | 14 +++++----- doc/ru/other/timeouts.md | 2 +- 7 files changed, 49 insertions(+), 46 deletions(-) create mode 100644 doc/ru/appendix/fromtranslators.md diff --git a/doc/ru/appendix/fromtranslators.md b/doc/ru/appendix/fromtranslators.md new file mode 100644 index 00000000..b43c061f --- /dev/null +++ b/doc/ru/appendix/fromtranslators.md @@ -0,0 +1,6 @@ +## От переводчиков + +Авторы этой документации требуют от читателя не совершать каких-либо ошибок и постоянно следить за качеством пишущегося кода. Мы, как переводчики и опытные программисты на JavaScript рекомендуем *прислушиваться* к этим советам, но при этом не делать из этого *крайность*. Опыт — сын ошибок трудных, и иногда в борьбе с ошибками зарождается намного более детальное понимание предмета. Да, нужно избегать ошибок, но допускать их неосознанно — вполне нормально. + +Относитесь с мудростью к тому, что вы пишете — важно знать как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. *Важно* знать как работает [прототипное наследование](#object.prototype), но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код, если у вас есть талант. + diff --git a/doc/ru/index.json b/doc/ru/index.json index 84327cdc..9e27bf03 100644 --- a/doc/ru/index.json +++ b/doc/ru/index.json @@ -68,7 +68,15 @@ "articles": [ "timeouts" ] + }, + { + "title": "Пояснения", + "dir": "appendix", + "articles": [ + "fromtranslators" + ] } + ] } diff --git a/doc/ru/intro/license.md b/doc/ru/intro/license.md index 5cdd8256..3383980f 100644 --- a/doc/ru/intro/license.md +++ b/doc/ru/intro/license.md @@ -1,6 +1,6 @@ ## Лицензия -JavaScript Гарден распространяется под [лицензией MIT][1] и располагается на [GitHub][2]. Если вы найдёте ошибку или опечатку, пожалуйста [сообщите нам о ней][3] или запросите права на загрузку репозитория. Кроме того, вы можете найти нас в [комнате JavaScript][4] среди чатов Stack Overflow. +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 diff --git a/doc/ru/object/general.md b/doc/ru/object/general.md index e976da27..2ee81e54 100644 --- a/doc/ru/object/general.md +++ b/doc/ru/object/general.md @@ -74,23 +74,24 @@ delete: 'Я тоже ключевое слово, так что я' // бросаю SyntaxError }; -Свойства объектов могут записываться и напрямую символами и в виде строк. В соответствии с другим упущением в парсере JavaScript, этот код выбросит `SyntaxError` во всех версиях ранее ECMAScript 5. +Свойства объектов могут записываться и явно символами и в виде закавыченных строк. В связи с другим упущением в парсере JavaScript, этот код выбросит `SyntaxError` во всех версиях ранее ECMAScript 5. -Источником ошибки является факт, что `delete` — это *ключевое слово* и поэтому его необходимо записывать как *строчный литерал* — ради уверенности в том, что оно будет корректно опознано более старыми движками JavaScript. +Источником ошибки является факт, что `delete` — это *ключевое слово* и поэтому его необходимо записывать как *строчный литерал*: ради уверенности в том, что оно будет корректно опознано более старыми движками JavaScript. -И еще один пример в пользу строковой нотации, это относится к JSON: +*От перев.:* И еще один пример в пользу строковой нотации, это относится к [JSON][2]: - // валидный JS и валидный JSON + // валидный JavaScript и валидный JSON { - "foo":"oof", - "bar":"rab" + 'foo': 'oof', + 'bar': 'rab' } - // валидный JS и НЕ валидный JSON + // валидный JavaScript и НЕ валидный JSON { - foo:"oof", - bar:"rab" + 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/doc/ru/object/hasownproperty.md b/doc/ru/object/hasownproperty.md index 6b18353d..dd2fa159 100644 --- a/doc/ru/object/hasownproperty.md +++ b/doc/ru/object/hasownproperty.md @@ -1,53 +1,41 @@ -## `hasOwnProperty` +## Функция `hasOwnProperty` -In order to check whether a object has a property defined on *itself* and **not** -somewhere on its [prototype chain](#object.prototype), it is necessary to use the -`hasOwnProperty` method which all objects inherit from `Object.prototype`. +Если вам необходимо проверить, определено ли свойство у *самого объекта*, а **не** в его [цепочке прототипов](#object.prototype), вы можете использовать метод `hasOwnProperty`, который все объекты наследуют от `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`. +> **Note:** Для проверки наличия свойства **недостаточно** проверять, эквивалентно ли оно `undefined`. Свойство может вполне себе существовать, но при этом ему может быть присвоено значение `undefined`. -`hasOwnProperty` is the only thing in JavaScript which deals with properties and -does **not** traverse the prototype chain. +`hasOwnProperty` — единственная функция в JavaScript, которая позволяет получить свойства объекта **без обращения** к цепочке его прототипов. - // Poisoning Object.prototype - Object.prototype.bar = 1; + // испортим Object.prototype + Object.prototype.bar = 1; var foo = {goo: undefined}; - + foo.bar; // 1 'bar' in foo; // true foo.hasOwnProperty('bar'); // false foo.hasOwnProperty('goo'); // true -Only `hasOwnProperty` will give the correct and expected result, this is -essential when iterating over the properties of any object. There is **no** other -way to exclude properties that are not defined on the object *itself*, but -somewhere on its prototype chain. +Только используя hasOwnProperty можно гарантировать правильный результат при переборе свойств объекта. И **нет** иного способа для определения свойств, которые определены в *самом* объекте, а не где-то в цепочке прототипов. -### `hasOwnProperty` as a property +### `hasOwnProperty` как свойство -JavaScript does **not** protect the property name `hasOwnProperty`; thus, if the -possibility exists that an object might have a property with this name, it is -necessary to use an *external* `hasOwnProperty` in order to get correct results. +JavaScript **не** резервирует свойство с именем `hasOwnProperty`. Так что, если есть потенциальная возможность, что объект может содержать свойство с таким именем, требуется использовать *внешний* вариант функции `hasOwnProperty` чтобы получить корректные результаты. var foo = { hasOwnProperty: function() { return false; }, - bar: 'Here be dragons' + bar: 'Да прилетят драконы' }; - foo.hasOwnProperty('bar'); // always returns false + foo.hasOwnProperty('bar'); // всегда возвращает false - // Use another hasOwnProperty and call it with 'this' set to foo + // Используем метод hasOwnProperty пустого объекта + // и передаём foo в качестве this {}.hasOwnProperty.call(foo, 'bar'); // true -### In conclusion +### Заключение -When checking for the existence of a property on a object, `hasOwnProperty` is -the **only** method of doing so. It is also recommended to make `hasOwnProperty` -part of **every** [`for in` loop](#object.forinloop), this will avoid errors from -extended native [prototypes](#object.prototype). +**Единственным** способом проверить существование свойства у объекта является использование метода `hasOwnProperty`. При этом, рекомендуется использовать этот метод в **каждом** [цикле `for in`](#object.forinloop) вашего проекта, чтобы избежать возможных ошибок с ошибочным заимствованием свойств из [прототипов](#object.prototype) родительских объектов. Также вы можете использовать конструкцию `{}.hasOwnProperty.call(...)` на случай, если кто-то вздумает расширить [прототипы](#object.prototype) встроенных объектов. diff --git a/doc/ru/object/prototype.md b/doc/ru/object/prototype.md index 65598818..6982caa7 100644 --- a/doc/ru/object/prototype.md +++ b/doc/ru/object/prototype.md @@ -2,13 +2,13 @@ В JavaScript отсутствует классическая модель наследования — вместо неё используется [*прототипная модель*][1]. -Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё предельно легко реализовать классическое наследование, а наоборот — тут уж придётся сильно потрудиться. +Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё можно предельно легко реализовать классическое наследование, а попытки обойти его при этом могут создать только трудности. Из-за того, что JavaScript — практически единственный широко используемый язык с прототипным наследованием, придётся потратить некоторое время на осознание различий между этими двумя моделями. -Первое важное отличие заключается в том, что наследование в JavaScript производится с использованием так называемых *цепочек прототипов*. +Первое важное отличие заключается в том, что наследование в JavaScript выполняется с использованием так называемых *цепочек прототипов*. -> **Замечание:** В результате выполнения конструкции `Bar.prototype = Foo.prototype` оба объекта будут делить друг с другом **один и тот же** прототип. Так что изменение прототипа одного из объектов повлечёт за собой изменение прототипа другого и наоборот — вряд ли это окажется тем, чего вы ожидали. +> **Замечание:** В результате выполнения конструкции `Bar.prototype = Foo.prototype` оба объекта будут делить друг с другом **один и тот же** прототип. Так что изменение прототипа одного из объектов повлечёт за собой изменение прототипа другого и наоборот — вряд ли это окажется тем, что вы ожидали. function Foo() { this.value = 42; @@ -49,7 +49,7 @@ ### Свойство `prototype` -То, что свойство `prototype` используется языком для построения цепочек прототипов, даёт нам возможность присвоить **любое** значение этому свойству. Однако, обычные примитивы будут просто-напросто игнорироваться при назначении в качестве прототипа. +То, что свойство `prototype` используется языком для построения цепочек прототипов, даёт нам возможность присвоить **любое** значение этому свойству. Однако обычные примитивы будут просто-напросто игнорироваться, если назначать их в качестве прототипа. function Foo() {} Foo.prototype = 1; // ничего не произойдёт @@ -57,7 +57,7 @@ "foo":"bar" }; -При этом присвоение объектов, как в примере выше, позволит динамически создавать цепочки прототипов. +При этом присвоение объектов, как в примере выше, позволит вам динамически создавать цепочки прототипов. ### Производительность @@ -71,11 +71,11 @@ Подобная практика нарушает принцип *инкапсуляции*, и имеет соответствующее название — [monkey patching][2]. К сожалению, в основу многих широко распространенных фреймворков, например Prototype, положен принцип изменения базовых прототипов. Вам же стоит запомнить — от хорошей жизни прототипы встроенных объектов не меняют. -**Единственным** оправданием для расширения встроенных прототипов может быть только воссоздание возможностей более новых движков JavaScript, например функция [`Array.forEach`][4], которая появилась в версии 1.6. +**Единственным** оправданием для расширения встроенных прототипов может быть только воссоздание возможностей более новых движков JavaScript, например функции [`Array.forEach`][4], которая появилась в версии 1.6. ### Заключение -Перед тем как разрабатывать сложные приложения на JavaScript, вы **должны** полностью понимать как работают прототипы, и как организовывать наследование на их основе. Так же, помните о зависимости между длиной цепочек прототипов и производительностью — разрывайте их при необходимости. Кроме того - **никогда** не расширяйте прототипы встроенных объектов (ну, если только для совместимости с новыми возможностями Javascript) +Перед тем, как вы приступите к разработке сложных приложений на 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 diff --git a/doc/ru/other/timeouts.md b/doc/ru/other/timeouts.md index 29d74523..94ab9cf1 100644 --- a/doc/ru/other/timeouts.md +++ b/doc/ru/other/timeouts.md @@ -109,7 +109,7 @@ **Никогда** не используйте строки как параметры `setTimeout` или `setInterval`. Это явный признак **действительно** плохого кода, если вызываемой функции необходимо передавать аргументы. Лучше передавать *анонимную функцию*, которая самостоятельно будет отвечать за сам вызов. -Кроме того, избегайте использования `setInterval` в случаях, когда его планировщих может блокировать выполнение JavaScript. +Кроме того, избегайте использования `setInterval` в случаях, когда его планировщик может блокировать выполнение JavaScript. [1]: http://ru.wikipedia.org/wiki/Document_Object_Model From 8d1b0816b508781edd364c970bac14a71b4f12ec Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Fri, 18 Mar 2011 22:39:22 +0200 Subject: [PATCH 068/641] object/forinloop --- doc/ru/object/forinloop.md | 42 +++++++++++---------------------- doc/ru/object/hasownproperty.md | 2 +- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/doc/ru/object/forinloop.md b/doc/ru/object/forinloop.md index 6150e259..d42fd9e0 100644 --- a/doc/ru/object/forinloop.md +++ b/doc/ru/object/forinloop.md @@ -1,51 +1,37 @@ -## The `for in` Loop +## Цикл `for in` -Just like the `in` operator, the `for in` loop also traverses the prototype -chain when iterating over the properties of an object. +Как и оператор `in`, цикл `for in` проходит по всей цепочке прототипов обходя свойства объекта. -> **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 +> **Примечание:** Цикл `for in` **не** обходит те свойства объекта, у которых атрибут `enumerable` установлен в `false`; как пример - свойство `length` у массивов + + // Испортим Object.prototype Object.prototype.bar = 1; var foo = {moo: 2}; for(var i in foo) { - console.log(i); // prints both bar and moo + console.log(i); // печатает и bar и moo } -Since it is not possible to change the behavior of the `for in` loop itself, it -is necessary to filter out the unwanted properties inside the loop body , -this is done by using the [`hasOwnProperty`](#object.hasownproperty) method of -`Object.prototype`. +Так как изменить поведение цикла `for in` как такового не представляется возможным, то для фильтрации нежелательных свойств объекта внутри этого цикла используют метод [`hasOwnProperty`](#object.hasownproperty) из `Object.prototype`. -> **Note:** Since the `for in` always traverses the complete prototype chain, it -> will get slower with each additional layer of inheritance added to an object. +> **Примечание:** Цикл `for in` всегда обходит всю цепочку прототипов полностью: таким образом, чем больше прототипов (слоёв наследования) в цепочке, тем медленнее работает цикл. -### Using `hasOwnProperty` for filtering +### Использование `hasOwnProperty` в качестве фильтра - // still the foo from above + // возьмём foo из примера выше for(var i in foo) { if (foo.hasOwnProperty(i)) { console.log(i); } } -This version is the only correct one to use. Due to the use of `hasOwnPropery` 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. +Эта единственная версия правильного использования. Благодаря использованию `hasOwnPropery` будет выведен **только** `moo`. Если же убрать `hasOwnProperty`, код становится нестабилен и могут возникнуть ошибки, если кто-то изменил встроенные прототипы, такие как `Object.prototype`. -One widely used framework which does this is [Prototype][1]. When this -framework is included, `for in` loops that do not use `hasOwnProperty` are -guaranteed to break. +Один из самых популярных фреймворков [Prototype][1] как раз этим и славится, и если вы его подключаете, то не забудьте использовать `hasOwnProperty` внутри цикла `for in`, иначе гарантированно возникнут проблемы. -### Best practices +### Рекомендации -It is recommended to **always** use `hasOwnProperty`. Never should any -assumptions be made about the environment the code is running in, or whether the -native prototypes have been extended or not. +Рекомендация одна — **всегда** используйте `hasOwnProperty`. пишите код, который будет в наименьшей мере зависеть от окружения в котором он будет запущен — не стоит гадать, расширял кто-то прототипы или нет и используется ли в ней та или иная библиотека. [1]: http://www.prototypejs.org/ diff --git a/doc/ru/object/hasownproperty.md b/doc/ru/object/hasownproperty.md index dd2fa159..e1324e32 100644 --- a/doc/ru/object/hasownproperty.md +++ b/doc/ru/object/hasownproperty.md @@ -2,7 +2,7 @@ Если вам необходимо проверить, определено ли свойство у *самого объекта*, а **не** в его [цепочке прототипов](#object.prototype), вы можете использовать метод `hasOwnProperty`, который все объекты наследуют от `Object.prototype`. -> **Note:** Для проверки наличия свойства **недостаточно** проверять, эквивалентно ли оно `undefined`. Свойство может вполне себе существовать, но при этом ему может быть присвоено значение `undefined`. +> **Примечание:** Для проверки наличия свойства **недостаточно** проверять, эквивалентно ли оно `undefined`. Свойство может вполне себе существовать, но при этом ему может быть присвоено значение `undefined`. `hasOwnProperty` — единственная функция в JavaScript, которая позволяет получить свойства объекта **без обращения** к цепочке его прототипов. From ed7bbccf7553157efe1d1aab3b03161efccf5b57 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sat, 19 Mar 2011 00:05:30 +0200 Subject: [PATCH 069/641] function/general --- doc/ru/function/general.md | 47 ++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/doc/ru/function/general.md b/doc/ru/function/general.md index 5bfd6cd8..847a5f0a 100644 --- a/doc/ru/function/general.md +++ b/doc/ru/function/general.md @@ -1,48 +1,45 @@ -## Function Declarations and Expressions +## Выражения и объявление функций -Functions in JavaScript are first class objects, that means that they can be -passed around like any other value. One common use of this feature is to pass -an *anonymous function* as a callback to another, possibly asynchronous function. +Функции в JavaScript тоже являются объектами (шок, сенсация) — следовательно, их можно передавать и присваивать точно так же, как и любой другой объект. Один из вариантов использования такой возможности — передача *анонимной функции* как функции обратного вызова в другую функцию — к примеру, для ассинхронных вызовов. -### The `function` declaration +### Объявление `function` + // всё просто и привычно function foo() {} -The above function gets [hoisted](#function.scopes) before the execution of the -program starts; thus, it is available *everywhere* in the scope it was *defined* -in, even if called before the actual definition in the source. +В следующем примере описанная функция [резервируется](#function.scopes) перед запуском всего скрипта; за счёт этого она доступна *в любом месте* кода, вне зависимости от того где она *определена* — даже если функция вызывается до её фактического объявления в коде. - foo(); // Works because foo was created before this code runs + + foo(); // сработает, т.к. функция будет создана до выполнения кода function foo() {} -### The `function` expression +### `function` как выражение var foo = function() {}; -This example assigns the unnamed and *anonymous* function to the variable `foo`. +В этом примере безымянная и *анонимная* функция присваивается переменной `foo`. foo; // 'undefined' - foo(); // this raises a TypeError + foo(); // вызовет TypeError var foo = function() {}; -Due to the fact that `var` is a declaration, that hoists the variable name `foo` -before the actual execution of the code starts, `foo` is already defined when -the script gets executed. +Так как в данном примере выражение `var` — это определение функции, переменная с именем `foo` будет заранее зарезервирована перед запуском скрипта (таким образом, `foo` уже будет определена во время его работы). + +Но поскольку присвоения исполняются непосредственно во время работы кода, `foo` по умолчанию будет присвоено значение [`undefined`](#core.undefined) (до обработки строки с определением функции): -But since assignments only happens at runtime, the value of `foo` will default -to [undefined](#core.undefined) before the corresponding code is executed. + var foo; // переменная неявно резервируется + foo; // 'undefined' + foo(); // вызовет TypeError + foo = function() {}; -### Named function expression +### Выражения с именованными фунциями -Another special case is the assignment of named functions. +Существует еще ньюанс, касающийся именованных функций создающихся через присваивание: var foo = function bar() { - bar(); // Works + bar(); // работает } - bar(); // ReferenceError + 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. +Здесь объект `bar` не доступен во внешней области, так как имя `bar` используется только для присвоения переменной `foo`; однако `bar` можно вызвать внутри функции. Такое поведение связано с особенностью работы JavaScript с [пространствами имен](#function.scopes) - имя функции *всегда* доступно в локальной области видимости самой функции. From 3a6513bed746119c451e7c41029fd5e128f6184a Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sat, 19 Mar 2011 11:07:05 +0200 Subject: [PATCH 070/641] function/this --- doc/ru/function/this.md | 87 ++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 53 deletions(-) diff --git a/doc/ru/function/this.md b/doc/ru/function/this.md index f1927e24..a4fa2fdc 100644 --- a/doc/ru/function/this.md +++ b/doc/ru/function/this.md @@ -1,101 +1,84 @@ -## How `this` Works +## Как работает `this` -JavaScript has a different concept of what the special name `this` refers to -than most other programming languages do. There are exactly **five** different -ways in which the value of `this` can be bound in the language. +В JavaScript область ответственности специальной переменной `this` концептуально отличается от того, за что отвечает `this` в других языках программирования. Различают ровно **пять** вариантов того, к чему привязывается this в языке. -### The global scope +### 1. Глобальная область видимости this; -When using `this` in global scope, it will simply refer to the *global* object. +Когда мы используем `this` в глобальной области, она будет просто ссылаться на *глобальный* объект. - -### Calling a function +### 2. Вызов функции foo(); -Here `this` will again refer to the *global* object. +Тут `this` так же ссылается на *глобальный* объект. -> **ES5 Note:** In strict mode, the global case **no longer** exists. -> `this` will instead have the value of `undefined` in that case. +> **ES5 Замечание:** В strict-режиме **теряется** понятие глобальности, поэтому в этом случае `this` будет иметь значение `undefined`. -### Calling a method +### 3. Вызов метода - test.foo(); + test.foo(); -In this example `this` will refer to `test`. +В данном примере `this` ссылается на `test`. -### Calling a constructor +### 4. Вызов конструктора - new foo(); + 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`. +Если перед вызовом функции присутствует ключевое слово `new` то данная функция будет действовать как [конструктор](#function.constructors). Внутри такой функции `this` будет указывать на *новосозданный* `Object`. -### Explicit setting of `this` +### 5. Переопределение `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 + foo.apply(bar, [1, 2, 3]); // массив развернётся в a = 1, b = 2, c = 3 + foo.call(bar, 1, 2, 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. +Когда мы используем методы `call` или `apply` из `Function.prototype`, то внутри вызваемой функции `this` **явным образом** будет присвоено значение первого передаваемого параметра. -As a result, the above example the *method case* does **not** apply, and `this` -inside of `foo` will be set to `bar`. +Исходя из этого, в предыдущем примере (строка с `apply`) *вызов метода* (3 вариант) **не** будет применён, и `this` внутри `foo` будет присвоено `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. +> **Замечание:** `this` **нельзя** использовать внутри литералов `{}` (`Object`) для ссылки на сам объект. Т.е. если мы напишем `var obj = {me: this}`, то `me` не будет ссылаться на `obj`, поскольку `this` присваивается только по одному из пяти описанных правил. -### Common pitfalls +### Наиболее распространенные ошибки -While most of these cases make sense, the first one is to be considered another -mis-design of the language, as it **never** has any practical use. +Хотя большинство из этих примеров наполнены глубоким смыслом, первый из них можно считать ещё одним упущением в самом языке, поскольку он **вообще** не имеет практического применения. Foo.method = function() { function test() { - // this is set to the global object + // this ссылается на глобальный объект } test(); } -A common misconception is that `this` inside of `test` refers to `Foo`, while in -fact it **does not**. +Распространенным заблуждением будет то, что `this` внутри `test` ссылается на `Foo`, но это **не так**. -In order to gain access to `Foo` from within `test` it is necessary to create a -local variable inside of `method` which refers to `Foo`. +Для того, чтобы получить доступ к `Foo` внутри test необходимо создать локальную перменную внутри `method`, которая и будет ссылаться на `Foo`. Foo.method = function() { var that = this; function test() { - // Use that instead of this here + // Здесь используем that вместо this } test(); } -`that` is just a normal 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. +Подходящее имя для переменной - `that`, его часто используют для ссылки на внешний `this`. В комбинации с [замыканиями](#function.closures) `this` можно пробрасывать в глобальную область, или в любой другой объект. -### Assigning methods +> **Замечание** от перев. Кроме `that` так же часто встречаются другие варианты `this_`, `self_` и другие варианты, но лучше принять для себя `that` как стандарт и тогда, возможно, все вокруг будут им пользоваться. -Another thing that does **not** work in JavaScript is function aliasing, that is, -**assigning** a method to a variable. +### Назначение методов + +Еще одной фичей, которая **не** работает в `JavaScript`, является создание псевдонимов для методов, т.е. **присвоение** метода объекта переменной. var test = someObject.methodTest; test(); -Due to the first case `test` now acts like like a plain function call; therefore, -`this` inside it will no longer refer to `someObject`. +Следуя первому правилу `test` вызывается как обычная функция; следовательно `this` внутри него больше не ссылается на `someObject`. -While the late binding of `this` might seem like a bad idea at first, it is in -fact what makes [prototypal inheritance](#object.prototype) work. +Хотя позднее связывание `this` на первый взгляд может показаться плохой идеей, но на самом деле именно благодаря этому работает [наследование прототипов](#object.prototype). function Foo() {} Foo.prototype.method = function() {}; @@ -105,7 +88,5 @@ fact what makes [prototypal inheritance](#object.prototype) work. new Bar().method(); -When `method` gets called on a instance of `Bar`, `this` will now refer to that -very instance. - +В момент, когда будет вызван `method` нового экземляра `Bar`, `this` будет ссылаться на этот самый экземпляр. From c0c814da6a05cdd9ce2a5ecf42534d68b7b18597 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sat, 19 Mar 2011 11:48:17 +0200 Subject: [PATCH 071/641] function/closures --- doc/ru/function/closures.md | 60 +++++++++++++------------------------ 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/doc/ru/function/closures.md b/doc/ru/function/closures.md index 0bb6d66d..d7cc7e0c 100644 --- a/doc/ru/function/closures.md +++ b/doc/ru/function/closures.md @@ -1,11 +1,8 @@ -## Closures and References +## Замыкания и ссылки -One of JavaScript's most powerful features is the availability of *closures*, -this means that scopes **always** keep access to the outer scope they were -defined in. Since the only scoping that JavaScript has is -[function scope](#function.scopes), all functions, by default, act as closures. +Одним из самых мощных инструментов JavaScript'а считаются возмжность создавать *замыкания* — это когда наша область видимости **всегда** имеет доступ к внешней области, в которой она была объявлена. Собственно, единственный механизм работы с областями видимости в JavaScript — это [функции](#function.scopes): т.е. объявляя функцию, вы автоматически реализуете замыкания. -### Emulating private variables +### Эмуляция приватных свойств function Counter(start) { var count = start; @@ -24,69 +21,52 @@ defined in. Since the only scoping that JavaScript has is foo.increment(); foo.get(); // 5 -Here, `Counter` returns **two** closures. The function `increment` as well as -the function `get`. Both of these functions keep a **reference** to the scope of -`Counter` and, therefore, always keep access to the `count` variable that was -defined in that very scope. +В данном примере `Counter` возвращает **два** замыкания: функции `increment` и `get`. Обе эти функции сохраняют **ссылку** на область видимости `Counter` и, соответственно, имеют доступ к переменной `count` из этой самой области. -### 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. +Поскольку в JavaScript нельзя присваивать или ссылаться на области видимости, заполучить `count` извне **не** представляется возможным. Единственным способом взаимодействовать с ним остается использование двух замыканий. var foo = new Counter(4); foo.hack = function() { count = 1337; }; -The above code will **not** change the variable `count` in the scope of `Counter`, -since `foo.hack` was not defined in **that** scope. It will instead create - or -override - the *global* variable `count`. +В приведенном примере мы **не** изменяем переменную `count` в области видимости `Counter`, т.к. `foo.hack` не объявлен в **данной** области. Вместо этого будет создана или перезаписана *глобальная* переменная `count`; -### Closures inside loops +### Замыкания внутри циклов -One often made mistake is to use closures inside of loops, as if they were -copying the value of the loops index variable. +Часто встречается ошибка, когда замыкания используют внутри циклов, передавая переменную индекса внутрь. for(var i = 0; i < 10; i++) { setTimeout(function() { - console.log(i); + console.log(i); }, 1000); } -The above will **not** output the numbers `0` through `9`, but will simply print -the number `10` ten times. +Данный код **не** будет выводить числа с `0` до `9`, вместо этого число `10` будет выведено десять раз. -The *anonymous* function keeps a **reference** to `i` and at the time -`console.log` gets called, the `for loop` has already finished and the value of -`i` as been set to `10`. +*Анонимная* функция сохраняет **ссылку** на `i` и когда будет вызвана функция `console.log`, цикл `for` уже закончит свою работу, а в `i` будет содержаться `10`. -In order to get the desired behavior, it is necessary to create a **copy** of -the value of `i`. +Для получения желаемого результата необходимо создать **копию** переменной `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). +Для того, чтобы скопировать значение индекса из цикла, лучше всего использовать [анонимную функцию](#function.scopes) как обёртку. for(var i = 0; i < 10; i++) { (function(e) { setTimeout(function() { - console.log(e); + 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`. +Анонимная функция-обертка будет вызвана сразу же, и в качестве первого аргумента получит `i`, **значение** которой будет скопировано в параметр `e`. -The anonymous function that gets passed to `setTimeout` now has a reference to -`e`, whose value does **not** get changed by the loop. +Анонимная функция, которая передается в `setTimeout`, теперь содержит ссылку на `e`, значение которой **не** изменяется циклом. -There is another possible way of achieving this; that is to return a function -from the anonymous wrapper, that will then have the same behavior as the code -above. +Еще одним способом реализации является возврат функции из анонимной функции-обертки, поведение этого кода будет таким же, как и в коде из предыдущего примера. for(var i = 0; i < 10; i++) { setTimeout((function(e) { @@ -96,3 +76,5 @@ above. })(i), 1000) } +> **Замечание** от перев. Переменную `e` можно тоже назвать `i`, если вы хотите: это не поменяет поведения кода — внутренняя переменная `i` всё также будет *копией* внешней переменной + From 9e9ec9fb7c69e2b16e21198bbfda711bb134766c Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sat, 19 Mar 2011 12:25:10 +0200 Subject: [PATCH 072/641] array/general --- doc/ru/array/general.md | 46 +++++++++++++---------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/doc/ru/array/general.md b/doc/ru/array/general.md index 7e0ce1d1..a9d2d0a5 100644 --- a/doc/ru/array/general.md +++ b/doc/ru/array/general.md @@ -1,44 +1,29 @@ -## Array Iteration and Properties +##  Итерации по массивам и свойства -Although arrays in JavaScript are objects, there are no good reasons to use -the [`for in loop`](#object.forinloop) in for iteration on them. In fact there -are a number of good reasons **against** the use of `for in` on arrays. +Не смотря на то, что массивы в JavaScript являются объектами, нет достаточных оснований для использования [цикла `for in`](#object.forinloop) для итерации по элементами. Фактически, существует несколько весомых причин **против** использования `for in` в массивах. -> **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**. +> **Замечание:** Массивы в JavaScript **не** являются *ассоциативными массивами*. Для связывания ключей и значений в JavaScript есть только [объекты](#object.general). И при том, что ассоциативные массивы **сохраняют** заданный порядок, объекты **не** делают этого. -Since the `for in` loop enumerates all the properties that are on the prototype -chain and the only way to exclude those properties is to use -[`hasOwnProperty`](#object.hasownproperty), it is already up to **twenty times** -slower than a normal `for` loop. +Во время выполнения `for in` циклически перебираются все свойства объекта, находящиеся в цепочке прототипов. Единственный способ исключить ненужные свойства — использовать [`hasOwnProperty`](#object.hasownproperty), а это **в 20 раз** медленнее обычного цикла `for`. -### Iteration +### Итерирование -In order to achieve the best performance when iterating over arrays, it is best -to use the classic `for` loop. +Для достижения лучшей производительности при итерации по массивам, лучше всего использовать обычный цикл `for`. var list = [1, 2, 3, 4, 5, ...... 100000000]; for(var i = 0, l = list.length; i < l; i++) { console.log(list[i]); } -There is one extra catch in the above example, that is the caching of the -length of the array via `l = list.length`. +В примере выше есть один дополнительный приём, с помощью которого кэшируется величина длины массива: `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. +Не смотря на то, что свойство length определено в самом массиве, поиск этого свойства накладывает дополнительные расходы на каждой итерации цикла. Несмотря на то, что в этом случае новые движки JavaScript **могут** применять оптимизацию, нет способа узнать, будет оптимизирован код на новом движке или нет. -In fact, leaving out the caching may result in the loop being only **half as -fast** as with the cached length. +Фактически, отсутствие кэширования может привести к выполнению цикла в **два раза медленнее**, чем при кэшировании длины -### The `length` property +### Свойство `length` -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. +Несмотря на то, что *геттер* свойства `length` просто возвращает количество элементов содежащихся в массиве, *сеттер* можно использовать для **обрезания** массива. var foo = [1, 2, 3, 4, 5, 6]; foo.length = 3; @@ -47,12 +32,9 @@ elements that are contained in the array, the *setter* can be used to foo.length = 6; foo; // [1, 2, 3] -Assigning a smaller length does truncate the array, but increasing the length -does not have any effect on the array. +Присвоение свойству `length` меньшей величины урезает массив, однако присвоение большего значения не влечёт никакого эффекта. -### 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. +Для оптимального работы кода рекомендуется всегда использовать простой цикл `for` и кэшировать свойство `length`. Использование `for in` с массивами является признаком плохого кода, обладающего предпосылками к ошибкам и может привести к низкой скорости его выполнения. From 3493b464ac853ab4899b7096b166ea85d4a3ceb2 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sat, 19 Mar 2011 13:28:28 +0200 Subject: [PATCH 073/641] array/constructor --- doc/ru/appendix/fromtranslators.md | 4 +++- doc/ru/array/constructor.md | 36 +++++++++++------------------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/doc/ru/appendix/fromtranslators.md b/doc/ru/appendix/fromtranslators.md index b43c061f..5f0e34d4 100644 --- a/doc/ru/appendix/fromtranslators.md +++ b/doc/ru/appendix/fromtranslators.md @@ -2,5 +2,7 @@ Авторы этой документации требуют от читателя не совершать каких-либо ошибок и постоянно следить за качеством пишущегося кода. Мы, как переводчики и опытные программисты на JavaScript рекомендуем *прислушиваться* к этим советам, но при этом не делать из этого *крайность*. Опыт — сын ошибок трудных, и иногда в борьбе с ошибками зарождается намного более детальное понимание предмета. Да, нужно избегать ошибок, но допускать их неосознанно — вполне нормально. -Относитесь с мудростью к тому, что вы пишете — важно знать как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. *Важно* знать как работает [прототипное наследование](#object.prototype), но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код, если у вас есть талант. +Относитесь с мудростью к тому, что вы пишете — важно знать как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. *Важно* знать как работает [прототипное наследование](#object.prototype), но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код — ну, если у вас есть талант. + +Гонка за оптимизацией — это драматично и правильно, но лучше написать работающий код, а потом уже его оптимизировать и искать узкие места, при необходимости. Оптимизацию необходимо делать, если вы видите явные неудобства для пользователя в тех или иных браузерах или у вас супер-крупный проект, которым никогда не помешает оптимизация, или вы работаете с какой-либо сверхтребовательной технологией типа WebGL. Данная документация очень поможет вам в определении этих узких мест. diff --git a/doc/ru/array/constructor.md b/doc/ru/array/constructor.md index 8004168a..56fbb634 100644 --- a/doc/ru/array/constructor.md +++ b/doc/ru/array/constructor.md @@ -1,35 +1,25 @@ -## The `Array` Constructor +## Конструктор `Array` -Since the `Array` constructor is ambiguous in how it deals with its parameters, -it is highly recommended to always use the array literals - `[]` notation - -when creating new arrays. +Так как в конструкторе `Array` есть некоторая двусмысленность кастательно его параметров, настоятельно рекомендуется всегда использовать синтаксис литеральной нотации — `[]` — при создании массивов. - [1, 2, 3]; // Result: [1, 2, 3] - new Array(1, 2, 3); // Result: [1, 2, 3] + [1, 2, 3]; // Результат: [1, 2, 3] + new Array(1, 2, 3); // Результат: [1, 2, 3] - [3]; // Result: [3] - new Array(3); // Result: [] - new Array('3') // Result: ['3'] + [3]; // Результат: [3] + new Array(3); // Результат: [] + new Array('3') // Результат: ['3'] -In cases when there is only one argument passed to the `Array` constructor, -and that argument is a `Number`, the constructor will return a new *sparse* -array with the `length` property set to the value of the argument. It should be -noted that **only** the `length` property of the new array will be set this way, -the actual indexes of the array will not be initialized. +В случае, когда в конструктор `Array` передается один аргумент и этот аргумент имеет тип `Number`, конструктор возвращает новый, *запоненный случайными значениями*, массив, имеющий длину равную значению переданного аргумента. Стоит заметить, что в этом случае будет установлено только свойство `length` нового массива, фактически индексы массива не будут проинициализированы. var arr = new Array(3); - arr[1]; // undefined - 1 in arr; // false, the index was not set + arr[1]; // не определён, undefined + 1 in arr; // false, индекс не был установлен -The behavior of being able to set the length of the array upfront only comes in -handy in a few cases, like repeating a string, in which it avoids the use of a -`for loop` code. +Поведение, которое позволяет изначально установить только размер массива может пригодиться лишь в нескольких случаях, таких как повторение строк, за счёт чего избегается использование цикла `for loop`. new Array(count + 1).join(stringToRepeat); -### In conclusion +### Заключение -The use of the `Array` constructor should be avoided as much as possible. -Literals are definitely preferred. They are shorter and have a clearer syntax; -therefore, they also increase the readability of the code. +Использование конструктора `Array` нужно избегать, насколько это возможно. Литералы определённо предпочтительнее — это краткая запись и она имеет более понятный синтаксис, так что при этом даже улучшается читабельность кода. From c85e2a5d66ed9cd5db28d0b5210df5b843c6f0e1 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sat, 19 Mar 2011 14:02:14 +0200 Subject: [PATCH 074/641] modified gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 80f4f3b3..b918c19b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /site/de /site/ru *.md~ +*.src.md From c4e453111cfd2561da4d3c7603b44060525e5344 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sat, 19 Mar 2011 23:18:39 +0200 Subject: [PATCH 075/641] function/arguments --- doc/ru/function/arguments.md | 85 ++++++++++++++---------------------- 1 file changed, 32 insertions(+), 53 deletions(-) diff --git a/doc/ru/function/arguments.md b/doc/ru/function/arguments.md index 915afdf0..a2d68c84 100644 --- a/doc/ru/function/arguments.md +++ b/doc/ru/function/arguments.md @@ -1,45 +1,36 @@ -## The `arguments` Object +## Объект `arguments` + +В области видимости любой функции в JavaScript есть доступ к специальной переменной `arguments`. Эта переменная содержит в себе список всех аргументов? переданных данной функции ghb dspjdt. 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. +> **Замечание:** В случае, если переменная `arguments` уже была объявлена в области видимости функции либо путём присвоения через выражение `var`, либо являясь формальным параметром, объект `arguments` не будет создан. -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`. +Объект `arguments` **не** является наследником `Array`. Он, конечно же, очень похож на массив, и даже содержит свойство `length` — но он не наследует `Array.prototype`, а представляет собой `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. +По этой причине, у объекта `arguments` **отсутствуют** стандартные методы массивов, такие как `push`, `pop` или `slice`. Хотя итерация с использованием обычного цикла `for` по агрументам работает вполне корректно, вам придётся конвертировать этот объект в настоящий массив типа `Array`, чтобы применять к нему стандартные методы массивов. -### Converting to an array +### Конвертация в массив -The code below will return a new `Array` containing all the elements of the -`arguments` object. +Указанный код вернёт новый массив типа `Array`, содержащий все элементы объекта `arguments`. Array.prototype.slice.call(arguments); -This conversion is **slow**, it is **not recommended** to use it in performance -critical sections of code. +Эта конвертация занимает **много времени** и **не рекомендуется** использовать её в критических частях кода. -### Passing arguments +### Передача аргументов -The following is the recommended way of passing arguments from one function to -another. +Ниже представлен рекомендуемый способ передачи аргументов из одной функции в другую. function foo() { bar.apply(null, arguments); } function bar(a, b, c) { - // do stuff here + // делаем здесь что-либо } -Another trick is to use both `call` and `apply` together to create fast, unbound -wrappers. +Другой трюк — использовать и `call` и `apply` вместе, чтобы быстро создать несвязанную обёртку: function Foo() {} @@ -47,26 +38,25 @@ wrappers. console.log(this, a, b, c); }; - // Create an unbound version of "method" - // It takes the parameters: this, arg1, arg2...argN + // Создаём несвязанную версию "method" + // Она принимает параметры: this, arg1, arg2...argN Foo.method = function() { - // Result: Foo.prototype.method.call(this, arg1, arg2... argN) + // Результат: Foo.prototype.method.call(this, arg1, arg2... argN) Function.call.apply(Foo.prototype.method, arguments); + }; -### Formal parameters and arguments indexes +### Формальные аргументы и индексы аргументов -The `arguments` object creates *getter* and *setter* functions for both its -properties as well as the function's formal parameters. +Объект `arguments` создаёт по *геттеру* и *сеттеру* и для всех своих свойств и для формальных параметров функции. -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. +В результате, изменение формального параметра также изменит значение соответствующего свойства объекта `arguments` и наоборот. function foo(a, b, c) { arguments[0] = 2; - a; // 2 + a; // 2 b = 4; arguments[1]; // 4 @@ -77,43 +67,32 @@ of the corresponding property on the `arguments` object, and the other way aroun } foo(1, 2, 3); -### Performance myths and truths +### Мифы и правда о производительности -The `arguments` object is always created with the only two exceptions being the -cases where it is declared as a name inside of a function or one of its formal -parameters. It does not matter whether it is used or not. +Объект `arguments` создаётся во всех случаях, лишь за двумя исключениямии — когда он переопределён по имени внутри функции или когда одним из её параметров является переменная с таким именем. Не важно, используется он при этом или нет. -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. +> **ES5 Замечание:** Эти *геттеры* и *сеттеры* не создаются в strict-режиме. -However, there is one case which will drastically reduce the performance in -modern JavaScript engines. That case is the use of `arguments.callee`. +Однако, есть один момент, который может радикально понизить производительность современных движков JavaScript. Этот момент — использование `arguments.callee`. function foo() { - arguments.callee; // do something with this function object - arguments.callee.caller; // and the calling function object + arguments.callee; // сделать что-либо с этим объектом функции + arguments.callee.caller; // и с вызвавшим его объектом функции } function bigLoop() { for(var i = 0; i < 100000; i++) { - foo(); // Would normally be inlined... + foo(); // При обычных условиях должна бы была быть развёрнута... } } -In the above code, `foo` can no longer be a subject to [inlining][1] since it -needs to know about both itself and its caller. This not only defeats possible -performance gains that would arise from inlining, it also breaks encapsulation -since the function may now be dependent on a specific calling context. +В коде выше, `foo` больше не может [быть развёрнута][1], потому что ей необходима ссылка и на себя, и на вызвавший объект. Это не только кладёт на лопатки механизм развёртывания, но и нарушает принцип инкапсуляции, поскольку функция становится зависима от конкретного контекста вызова. -It is **highly recommended** to **never** make use of `arguments.callee` or any of -its properties. +**Крайне рекомендуется** **никогда** не использовать `arguments.callee` или его свойства. -> **ES5 Note:** In strict mode, `arguments.callee` will throw a `TypeError` since -> its use has been deprecated. +> **ES5 Замечание:** В strict-режиме `arguments.callee` выбросит `TypeError`, поскольку его использование принято устаревшим. [1]: http://en.wikipedia.org/wiki/Inlining - From 64aeeba0b1be17816fe8fb41e8b1800039b694d8 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Sun, 20 Mar 2011 13:15:47 +0800 Subject: [PATCH 076/641] Removed unused color plugin --- site/javascript/plugin.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/site/javascript/plugin.js b/site/javascript/plugin.js index e0204fbd..9461358a 100644 --- a/site/javascript/plugin.js +++ b/site/javascript/plugin.js @@ -1,9 +1,3 @@ -/* - * jQuery Color Animations - * Copyright 2007 John Resig - * Released under the MIT and GPL licenses. - */(function(a){a.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(d,e){a.fx.step[e]=function(a){a.colorInit||(a.start=c(a.elem,e),a.end=b(a.end),a.colorInit=true),a.elem.style[e]="rgb("+[Math.max(Math.min(parseInt(a.pos*(a.end[0]-a.start[0])+a.start[0]),255),0),Math.max(Math.min(parseInt(a.pos*(a.end[1]-a.start[1])+a.start[1]),255),0),Math.max(Math.min(parseInt(a.pos*(a.end[2]-a.start[2])+a.start[2]),255),0)].join(",")+")"}});function b(b){var c;if(b&&b.constructor==Array&&b.length==3)return b;if(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b))return[parseInt(c[1]),parseInt(c[2]),parseInt(c[3])];if(c=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b))return[parseFloat(c[1])*2.55,parseFloat(c[2])*2.55,parseFloat(c[3])*2.55];if(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b))return[parseInt(c[1],16),parseInt(c[2],16),parseInt(c[3],16)];if(c=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b))return[parseInt(c[1]+c[1],16),parseInt(c[2]+c[2],16),parseInt(c[3]+c[3],16)];if(c=/rgba\(0, 0, 0, 0\)/.exec(b))return d.transparent;return d[a.trim(b).toLowerCase()]}function c(c,d){var e;do{e=a.curCSS(c,d);if(e!=""&&e!="transparent"||a.nodeName(c,"body"))break;d="backgroundColor"}while(c=c.parentNode);return b(e)}var d={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]}})(jQuery); - /** * jQuery.browser.mobile (http://detectmobilebrowser.com/) * From 8bc16331aa0e10f7e29c109f09a28613835531c1 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sun, 20 Mar 2011 16:48:13 +0200 Subject: [PATCH 077/641] function/constructors --- doc/ru/function/arguments.md | 19 ++++---- doc/ru/function/constructors.md | 77 +++++++++++---------------------- 2 files changed, 33 insertions(+), 63 deletions(-) diff --git a/doc/ru/function/arguments.md b/doc/ru/function/arguments.md index a2d68c84..963917a9 100644 --- a/doc/ru/function/arguments.md +++ b/doc/ru/function/arguments.md @@ -1,9 +1,6 @@ ## Объект `arguments` -В области видимости любой функции в JavaScript есть доступ к специальной переменной `arguments`. Эта переменная содержит в себе список всех аргументов? переданных данной функции ghb dspjdt. - -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. +В области видимости любой функции в JavaScript есть доступ к специальной переменной `arguments`. Эта переменная содержит в себе список всех аргументов, переданных данной функции. > **Замечание:** В случае, если переменная `arguments` уже была объявлена в области видимости функции либо путём присвоения через выражение `var`, либо являясь формальным параметром, объект `arguments` не будет создан. @@ -17,7 +14,7 @@ This variable holds a list of all the arguments that were passed to the function Array.prototype.slice.call(arguments); -Эта конвертация занимает **много времени** и **не рекомендуется** использовать её в критических частях кода. +Эта конвертация занимает **много времени** и использовать её в критических частях кода **не рекомендуется**. ### Передача аргументов @@ -27,7 +24,7 @@ This variable holds a list of all the arguments that were passed to the function bar.apply(null, arguments); } function bar(a, b, c) { - // делаем здесь что-либо + // делаем здесь что-нибудь } Другой трюк — использовать и `call` и `apply` вместе, чтобы быстро создать несвязанную обёртку: @@ -69,9 +66,9 @@ This variable holds a list of all the arguments that were passed to the function ### Мифы и правда о производительности -Объект `arguments` создаётся во всех случаях, лишь за двумя исключениямии — когда он переопределён по имени внутри функции или когда одним из её параметров является переменная с таким именем. Не важно, используется он при этом или нет. +Объект `arguments` создаётся во всех случаях, лишь за двумя исключениями — когда он переопределён внутри функции (по имени) или когда одним из её параметров является переменная с таким именем. Не важно, используется при этом сам объект или нет. -*Геттеры* и *сеттеры* создаются **всегда**; так что, их использование почти никак не влияет на производительность. +*Геттеры* и *сеттеры* создаются **всегда**; так что, их использование практически никак не влияет на производительность. > **ES5 Замечание:** Эти *геттеры* и *сеттеры* не создаются в strict-режиме. @@ -88,11 +85,11 @@ This variable holds a list of all the arguments that were passed to the function } } -В коде выше, `foo` больше не может [быть развёрнута][1], потому что ей необходима ссылка и на себя, и на вызвавший объект. Это не только кладёт на лопатки механизм развёртывания, но и нарушает принцип инкапсуляции, поскольку функция становится зависима от конкретного контекста вызова. +В коде выше, `foo` больше не может [быть развёрнута][1], потому что для работы ей необходима ссылка и на себя и на вызвавший её объект. Это не только кладёт на лопатки механизм развёртывания, но и нарушает принцип инкапсуляции, поскольку функция становится зависима от конкретного контекста вызова. -**Крайне рекомендуется** **никогда** не использовать `arguments.callee` или его свойства. +**Крайне не рекомендуется** использовать `arguments.callee` или какое-либо из его свойств. **Никогда**. -> **ES5 Замечание:** В strict-режиме `arguments.callee` выбросит `TypeError`, поскольку его использование принято устаревшим. +> **ES5 Замечание:** В strict-режиме использование `arguments.callee` породит `TypeError`, поскольку его использование принято устаревшим. [1]: http://en.wikipedia.org/wiki/Inlining diff --git a/doc/ru/function/constructors.md b/doc/ru/function/constructors.md index 52935451..c5006cf1 100644 --- a/doc/ru/function/constructors.md +++ b/doc/ru/function/constructors.md @@ -1,15 +1,10 @@ -## 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. +Создание конструкторов в JavaScript также отличается от большинства других языков. Любая функция вызванная с использованием ключевого слова `new` будет конструктором. -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. +Внутри конструктора (вызываемой функции) `this` будет указывать на новосозданный `Object`. [Прототипом](#object.prototype) этого **нового** объекта будет `prototype` функции, которая была вызвана в качестве коструктора. -If the function that was called has no explicit `return` statement, then it -implicitly returns the value of `this` - the new object. +Если вызываемая функция не имеет явного возврата посредством `return`, то вернётся `this` — этот новый объект. function Foo() { this.bla = 1; @@ -21,16 +16,14 @@ implicitly returns the value of `this` - the new object. var test = new Foo(); -The above calls `Foo` as constructor and sets the `prototype` of the newly -created object to `Foo.prototype`. +В этом примере `Foo` вызывается как конструктор и прототип созданного объекта будет привязан к `Foo.prototype`. -In case of an explicit `return` statement the function returns the value -specified that statement, **but only** if the return value is an `Object`. +В случае, когда функция в явном виде возвращает некое значение используя `return`, то мы получим именно его, **но только** если возвращаемое значение представляет собой `Object`. function Bar() { return 2; } - new Bar(); // a new object + new Bar(); // новый объект function Test() { this.value = 2; @@ -39,23 +32,20 @@ specified that statement, **but only** if the return value is an `Object`. foo: 1 }; } - new Test(); // the returned object + new Test(); // возвращённый объект -When the `new` keyword is omitted, the function will **not** return a new object. +Если же опустить ключевое слово `new`, то функция **не** будет возвращать каких-либо объектов. function Foo() { - this.bla = 1; // gets set on the global object + this.bla = 1; // устанавливается глобальному объекту } Foo(); // undefined -While the above example might still appear to work in some cases, due to the -workings of [`this`](#function.this) in JavaScript, it will use the -*global object* as the value of `this`. +Приведенный пример в некоторых случаях может всё-таки сработать, это связано с поведением [`this`](#function.this) в JavaScript — он будет восприниматься парсером как *глобальный объект*. -### Factories +### Фабрики -In order to be able to omit the `new` keyword, the constructor function has to -explicitly return a value. +Если хотите избавится от `new`, напишите конструктор, возвращающий значение используя `return`. function Bar() { var value = 1; @@ -72,25 +62,17 @@ explicitly return a value. new Bar(); Bar(); -Both calls to `Bar` return the exact same thing, a newly create object which -has a property called `method` on it, that is a -[Closure](#function.closures). +В обоих случаях при вызове `Bar` мы получим один и тот же результат — новый объект со свойством `method` (спасибо [замыканию](#function.closures) за это). -It is also to note that the call `new Bar()` does **not** affect the prototype -of the returned object. While the prototype will be set on the newly created -object, `Bar` never returns that new object. +Так же следует заметить, что вызов `new Bar()` никак **не** связан с прототипом возвращаемого объекта. Хоть прототип и назначается всем новосозданным объектам, но `Bar` никогда не возвращает этот новый объект. -In the above example, there is no functional difference between using and -not using the `new` keyword. +В предыдущем примере нет функциональных отличий между вызовом конструктора с или без `new`. +### Создание объектов используя фабрики -### Creating new objects via factories +Часто рекомендуется **не** использовать `new`, поскольку если вы его забудете, это может привести к багам. -An often made recommendation is to **not** use `new` since forgetting its use -may lead to bugs. - -In order to create new object, one should rather use a factory and construct a -new object inside of that factory. +Чтобы создать новый объект, лучше использовать фабрику и создать новый объект *внутри* этой фабрики. function Foo() { var obj = {}; @@ -107,22 +89,13 @@ new object inside of that factory. return obj; } -While the above is robust against a missing `new` keyword and certainly makes -the use of [private variables](#function.closures) easier, it comes with some -downsides. +Хотя данный пример и сработает, если вы забыли ключевое слово `new` и благодаря ему легче работать с [приватными переменными](#function.closures), у него есть несколько недостатков - 1. It uses more memory since the created objects do **not** share the methods - on a prototype. - 2. In order to inherit the factory needs to copy all the methods from another - object or put that object on the prototype of the new object. - 3. Dropping the prototype chain just because of a left out `new` keyword - somehow goes against the spirit of the language. + 1. Он использует больше памяти, поскольку созданные объекты **не** хранят методы в прототипе и соответственно для каждого нового объекта создаётся копия каждого метода. + 2. Чтобы эмулировать наследование, фабрике нужно скопировать все методы из другого объекта или установить прототипом нового объекта старый. + 3. Разрыв цепи прототипов просто по причине забытого ключевого слова `new` идёт в разрез с духом языка. -### In conclusion +### Заключение -While omitting the `new` keyword might lead to bugs, it is certainly **not** a -reason to drop the use of prototypes altogether. In the end it comes down to -which solution is better suited for the needs of the application, it is -especially important to choose a specific style of object creation **and stick** -with it. +Хотя забытое ключевое слово `new` и может привести к багам, это точно **не** причина отказываться от использования прототипов. В конце концов, полезнее решить какой из способов лучше совпадает с требованиями приложения, очень важно выбрать один из стилей создания объектов и **не изменять** ему. From 431a95a08f203031c8252d9caaf3cd8550c121a0 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sun, 20 Mar 2011 19:04:40 +0200 Subject: [PATCH 078/641] types/casting --- doc/ru/function/constructors.md | 2 +- doc/ru/types/casting.md | 72 ++++++++++++++------------------- 2 files changed, 32 insertions(+), 42 deletions(-) diff --git a/doc/ru/function/constructors.md b/doc/ru/function/constructors.md index c5006cf1..c86ed8e5 100644 --- a/doc/ru/function/constructors.md +++ b/doc/ru/function/constructors.md @@ -97,5 +97,5 @@ ### Заключение -Хотя забытое ключевое слово `new` и может привести к багам, это точно **не** причина отказываться от использования прототипов. В конце концов, полезнее решить какой из способов лучше совпадает с требованиями приложения, очень важно выбрать один из стилей создания объектов и **не изменять** ему. +Хотя забытое ключевое слово `new` и может привести к багам, это точно **не** причина отказываться от использования прототипов. В конце концов, полезнее решить какой из способов лучше совпадает с требованиями приложения: очень важно выбрать один из стилей создания объектов и после этого **не изменять** ему. diff --git a/doc/ru/types/casting.md b/doc/ru/types/casting.md index 34b320d0..aa7c05d2 100644 --- a/doc/ru/types/casting.md +++ b/doc/ru/types/casting.md @@ -1,63 +1,54 @@ -## 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 +## Приведение типов + +JavaScript *слабо типизированный* язык, поэтому *преобразование типов* будет применяться **везде**, где возможно. + + // Эти равенства — истинны + new Number(10) == 10; // Number.toString() преобразуется + // обратно в число + + 10 == '10'; // Strings преобразуется в Number + 10 == '+10 '; // Ещё чуток строко-безумия + 10 == '010'; // и ещё + isNaN(null) == false; // null преобразуется в 0 + // который конечно NaN + + // Эти равенства — ложь 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. +> **ES5 Замечание:** Числовые литералы, которые начинаются с 0 интерпретируются как восьмеричные (Base 8). В 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. +Для того, чтобы избежать этого, **настоятельно** рекомендуется использовать [оператор строгого равенства](#types.equality). Хотя это позволяет избежать многих распространенных ошибок, существует ещё много дополнительных вопросов, которые возникают из-за слабости типизации JavaScript. -### 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. +Конструкторы встроенных типов, например, `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 + new Number(10) === 10; // False, Object и Number + Number(10) === 10; // True, Number и Number + new Number(10) + 0 === 10; // True, из-за неявного преобразования -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. +Использование встроенных типов, например `Number`, с конструктором создаёт новый экземпляр объекта Number, но использование без ключевого слова `new` создаст функцию `Number`, которая будет вести себя, как конвертер. -In addition, having literals or non-object values in there will result in even -more type coercion. +Кроме того, наличие литералов или переменных, которые не являются объектами приведет к еще большему насилию над типами. -The best option is to cast to one of the three possible types **explicitly**. +Лучший вариант — это **явное** приведение к одному из трех возможных типов. -### Casting to a string +### Приведение к строке '' + 10 === '10'; // true -By prepending a empty string a value can easily be casted to a string. +Путём добавления в начале пустой строки, значение легко приводится к строке. -### Casting to a number +### Приведение к числовому типу +'10' === 10; // true -Using the **unary** plus operator it is possible to cast to a number. +Используя **унарный** оператор плюс можно преобразовать значение в число. -### Casting to a boolean +### Приведение к булеву типу -By using the **not** operator twice, a value can be converted a boolean. +Используя оператор **not (!)** дважды, значение может быть приведено к логическому (булеву) типу. !!'foo'; // true !!''; // false @@ -67,4 +58,3 @@ By using the **not** operator twice, a value can be converted a boolean. !!{}; // true !!true; // true - From 7d6087ad6864109f6d7bb8a435a4b98a5518e440 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sun, 20 Mar 2011 19:31:03 +0200 Subject: [PATCH 079/641] types/equality --- doc/ru/appendix/fromtranslators.md | 6 ++-- doc/ru/types/equality.md | 47 +++++++++++------------------- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/doc/ru/appendix/fromtranslators.md b/doc/ru/appendix/fromtranslators.md index 5f0e34d4..95edf966 100644 --- a/doc/ru/appendix/fromtranslators.md +++ b/doc/ru/appendix/fromtranslators.md @@ -2,7 +2,9 @@ Авторы этой документации требуют от читателя не совершать каких-либо ошибок и постоянно следить за качеством пишущегося кода. Мы, как переводчики и опытные программисты на JavaScript рекомендуем *прислушиваться* к этим советам, но при этом не делать из этого *крайность*. Опыт — сын ошибок трудных, и иногда в борьбе с ошибками зарождается намного более детальное понимание предмета. Да, нужно избегать ошибок, но допускать их неосознанно — вполне нормально. -Относитесь с мудростью к тому, что вы пишете — важно знать как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. *Важно* знать как работает [прототипное наследование](#object.prototype), но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код — ну, если у вас есть талант. +Относитесь с **мудростью** к тому, что вы пишете — *важно* знать как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. *Важно* знать как работает [прототипное наследование](#object.prototype), но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код — ну, если у вас есть талант. -Гонка за оптимизацией — это драматично и правильно, но лучше написать работающий код, а потом уже его оптимизировать и искать узкие места, при необходимости. Оптимизацию необходимо делать, если вы видите явные неудобства для пользователя в тех или иных браузерах или у вас супер-крупный проект, которым никогда не помешает оптимизация, или вы работаете с какой-либо сверхтребовательной технологией типа WebGL. Данная документация очень поможет вам в определении этих узких мест. +К примеру, в статье про [сравнение объектов](#types.equality) авторы рекомендуют использовать только оператор строгого неравенства `===`. Но мы считаем, если вы уверены и осознали, что оба сравниваемых операнда имеют один тип, вы имеете право опустить последний символ `=`. Вы вольны применять строгое неравенство только если вы не уверены в типах операндов (`=== undefined` — это полезный приём). Так в вашем коде будут опасные и безопасные области, но при этом по коду будет явно видно, где вы рассчитываете на переменные одинаковых типов, а где позволяете пользователю вольности. + +Гонка за оптимизацией — это драматично и правильно, но лучше написать работающий и понятный вам код, а потом уже его оптимизировать и искать узкие места, при необходимости. Оптимизацию необходимо делать, если вы видите явные неудобства для пользователя в тех или иных браузерах или у вас есть супер-крупный проект, которым никогда мешает оптимизация, или вы работаете с какой-либо сверхтребовательной технологией типа WebGL. Данная документация очень поможет вам в определении этих узких мест. diff --git a/doc/ru/types/equality.md b/doc/ru/types/equality.md index 1bc4c9cf..baa95218 100644 --- a/doc/ru/types/equality.md +++ b/doc/ru/types/equality.md @@ -1,14 +1,13 @@ -## Equality and comparisons +##  Равенство и сравнение -JavaScript has two different ways of comparing the values of objects for equality. +JavaScript имеет 2 различных способа сравнения значений объектов на равенство. -### The equals operator +### Оператор сравнения -The equals operator consists of two equal signs: `==` +Оператор сравнения состоит из **двух** символов равенства: `==` + +*Слабая типизированность* языка JavaScript подразумевает **приведение** обеих переменных к **одному типу** для того, чтобы произвести сравнение. -JavaScript features *weak typing*, that means, that the equals operator -**coerces** types in order to compare them. - "" == "0" // false 0 == "" // true 0 == "0" // true @@ -19,20 +18,15 @@ JavaScript features *weak typing*, that means, that the equals operator 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 equals operator +### Оператор строгого равенства -The strict equals operator consists of **three** equal signs: `===` +Оператор строгого равенства состоит из **трёх** символов равенства: `===` -Other than the normal equals operator, the strict equals operator does **not** -perform type coercion between its operands. +В отличие от обычного оператора равенства, оператор строгого равенства **не** выполняет приведение типов между операндами. "" === "0" // false 0 === "" // false @@ -44,14 +38,11 @@ perform type coercion between its operands. null === undefined // false " \t\r\n" === 0 // false -The above results are a lot clearer and allow for early breakage of code. This -hardens code to a certain degree and also gives performance improvements in case -the operands are of different types. +Результаты выше более понятны и позволяют быстрее выявлять ошибки в коде. Это в определённой степени улучшает код, а также дает прирост производительности в случае, если операнды имеют различные типы. -### Comparing objects +### Сравнение объектов -While both `==` and `===` are stated as **equality** operators, they behave -different when at least one of their operands happens to be an `Object`. +Хотя оба оператора `==` и `===` заявлены как операторы равенства, они ведут себя по-разному, когда хотя бы один из операндов является `Object`. {} === {}; // false new String('foo') === 'foo'; // false @@ -59,13 +50,9 @@ different when at least one of their operands happens to be an `Object`. 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 a pointer comparison in C do. +Здесь оба операнда сравниваются на **идентичность**, а **не** равенство; то есть, будет проверяться, являются ли операнды одним **экземпляром** объекта, так же как делает `is` в Python и сравниваются указатели в С. -### In conclusion +### Заключение -It is highly recommended to only use the **strict equals** 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. +Крайне рекомендуется использовать только операторы **строгого равенства**. В случае, когда намечается преобразование типов, нужно сделать [явное приведение](#types.casting) и не оставлять их на совести языковых хитростей с преобразованиями. From 2d6c991a77bdedd42f302d9efc1f176c45072fda Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sun, 20 Mar 2011 19:59:47 +0200 Subject: [PATCH 080/641] types/typeof --- doc/ru/appendix/fromtranslators.md | 2 +- doc/ru/types/typeof.md | 67 ++++++++++-------------------- 2 files changed, 23 insertions(+), 46 deletions(-) diff --git a/doc/ru/appendix/fromtranslators.md b/doc/ru/appendix/fromtranslators.md index 95edf966..655897a7 100644 --- a/doc/ru/appendix/fromtranslators.md +++ b/doc/ru/appendix/fromtranslators.md @@ -4,7 +4,7 @@ Относитесь с **мудростью** к тому, что вы пишете — *важно* знать как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. *Важно* знать как работает [прототипное наследование](#object.prototype), но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код — ну, если у вас есть талант. -К примеру, в статье про [сравнение объектов](#types.equality) авторы рекомендуют использовать только оператор строгого неравенства `===`. Но мы считаем, если вы уверены и осознали, что оба сравниваемых операнда имеют один тип, вы имеете право опустить последний символ `=`. Вы вольны применять строгое неравенство только если вы не уверены в типах операндов (`=== undefined` — это полезный приём). Так в вашем коде будут опасные и безопасные области, но при этом по коду будет явно видно, где вы рассчитываете на переменные одинаковых типов, а где позволяете пользователю вольности. +К примеру, в статье про [сравнение объектов](#types.equality) авторы рекомендуют использовать только оператор строгого неравенства `===`. Но мы считаем, если вы уверены и осознали, что оба сравниваемых операнда имеют один тип, вы имеете право опустить последний символ `=`. Вы вольны применять строгое неравенство только если вы не уверены в типах операндов (`!== undefined` — это полезный приём). Так в вашем коде будут опасные и безопасные области, но при этом по коду будет явно видно, где вы рассчитываете на переменные одинаковых типов, а где позволяете пользователю вольности. Гонка за оптимизацией — это драматично и правильно, но лучше написать работающий и понятный вам код, а потом уже его оптимизировать и искать узкие места, при необходимости. Оптимизацию необходимо делать, если вы видите явные неудобства для пользователя в тех или иных браузерах или у вас есть супер-крупный проект, которым никогда мешает оптимизация, или вы работаете с какой-либо сверхтребовательной технологией типа WebGL. Данная документация очень поможет вам в определении этих узких мест. diff --git a/doc/ru/types/typeof.md b/doc/ru/types/typeof.md index 200b94d4..99724139 100644 --- a/doc/ru/types/typeof.md +++ b/doc/ru/types/typeof.md @@ -1,21 +1,14 @@ -## The `typeof` operator +## Оператор `typeof` -The `typeof` operator (together with -[`instanceof`](#types.instanceof)) is probably the biggest -design flaw of JavaScript, as it is near of being **completely broken**. +Оператор `typeof` (вместе с [`instanceof`](#types.instanceof)) — это, вероятно, самая большая недоделка в JavaScript, поскольку, похоже, он **поломан более чем полностью**. -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. +Хотя `instanceof` еще имеет ограниченное применение, `typeof` на самом деле имеет *только один* практический случай применения, который при этом **не** является проверкой типа объекта. -> **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. +> **Замечаение:** Хотя для вызова `typeof` также можно использовать синтаксис функции, т.е. `typeof(obj)`, на самом деле это не функция. Двойные круглые скобки будут работать нормально и возвращаемое значение будет использоваться как операнд оператора `typeof`. Но функции `typeof` — **нет**. -### The JavaScript type table +### Таблица типов JavaScript - Value Class Type + Значение Класс Тип ------------------------------------- "foo" String string new String("foo") String object @@ -28,60 +21,44 @@ 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) + /abc/g RegExp object (function в Nitro/V8) + new RegExp("meow") RegExp object (function в 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. +В таблице выше *Тип* представляет собой значение, возвращаемое оператором `typeof`. Как хорошо видно, это значение может быть абсолютно любым, но не логичным. -The *Class* refers to the value of the internal `[[Class]]` property of an object. +*Класс* представляет собой значение внутреннего свойства `[[Class]]` объекта. -> **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`. +> **Из спецификации:** Значением `[[Class]]` может быть одна из следующих строк: `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`. +Для того, чтобы получить значение `[[Class]]` необходимо вызвать метод `toString` у `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`. +Спецификация предоставляет только один способ доступа к значению `[[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 -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. +В примере выше `Object.prototype.toString` вызывается со значением [this](#function.this), являющимся объектом, значение [[Class]] которого нужно получить. -> **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. +> **ES5 Замечание:** Для удобства в ECMAScript 5 возвращаемое значение `Object.prototype.toString `для `null` и `undefined` было изменено с `Object` на `Null` и `Undefined` соответственно. -### 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 +Выше проверяется, было ли `foo` действительно объявлено или нет; просто обращение к переменной приведёт к `ReferenceError`. Это единственное, чем на самом деле полезен `typeof`. -In order to check the type of an object, it is highly recommended to use -`Object.prototype.toString`; as this is the only reliable way of doing so. -As shown in the above type table, some return values of `typeof` are not defined -in the specification; thus, they can differ across various implementations. +### Заключение -Unless checking whether a variable is defined, `typeof` should be avoided at -**all costs**. +Для проверки типа объекта настоятельно рекомендуется использовать` Object.prototype.toString` — это единственный надежный способ. Как показано выше в таблице типов, некоторые возвращаемые `typeof` значения не определены в спецификации: таким образом, они могут отличаться в различных реализациях. +Кроме проверки, была ли определена переменная, `typeof` следует избегать **во что бы то ни стало**. From 876498f6bcb613d96dcd059d362e534fe48e9b1d Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sun, 20 Mar 2011 20:55:28 +0200 Subject: [PATCH 081/641] types/instanceof --- doc/ru/types/instanceof.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/doc/ru/types/instanceof.md b/doc/ru/types/instanceof.md index 6fe0d63e..d1d76ee1 100644 --- a/doc/ru/types/instanceof.md +++ b/doc/ru/types/instanceof.md @@ -1,10 +1,8 @@ -## The `instanceof` operator +## Оператор `instanceof` -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). +Оператор `instanceof` сравнивает конструкторы двух операндов. Это полезно только когда сравниваются пользовательские объекты. Использование на встроенных типах почти так же бесполезно, как оператор [оператор typeof](#types.typeof). -### Comparing custom objects +### Сравнение пользовательских объектов function Foo() {} function Bar() {} @@ -13,12 +11,12 @@ nearly as useless as the [typeof operator](#types.typeof). 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, + // но не экземпляра Foo Bar.prototype = Foo; new Bar() instanceof Foo; // false -### Using `instanceof` with native types +### Использование `instanceof` со встроенными типами new String('foo') instanceof String; // true new String('foo') instanceof Object; // true @@ -26,13 +24,9 @@ nearly as useless as the [typeof operator](#types.typeof). 'foo' instanceof String; // false 'foo' instanceof Object; // false -One important thing to note here is, that `instanceof` does not work on objects -that origin from different JavaScript contexts (e.g. different documents -in a web browser), since their constructors will not be the exact same object. +Здесь надо отметить одну важную вещь: `instanceof` не работает на объектах, которые происходят из разных контекстов JavaScript (например, из различных документы в web-браузере), так как их конструкторы не будут конструкторами *тех самых* объектов. -### In conclusion +### Заключение -The `instanceof` operator should **only** be used when dealing with custom made -objects that origin from the same JavaScript context. Just like the -[`typeof`](#types.typeof) operator, every other use of it should be **avoided**. +Оператор instanceof должен использоваться **только** при обращении к пользовательским объектам, происходящим из одного контекста JavaScript. Так же, как и в случае оператора `typeof`, любое другое использование необходимо **избегать**. From 9744ddc383690521fa690b3c173d7054c7e2505b Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Sun, 20 Mar 2011 23:48:47 +0200 Subject: [PATCH 082/641] function/scopes --- doc/ru/appendix/fromtranslators.md | 8 +- doc/ru/function/scopes.md | 181 ++++++++++++----------------- 2 files changed, 78 insertions(+), 111 deletions(-) diff --git a/doc/ru/appendix/fromtranslators.md b/doc/ru/appendix/fromtranslators.md index 655897a7..4e69ca5f 100644 --- a/doc/ru/appendix/fromtranslators.md +++ b/doc/ru/appendix/fromtranslators.md @@ -2,9 +2,13 @@ Авторы этой документации требуют от читателя не совершать каких-либо ошибок и постоянно следить за качеством пишущегося кода. Мы, как переводчики и опытные программисты на JavaScript рекомендуем *прислушиваться* к этим советам, но при этом не делать из этого *крайность*. Опыт — сын ошибок трудных, и иногда в борьбе с ошибками зарождается намного более детальное понимание предмета. Да, нужно избегать ошибок, но допускать их неосознанно — вполне нормально. -Относитесь с **мудростью** к тому, что вы пишете — *важно* знать как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. *Важно* знать как работает [прототипное наследование](#object.prototype), но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код — ну, если у вас есть талант. +К примеру, в статье про [сравнение объектов](#types.equality) авторы настоятельно рекомендуют использовать *только* оператор строгого неравенства `===`. Но мы считаем, что если вы уверены и осознали, что оба сравниваемых операнда имеют один тип, вы имеете право опустить последний символ `=`. Вы вольны применять строгое неравенство только в случаях, когда вы не уверены в типах операндов (`!== undefined` — это полезный приём). Так в вашем коде будут опасные и безопасные области, но при этом по коду будет явно видно, где вы рассчитываете на переменные одинаковых типов, а где позволяете пользователю вольности. + +Функцию [`setInterval`](#other.timeouts) тоже можно использовать, если вы стопроцентно уверены, что код внутри неё будет исполняться как минимум в три раза быстрее переданного ей интервала. -К примеру, в статье про [сравнение объектов](#types.equality) авторы рекомендуют использовать только оператор строгого неравенства `===`. Но мы считаем, если вы уверены и осознали, что оба сравниваемых операнда имеют один тип, вы имеете право опустить последний символ `=`. Вы вольны применять строгое неравенство только если вы не уверены в типах операндов (`!== undefined` — это полезный приём). Так в вашем коде будут опасные и безопасные области, но при этом по коду будет явно видно, где вы рассчитываете на переменные одинаковых типов, а где позволяете пользователю вольности. +С другой стороны, [использование `var`](#function.scopes) и грамотная [расстановка точек с запятой](#core.semicolon) — обязательные вещи, халатное отношение к которым никак не может быть оправдано — в осознанном пропуске `var` (если только вы не переопределяете глобальный объект браузера... хотя *зачем*?) или точки с запятой нет никакого смысла. + +Относитесь с **мудростью** к тому, что вы пишете — *важно* знать как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. *Важно* знать как работает [прототипное наследование](#object.prototype), но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код — ну, если у вас есть талант. Гонка за оптимизацией — это драматично и правильно, но лучше написать работающий и понятный вам код, а потом уже его оптимизировать и искать узкие места, при необходимости. Оптимизацию необходимо делать, если вы видите явные неудобства для пользователя в тех или иных браузерах или у вас есть супер-крупный проект, которым никогда мешает оптимизация, или вы работаете с какой-либо сверхтребовательной технологией типа WebGL. Данная документация очень поможет вам в определении этих узких мест. diff --git a/doc/ru/function/scopes.md b/doc/ru/function/scopes.md index 95735b07..bee98112 100644 --- a/doc/ru/function/scopes.md +++ b/doc/ru/function/scopes.md @@ -1,88 +1,69 @@ -## Scopes and Namespaces +## Области видимости и пространства имён -Although JavaScript deals fine with the syntax of two matching curly -braces for blocks, it does **not** support block scope; hence, all that is left -is in the language is *function scope*. +Хотя JavaScript нормально понимает синтаксис двух фигурных скобок, окружающих блок, он **не** поддерживает блочную область видимости; всё что остаются в языке — *область видимости функций*. - function test() { // a scope - for(var i = 0; i < 10; i++) { // not a scope - // count + function test() { // область видимости + for(var i = 0; i < 10; i++) { // не область видимости + // считаем } 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. +> **Замечание:** Нотация `{...}` будет интерпретирована как блочное выражение, а **не** как литерал объекта, если она используется не присваивании, операторе `return` или в качестве функции. Это замечание, в паре с [автоматической вставкой точек с запятой](#core.semicolon), может привести к чрезвычайно хитрым ошибкам. -There are also no distinct namespaces in JavaScript, that means that everything -gets defined in one *globally shared* namespace. +Также JavaScript не знает ничего о различиях в пространствах имён: всё определяется в *глобально доступном* пространстве имён. -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`. +Каждый раз, когда JavaScript обнаруживает ссылку на переменную, он будет искать её всё выше и выше по областям видимости, пока не найдёт её. В случае, если он достигнет глобальной области видимости и не найдет запрошенное имя и там, он выбросит `ReferenceError`. -### The bane of global variables +### Проклятие глобальных переменных - // script A + // скрипт A foo = '42'; - // script B + // скрипт 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. +Вышеприведённые два скрипта **не** приводят к одному результату. Скрипт A определяет переменную по имени `foo` в *глобальной* области видимости, а скрипт B определяет `foo` в текущей области видимости. -Again, that is **not** at all the *same effect*, not using `var` can have major -implications. +Повторимся, это вообще **не** *тот же самый эффект*, если вы не используете `var` — то вы в большой опасности. - // global scope + // глобальная область видимости var foo = 42; function test() { - // local scope + // локальная область видимости foo = 21; } test(); foo; // 21 -Leaving out the `var` statement inside the function `test` will override the -value of `foo`. While this might not seem like a big deal at first, having -thousands of lines of JavaScript and not using `var` will introduce horrible and -hard to track down bugs. - - // global scope - var items = [/* some list */]; +Из-за того что оператор `var` опущен внутри функции, фунция `test` перезапишет значение `foo`. Это поначалу может показаться не такой уж и большой проблемой, но если у вас имеется тысяча строк JavaScript-кода и вы не используете `var`, то вам на пути встретятся страшные и трудноотлаживаемые ошибки, и это не шутка. + + // глобальная область видимости + var items = [/* какой-то список */]; 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! + // область видимости subLoop + for(i = 0; i < 10; i++) { // пропущенный оператор var + // делаем волшебные вещи! } } - -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 +Внешний цикл прекратит работу сразу после первого вызова `subLoop`, поскольку `subLoop` перезаписывает глобальное значение переменной `i`. Использование `var` во втором цикле `for` могло бы вас легко избавить от этой ошибки. **Никогда** не забывайте использовать `var`, если только влияние на внешнюю область видимости не является тем, что вы *намерены получить*. + +### Локальные переменные -The only source for local variables in JavaScript are -[function](#function.general) parameters and variables that were declared via the -`var` statement. +Единственный источник локальных переменных в JavaScript - это параметры [функций](#function.general) и переменные, объявленные с использованием оператора `var`. - // global scope + // глобальная область видимости var foo = 1; var bar = 2; var i = 2; function test(i) { - // local scope of the function test + // локальная область видимости для функции test i = 5; var foo = 3; @@ -90,13 +71,11 @@ The only source for local variables in JavaScript are } 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. +В то время как `foo` и `i` — локальные переменные в области видимости функции `test`, присвоение `bar` переопределит значение одноимённой глобальной переменной. -### Hoisting +### Высасывание -JavaScript **hoists** declarations. This means that both `var` statements and -`function` declarations will be moved to the top of their enclosing scope. +JavaScript **высасывает** определения. Это значит, что оба определения с использованием `var` и определение `function` будут перенесены наверх заключающей их области видимости. bar(); var bar = function() {}; @@ -115,16 +94,15 @@ JavaScript **hoists** declarations. This means that both `var` statements and } } -The above code gets transformed before any execution is started. JavaScript moves -the `var` statements as well as the `function` declarations to the top of the -nearest surrounding scope. +Этот код трансформируется ещё перед исполнением. JavaScript перемещает операторы `var` и определение `function` наверх ближайшей оборачивающей области видимости. - // var statements got moved here - var bar, someValue; // default to 'undefined' + // выражения с var переместились сюда + var bar, someValue; // по умолчанию - 'undefined' - // the function declartion got moved up too + // определение функции тоже переместилось function test(data) { - var goo, i, e; // missing block scope moves these here + var goo, i, e; // потерянная блочная область видимости + // переместилась сюда if (false) { goo = 1; @@ -136,96 +114,81 @@ nearest surrounding scope. } } - bar(); // fails with a TypeError since bar is still 'undefined' - someValue = 42; // assignments are not affected by hoisting + bar(); // вылетает с ошибкой TypeError, + // поскольку bar всё ещё 'undefined' + someValue = 42; // присвоения не подвержены высасыванию 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. +Потерянная область видимости блока не только переместит операторы `var` вовне циклов и их тел, но и сделает результаты некоторых конструкций с `if` неинтуитивными. -In the original code the `if` statement seemed to modify the *global -variable* `goo`, while actually it modifies the *local variable* - after hoisting -has been applied. +В исходном коде оператор `if` изменял *глобальную переменную* `goo`, когда как оказалось он изменяет *локальную переменную* — в результате работы высасывания. -Without the knowledge about *hoisting*, below code might seem to raise a +Если вы не знаниями о *высасывании*, то вы можете посчитать, нижеприведённый код должен породить `ReferenceError`. - // check whether SomeImportantThing has been initiliazed + // проверить, проинициализована ли SomeImportantThing if (!SomeImportantThing) { var SomeImportantThing = {}; } -But of course, the above works due to the fact that the `var` statement is being -moved to the top of the *global scope*. +Но конечно же этот код работает: из-за того, что оператор `var` был перемещён наверх *глобальной области видимости* var SomeImportantThing; - // other code might initiliaze SomeImportantThing here, or not + // другой код может инициализировать здесь переменную SomeImportantThing, + // ну или нет - // 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*. +Все области видимости в JavaScript, включая *глобальную области видимости*, содержат специальную, определённую внутри них, переменную [`this`](#function.this), которая ссылается на *текущий объект*. -Function scopes also have the name [`arguments`](#function.arguments) defined in -them which contains the arguments that were passed to a function. +Области видимости функций также содержат внутри себя переменную [`arguments`](#function.arguments), которая содержит аргументы, переданные в функцию. -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: +Например, когда JavaScript пытается получить доступ к переменной `foo` в области видимости функции, он будет искать её по имени в такой последовательности: - 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. + 1. Если в текущей области видимости есть выражение `var foo`, использовать его. + 2. Если один из параметров функции называется `foo`, использовать его. + 3. Если функциия сама называется `foo`, использовать её. + 4. Перейти на одну область видимости выше и начать с **п. 1** -> **Note:** Having a parameter called `arguments` will **prevent** the creation -> of the default `arguments` object. +> **Замечание:** Наличие параметра с именем `arguments` **не позволит** движку создать объект `arguments`, создающийся по умолчанию. -### 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*. +Частое последствие наличия только одного глобального пространства имён — проблемы с перекрытием имён переменных. В JavaScript эту проблему легко избежать, используя *анонимные обёртки*. (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. +Безымянные функции являются [выражениями](#function.general); поэтому, чтобы вы имели возможность их выполнить, они сперва должны быть разобраны. - ( // evaluate the function inside the paranthesis + ( // разобрать функцию внутри скобок function() {} - ) // and return the function object - () // call the result of the evaluation + ) // и вернуть объект функции + () // вызвать результат разбора -There are other ways for evaluating and calling the function expression; which, -while different in syntax, do behave the exact same way. +Есть другие способы разбора и последующего вызова выражения с функцией; они, хоть и различаются в синтаксисе, действуют одинаково. - // Two other ways + // Два других способа +function(){}(); (function(){}()); -### In conclusion +### Заключение -It is recommended to always use an *anonymous wrapper* for encapsulating code in -its own namespace. This does not only protect code against name clashes, it -also allows for better modularization of programs. +Рекомендуется всегда использовать *анонимную обёртку* для заключения кода в его собственное пространство имён. Это не только защищает код от совпадений имён, но и позваоляет создавать более модульные программы. -Additionally, the use of global variables is considered **bad practice**. **Any** -use of them indicates badly written code that is prone to errors and hard to maintain. +Важно добавить, что использование глобальных переменных считается **плохой практикой**. **Любое** их использование демонстрирует плохое качество кода и может привести к трудноуловимым ошибкам. From 4092c5d7d76605525ca47609e4c2e4b96333e695 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Mon, 21 Mar 2011 01:20:38 +0200 Subject: [PATCH 083/641] some orpho/language corrections --- doc/ru/array/constructor.md | 4 ++-- doc/ru/array/general.md | 10 +++++----- doc/ru/core/eval.md | 8 ++++---- doc/ru/core/semicolon.md | 6 +++--- doc/ru/core/undefined.md | 6 +++--- doc/ru/function/arguments.md | 4 ++-- doc/ru/function/closures.md | 2 +- doc/ru/function/constructors.md | 20 ++++++++++---------- doc/ru/function/general.md | 2 +- doc/ru/function/scopes.md | 22 +++++++++++----------- doc/ru/function/this.md | 10 +++++----- doc/ru/intro/translators.md | 2 +- doc/ru/object/forinloop.md | 6 +++--- doc/ru/object/general.md | 4 ++-- doc/ru/object/hasownproperty.md | 2 +- doc/ru/object/prototype.md | 10 +++++----- doc/ru/other/timeouts.md | 18 +++++++++--------- doc/ru/types/casting.md | 12 ++++++------ doc/ru/types/equality.md | 4 ++-- doc/ru/types/instanceof.md | 6 +++--- doc/ru/types/typeof.md | 12 ++++++------ 21 files changed, 85 insertions(+), 85 deletions(-) diff --git a/doc/ru/array/constructor.md b/doc/ru/array/constructor.md index 56fbb634..34f15976 100644 --- a/doc/ru/array/constructor.md +++ b/doc/ru/array/constructor.md @@ -1,6 +1,6 @@ ## Конструктор `Array` -Так как в конструкторе `Array` есть некоторая двусмысленность кастательно его параметров, настоятельно рекомендуется всегда использовать синтаксис литеральной нотации — `[]` — при создании массивов. +Так как в конструкторе `Array` есть некоторая двусмысленность касающаяся его параметров, настоятельно рекомендуется при создании массивов всегда использовать синтаксис литеральной нотации — `[]`. [1, 2, 3]; // Результат: [1, 2, 3] new Array(1, 2, 3); // Результат: [1, 2, 3] @@ -9,7 +9,7 @@ new Array(3); // Результат: [] new Array('3') // Результат: ['3'] -В случае, когда в конструктор `Array` передается один аргумент и этот аргумент имеет тип `Number`, конструктор возвращает новый, *запоненный случайными значениями*, массив, имеющий длину равную значению переданного аргумента. Стоит заметить, что в этом случае будет установлено только свойство `length` нового массива, фактически индексы массива не будут проинициализированы. +В случае, когда в конструктор `Array` передаётся один аргумент и этот аргумент имеет тип `Number`, конструктор возвращает новый, *заполненный случайными значениями*, массив, имеющий длину равную значению переданного аргумента. Стоит заметить, что в этом случае будет установлено только свойство `length` нового массива, индексы массива фактически не будут проинициализированы. var arr = new Array(3); arr[1]; // не определён, undefined diff --git a/doc/ru/array/general.md b/doc/ru/array/general.md index a9d2d0a5..8350eefa 100644 --- a/doc/ru/array/general.md +++ b/doc/ru/array/general.md @@ -1,6 +1,6 @@ ##  Итерации по массивам и свойства -Не смотря на то, что массивы в JavaScript являются объектами, нет достаточных оснований для использования [цикла `for in`](#object.forinloop) для итерации по элементами. Фактически, существует несколько весомых причин **против** использования `for in` в массивах. +Несмотря на то, что массивы в JavaScript являются объектами, нет достаточных оснований для использования [цикла `for in`](#object.forinloop) для итерации по элементам массива. Фактически, существует несколько весомых причин **против** использования `for in` в массивах. > **Замечание:** Массивы в JavaScript **не** являются *ассоциативными массивами*. Для связывания ключей и значений в JavaScript есть только [объекты](#object.general). И при том, что ассоциативные массивы **сохраняют** заданный порядок, объекты **не** делают этого. @@ -17,13 +17,13 @@ В примере выше есть один дополнительный приём, с помощью которого кэшируется величина длины массива: `l = list.length`. -Не смотря на то, что свойство length определено в самом массиве, поиск этого свойства накладывает дополнительные расходы на каждой итерации цикла. Несмотря на то, что в этом случае новые движки JavaScript **могут** применять оптимизацию, нет способа узнать, будет оптимизирован код на новом движке или нет. +Несмотря на то, что свойство `length` определено в самом массиве, поиск этого свойства накладывает дополнительные расходы на каждой итерации цикла. Пусть в этом случае новые движки JavaScript теоретически **могут** применить оптимизацию, но нет никакого способа узнать, будет оптимизирован код на новом движке или нет. Фактически, отсутствие кэширования может привести к выполнению цикла в **два раза медленнее**, чем при кэшировании длины ### Свойство `length` -Несмотря на то, что *геттер* свойства `length` просто возвращает количество элементов содежащихся в массиве, *сеттер* можно использовать для **обрезания** массива. +Хотя *геттер* свойства `length` просто возвращает количество элементов содежащихся в массиве, *сеттер* можно использовать для **обрезания** массива. var foo = [1, 2, 3, 4, 5, 6]; foo.length = 3; @@ -32,9 +32,9 @@ foo.length = 6; foo; // [1, 2, 3] -Присвоение свойству `length` меньшей величины урезает массив, однако присвоение большего значения не влечёт никакого эффекта. +Присвоение свойству `length` меньшей величины урезает массив, однако присвоение большего значения не даст никакого эффекта. ### Заключение -Для оптимального работы кода рекомендуется всегда использовать простой цикл `for` и кэшировать свойство `length`. Использование `for in` с массивами является признаком плохого кода, обладающего предпосылками к ошибкам и может привести к низкой скорости его выполнения. +Для оптимальной работы кода рекомендуется всегда использовать простой цикл `for` и кэшировать свойство `length`. Использование `for in` с массивами является признаком плохого кода, обладающего предпосылками к ошибкам и может привести к низкой скорости его выполнения. diff --git a/doc/ru/core/eval.md b/doc/ru/core/eval.md index 795d2ee1..3750281f 100644 --- a/doc/ru/core/eval.md +++ b/doc/ru/core/eval.md @@ -1,6 +1,6 @@ ## Почему нельзя использовать `eval` -Функция `eval` выполняет строку кода JavaScript в локальном области видимости. +Функция `eval` выполняет строку кода JavaScript в локальной области видимости. var foo = 1; function test() { @@ -11,7 +11,7 @@ test(); // 3 foo; // 1 -Но `eval` исполняется в локальной области видимости только когда он вызывается **напрямую** *и при этом* имя вызываемой функции именно `eval`. +Но `eval` исполняется в локальной области видимости только тогда, когда он вызывается **напрямую** *и при этом* имя вызываемой функции именно `eval`. var foo = 1; function test() { @@ -31,9 +31,9 @@ ### Проблемы с безопасностью -Кроме всего прочего, функция `eval` — это проблема в безопасности, поскольку исполняется **любой** переданный в неё код, **никогда** не следует использовать её со строками из неизвестных или недоверительных источников. +Кроме всего прочего, функция `eval` — это проблема в безопасности, поскольку исполняется **любой** переданный в неё код; **никогда** не следует использовать её со строками из неизвестных или недоверительных источников. ### Заключение -Никогда не стоит использовать `eval`: любое применение такого кода поднимает вопросы о качестве его работы, производительности и безопасности. Если вдруг для работы вам необходим `eval`, эта часть должна тут же ставиться под сомнение и **не** должна использоваться в первую очередь — нужно использовать *лучший способ* , которому не требуются вызовы `eval`. +Никогда не стоит использовать `eval`: любое применение такого кода поднимает вопросы о качестве его работы, производительности и безопасности. Если вдруг для работы вам необходим `eval`, эта часть должна тут же ставиться под сомнение и **не** должна использоваться в первую очередь — необходимо найти *лучший способ* , которому не требуются вызовы `eval`. diff --git a/doc/ru/core/semicolon.md b/doc/ru/core/semicolon.md index ce0af172..083dbc45 100644 --- a/doc/ru/core/semicolon.md +++ b/doc/ru/core/semicolon.md @@ -2,7 +2,7 @@ Хоть JavaScript и имеет синтаксис, подобный языкам семейства C, он при этом **не** принуждает вас ставить точки с запятой в исходном коде — вы всегда можете их опустить. -При этом JavaScript — не язык без точек с запятой, они на самом деле нужны ему, чтобы он мог разобраться в вашем коде. Поэтому парсер JavaScript **автоматически** вставляет их в те места, где сталкивается с ошибкой парсинга из-за отсутствия точки с запятой. +При этом JavaScript — не язык без точек с запятой, они на самом деле нужны ему, чтобы он мог разобраться в вашем коде. Поэтому парсер JavaScript **автоматически** вставляет их в те места, где сталкивается с ошибкой парсинга из-за их отсутствия. var foo = function() { } // ошибка разбора, ожидается точка с запятой @@ -77,7 +77,7 @@ })(window); //<- вставлена -> **Замечание:** Парсер JavaScript некорректно обрабатывает оператор return, за которым следует новая строка; кстати, причина может быть и не в автоматической вставке точек с запятой, но это в любом случае нежелательный побочный эффект +> **Замечание:** Парсер JavaScript некорректно обрабатывает оператор `return`, за которым следует новая строка; кстати, причина может быть и не в автоматической вставке точек с запятой, но это в любом случае нежелательный побочный эффект Парсер радикально подменил поведение изначального кода, а в определённых случаях он сделал **абсолютно неправильные выводы**. @@ -96,5 +96,5 @@ ### Заключение -Настоятельно рекомендуем **никогда** не забывать ставить или опускать точку с запятой; так же рекомендуется оставлять скобки на одной строке с соответствующим оператором и никогда не опускать их для выражений с использованием `if` / `else`. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят от изменения поведения этого кода, произведённого парсером втихую. +Настоятельно рекомендуем **никогда** не забывать ставить точку с запятой; так же рекомендуется оставлять скобки на одной строке с соответствующим оператором и никогда не опускать их для выражений с использованием `if` / `else`. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят от изменения поведения кода, произведённого парсером втихую. diff --git a/doc/ru/core/undefined.md b/doc/ru/core/undefined.md index 7e2fd6d9..dd1d16b2 100644 --- a/doc/ru/core/undefined.md +++ b/doc/ru/core/undefined.md @@ -21,9 +21,9 @@ ### Обработка изменений значения `undefined` -Поскольку глобальная переменная `undefined` содержит копию настоящего *значения* `undefined`, присвоение этой переменной нового значения **не** изменяет значения или *типа* `undefined`. +Поскольку глобальная переменная `undefined` содержит копию настоящего *значения* `undefined`, присвоение этой переменной нового значения **не** изменяет значения *типа* `undefined`. -Но при этом, чтобы сравнить что-либо со значением `undefined` прежде важно получить значение самой переменной `undefined`. +Но при этом, чтобы сравнить что-либо со *значением* `undefined` прежде нужно получить значение самой *переменной* `undefined`. Чтобы защитить код от переопределения переменной `undefined`, часто используется техника [анонимной обёртки](#function.scopes), которая использует отсутствующий аргумент. @@ -34,7 +34,7 @@ })('Hello World', 42); -Другой способ достичь того же эффекта — использовать опредение внутри обёртки. +Другой способ достичь того же эффекта — использовать определение внутри обёртки. var undefined = 123; (function(something, foo) { diff --git a/doc/ru/function/arguments.md b/doc/ru/function/arguments.md index 963917a9..64085e8d 100644 --- a/doc/ru/function/arguments.md +++ b/doc/ru/function/arguments.md @@ -66,7 +66,7 @@ ### Мифы и правда о производительности -Объект `arguments` создаётся во всех случаях, лишь за двумя исключениями — когда он переопределён внутри функции (по имени) или когда одним из её параметров является переменная с таким именем. Не важно, используется при этом сам объект или нет. +Объект `arguments` создаётся во всех случаях, лишь за двумя исключениями — когда он переопределён внутри функции (по имени) или когда одним из её параметров является переменная с таким именем. Неважно, используется при этом сам объект или нет. *Геттеры* и *сеттеры* создаются **всегда**; так что, их использование практически никак не влияет на производительность. @@ -85,7 +85,7 @@ } } -В коде выше, `foo` больше не может [быть развёрнута][1], потому что для работы ей необходима ссылка и на себя и на вызвавший её объект. Это не только кладёт на лопатки механизм развёртывания, но и нарушает принцип инкапсуляции, поскольку функция становится зависима от конкретного контекста вызова. +В коде выше, функция `foo` не может [быть развёрнута][1] (а могла бы), потому что для корректной работы ей необходима ссылка и на себя и на вызвавший её объект. Это не только кладёт на лопатки механизм развёртывания, но и нарушает принцип инкапсуляции, поскольку функция становится зависима от конкретного контекста вызова. **Крайне не рекомендуется** использовать `arguments.callee` или какое-либо из его свойств. **Никогда**. diff --git a/doc/ru/function/closures.md b/doc/ru/function/closures.md index d7cc7e0c..82835fcb 100644 --- a/doc/ru/function/closures.md +++ b/doc/ru/function/closures.md @@ -1,6 +1,6 @@ ## Замыкания и ссылки -Одним из самых мощных инструментов JavaScript'а считаются возмжность создавать *замыкания* — это когда наша область видимости **всегда** имеет доступ к внешней области, в которой она была объявлена. Собственно, единственный механизм работы с областями видимости в JavaScript — это [функции](#function.scopes): т.е. объявляя функцию, вы автоматически реализуете замыкания. +Одним из самых мощных инструментов JavaScript'а считаются возможность создавать *замыкания* — это такой приём, когда наша область видимости **всегда** имеет доступ к внешней области, в которой она была объявлена. Собственно, единственный механизм работы с областями видимости в JavaScript — это [функции](#function.scopes): т.о. объявляя функцию, вы автоматически реализуете замыкания. ### Эмуляция приватных свойств diff --git a/doc/ru/function/constructors.md b/doc/ru/function/constructors.md index c86ed8e5..4d60226c 100644 --- a/doc/ru/function/constructors.md +++ b/doc/ru/function/constructors.md @@ -1,6 +1,6 @@ ## Конструктор -Создание конструкторов в JavaScript также отличается от большинства других языков. Любая функция вызванная с использованием ключевого слова `new` будет конструктором. +Создание конструкторов в JavaScript также отличается от большинства других языков. Любая функция, вызванная с использованием ключевого слова `new`, будет конструктором. Внутри конструктора (вызываемой функции) `this` будет указывать на новосозданный `Object`. [Прототипом](#object.prototype) этого **нового** объекта будет `prototype` функции, которая была вызвана в качестве коструктора. @@ -16,9 +16,9 @@ var test = new Foo(); -В этом примере `Foo` вызывается как конструктор и прототип созданного объекта будет привязан к `Foo.prototype`. +В этом примере `Foo` вызывается в виде конструктора, следовательно прототип созданного объекта будет привязан к `Foo.prototype`. -В случае, когда функция в явном виде возвращает некое значение используя `return`, то мы получим именно его, **но только** если возвращаемое значение представляет собой `Object`. +В случае, когда функция в явном виде возвращает некое значение используя `return`, то в результате выполнения конструктора мы получим именно его, **но только** если возвращаемое значение представляет собой `Object`. function Bar() { return 2; @@ -34,18 +34,18 @@ } new Test(); // возвращённый объект -Если же опустить ключевое слово `new`, то функция **не** будет возвращать каких-либо объектов. +Если же опустить ключевое слово `new`, то функция **не** будет возвращать никаких объектов. function Foo() { this.bla = 1; // устанавливается глобальному объекту } Foo(); // undefined -Приведенный пример в некоторых случаях может всё-таки сработать, это связано с поведением [`this`](#function.this) в JavaScript — он будет восприниматься парсером как *глобальный объект*. +Этот пример в некоторых случаях всё-таки может сработать: это связано с поведением [`this`](#function.this) в JavaScript — он будет восприниматься парсером как *глобальный объект*. ### Фабрики -Если хотите избавится от `new`, напишите конструктор, возвращающий значение используя `return`. +Если хотите избавится от необходимости использования `new`, напишите конструктор, возвращающий значение посредством `return`. function Bar() { var value = 1; @@ -66,11 +66,11 @@ Так же следует заметить, что вызов `new Bar()` никак **не** связан с прототипом возвращаемого объекта. Хоть прототип и назначается всем новосозданным объектам, но `Bar` никогда не возвращает этот новый объект. -В предыдущем примере нет функциональных отличий между вызовом конструктора с или без `new`. +В предыдущем примере нет функциональных отличий между вызовом конструктора с оператором `new` или без него. -### Создание объектов используя фабрики +### Создание объектов с использованием фабрик -Часто рекомендуется **не** использовать `new`, поскольку если вы его забудете, это может привести к багам. +Часто рекомендуют **не** использовать `new`, поскольку если вы его забудете, это может привести к ошибкам. Чтобы создать новый объект, лучше использовать фабрику и создать новый объект *внутри* этой фабрики. @@ -93,7 +93,7 @@ 1. Он использует больше памяти, поскольку созданные объекты **не** хранят методы в прототипе и соответственно для каждого нового объекта создаётся копия каждого метода. 2. Чтобы эмулировать наследование, фабрике нужно скопировать все методы из другого объекта или установить прототипом нового объекта старый. - 3. Разрыв цепи прототипов просто по причине забытого ключевого слова `new` идёт в разрез с духом языка. + 3. Разрыв цепочки прототипов просто по причине забытого ключевого слова `new` идёт в разрез с духом языка. ### Заключение diff --git a/doc/ru/function/general.md b/doc/ru/function/general.md index 847a5f0a..f8012d1a 100644 --- a/doc/ru/function/general.md +++ b/doc/ru/function/general.md @@ -1,6 +1,6 @@ ## Выражения и объявление функций -Функции в JavaScript тоже являются объектами (шок, сенсация) — следовательно, их можно передавать и присваивать точно так же, как и любой другой объект. Один из вариантов использования такой возможности — передача *анонимной функции* как функции обратного вызова в другую функцию — к примеру, для ассинхронных вызовов. +Функции в JavaScript тоже являются объектами (шок, сенсация) — следовательно, их можно передавать и присваивать точно так же, как и любой другой объект. Одним из вариантов использования такой возможности является передача *анонимной функции* как функции обратного вызова в другую функцию — к примеру, для ассинхронных вызовов. ### Объявление `function` diff --git a/doc/ru/function/scopes.md b/doc/ru/function/scopes.md index bee98112..48c6e846 100644 --- a/doc/ru/function/scopes.md +++ b/doc/ru/function/scopes.md @@ -1,6 +1,6 @@ ## Области видимости и пространства имён -Хотя JavaScript нормально понимает синтаксис двух фигурных скобок, окружающих блок, он **не** поддерживает блочную область видимости; всё что остаются в языке — *область видимости функций*. +Хотя JavaScript нормально понимает синтаксис двух фигурных скобок, окружающих блок, он **не** поддерживает блочную область видимости; всё что остаётся на этот случай в языке — *область видимости функций*. function test() { // область видимости for(var i = 0; i < 10; i++) { // не область видимости @@ -9,11 +9,11 @@ console.log(i); // 10 } -> **Замечание:** Нотация `{...}` будет интерпретирована как блочное выражение, а **не** как литерал объекта, если она используется не присваивании, операторе `return` или в качестве функции. Это замечание, в паре с [автоматической вставкой точек с запятой](#core.semicolon), может привести к чрезвычайно хитрым ошибкам. +> **Замечание:** Нотация `{...}` будет интерпретирована как блочное выражение, а **не** как литерал объекта, если она не используется в присваивании, операторе `return` или в качестве функции. Это замечание, вкупе с [автоматической расстановкой точек с запятой](#core.semicolon), может привести к чрезвычайно хитрым ошибкам. Также JavaScript не знает ничего о различиях в пространствах имён: всё определяется в *глобально доступном* пространстве имён. -Каждый раз, когда JavaScript обнаруживает ссылку на переменную, он будет искать её всё выше и выше по областям видимости, пока не найдёт её. В случае, если он достигнет глобальной области видимости и не найдет запрошенное имя и там, он выбросит `ReferenceError`. +Каждый раз, когда JavaScript обнаруживает ссылку на переменную, он будет искать её всё выше и выше по областям видимости, пока не найдёт её. В случае, если он достигнет глобальной области видимости и не найдет запрошенное имя и там тоже, он ругнётся `ReferenceError`. ### Проклятие глобальных переменных @@ -25,7 +25,7 @@ Вышеприведённые два скрипта **не** приводят к одному результату. Скрипт A определяет переменную по имени `foo` в *глобальной* области видимости, а скрипт B определяет `foo` в текущей области видимости. -Повторимся, это вообще **не** *тот же самый эффект*, если вы не используете `var` — то вы в большой опасности. +Повторимся, это вообще **не** *тот же самый эффект*. Если вы не используете `var` — то вы в большой опасности. // глобальная область видимости var foo = 42; @@ -36,7 +36,7 @@ test(); foo; // 21 -Из-за того что оператор `var` опущен внутри функции, фунция `test` перезапишет значение `foo`. Это поначалу может показаться не такой уж и большой проблемой, но если у вас имеется тысяча строк JavaScript-кода и вы не используете `var`, то вам на пути встретятся страшные и трудноотлаживаемые ошибки, и это не шутка. +Из-за того что оператор `var` опущен внутри функции, фунция `test` перезапишет значение `foo`. Это поначалу может показаться не такой уж и большой проблемой, но если у вас имеется тысяча строк JavaScript-кода и вы не используете `var`, то вам на пути встретятся страшные и трудноотлаживаемые ошибки — и это не шутка. // глобальная область видимости var items = [/* какой-то список */]; @@ -123,9 +123,9 @@ JavaScript **высасывает** определения. Это значит, Потерянная область видимости блока не только переместит операторы `var` вовне циклов и их тел, но и сделает результаты некоторых конструкций с `if` неинтуитивными. -В исходном коде оператор `if` изменял *глобальную переменную* `goo`, когда как оказалось он изменяет *локальную переменную* — в результате работы высасывания. +В исходном коде оператор `if` изменял *глобальную переменную* `goo`, когда, как оказалось, он изменяет *локальную переменную* — в результате работы высасывания. -Если вы не знаниями о *высасывании*, то вы можете посчитать, нижеприведённый код должен породить +Если вы не знакомы с *высасываниями*, то можете посчитать, что нижеприведённый код должен породить `ReferenceError`. // проверить, проинициализована ли SomeImportantThing @@ -138,7 +138,7 @@ JavaScript **высасывает** определения. Это значит, var SomeImportantThing; // другой код может инициализировать здесь переменную SomeImportantThing, - // ну или нет + // а может и нет // убедиться, что она всё ещё здесь if (!SomeImportantThing) { @@ -158,11 +158,11 @@ JavaScript **высасывает** определения. Это значит, 3. Если функциия сама называется `foo`, использовать её. 4. Перейти на одну область видимости выше и начать с **п. 1** -> **Замечание:** Наличие параметра с именем `arguments` **не позволит** движку создать объект `arguments`, создающийся по умолчанию. +> **Замечание:** Наличие параметра функции с именем `arguments` **не позволит** движку создать объект `arguments`, создающийся по умолчанию. ### Пространства имён -Частое последствие наличия только одного глобального пространства имён — проблемы с перекрытием имён переменных. В JavaScript эту проблему легко избежать, используя *анонимные обёртки*. +Нередкое последствие наличия только одного глобального пространства имён — проблемы с перекрытием имён переменных. В JavaScript эту проблему легко избежать, используя *анонимные обёртки*. (function() { // самостоятельно созданное "пространство имён" @@ -180,7 +180,7 @@ JavaScript **высасывает** определения. Это значит, ) // и вернуть объект функции () // вызвать результат разбора -Есть другие способы разбора и последующего вызова выражения с функцией; они, хоть и различаются в синтаксисе, действуют одинаково. +Есть другие способы разбора и последующего вызова выражения с функцией; они, хоть и различаются в синтаксисе, но действуют одинаково. // Два других способа +function(){}(); diff --git a/doc/ru/function/this.md b/doc/ru/function/this.md index a4fa2fdc..b01f76b0 100644 --- a/doc/ru/function/this.md +++ b/doc/ru/function/this.md @@ -1,6 +1,6 @@ ## Как работает `this` -В JavaScript область ответственности специальной переменной `this` концептуально отличается от того, за что отвечает `this` в других языках программирования. Различают ровно **пять** вариантов того, к чему привязывается this в языке. +В JavaScript область ответственности специальной переменной `this` концептуально отличается от того, за что отвечает `this` в других языках программирования. Различают ровно **пять** вариантов того, к чему привязывается `this` в языке. ### 1. Глобальная область видимости @@ -38,13 +38,13 @@ Когда мы используем методы `call` или `apply` из `Function.prototype`, то внутри вызваемой функции `this` **явным образом** будет присвоено значение первого передаваемого параметра. -Исходя из этого, в предыдущем примере (строка с `apply`) *вызов метода* (3 вариант) **не** будет применён, и `this` внутри `foo` будет присвоено `bar`. +Исходя из этого, в предыдущем примере (строка с `apply`) правило #3 *вызов метода* **не** будет применёно, и `this` внутри `foo` будет присвоено `bar`. > **Замечание:** `this` **нельзя** использовать внутри литералов `{}` (`Object`) для ссылки на сам объект. Т.е. если мы напишем `var obj = {me: this}`, то `me` не будет ссылаться на `obj`, поскольку `this` присваивается только по одному из пяти описанных правил. ### Наиболее распространенные ошибки -Хотя большинство из этих примеров наполнены глубоким смыслом, первый из них можно считать ещё одним упущением в самом языке, поскольку он **вообще** не имеет практического применения. +Хотя большинство из примеров ниже наполнены глубоким смыслом, первый из них можно считать ещё одним упущением в самом языке, поскольку он **вообще** не имеет практического применения. Foo.method = function() { function test() { @@ -55,7 +55,7 @@ Распространенным заблуждением будет то, что `this` внутри `test` ссылается на `Foo`, но это **не так**. -Для того, чтобы получить доступ к `Foo` внутри test необходимо создать локальную перменную внутри `method`, которая и будет ссылаться на `Foo`. +Для того, чтобы получить доступ к `Foo` внутри функции `test`, необходимо создать локальную перменную внутри `method`, которая и будет ссылаться на `Foo`. Foo.method = function() { var that = this; @@ -67,7 +67,7 @@ Подходящее имя для переменной - `that`, его часто используют для ссылки на внешний `this`. В комбинации с [замыканиями](#function.closures) `this` можно пробрасывать в глобальную область, или в любой другой объект. -> **Замечание** от перев. Кроме `that` так же часто встречаются другие варианты `this_`, `self_` и другие варианты, но лучше принять для себя `that` как стандарт и тогда, возможно, все вокруг будут им пользоваться. +> **Замечание** от перев. Кроме `that` так же часто встречаются `this_`, `self_` и другие варианты, но лучше принять для себя `that` как стандарт и тогда, возможно, все вокруг тоже будут им пользоваться. ### Назначение методов diff --git a/doc/ru/intro/translators.md b/doc/ru/intro/translators.md index c7c95cdc..d08ba2a1 100644 --- a/doc/ru/intro/translators.md +++ b/doc/ru/intro/translators.md @@ -1,4 +1,4 @@ -## Перевод +## Переводчики - ['shaman.sir'][1] - [Антон Шевчук][2] diff --git a/doc/ru/object/forinloop.md b/doc/ru/object/forinloop.md index d42fd9e0..22470a2f 100644 --- a/doc/ru/object/forinloop.md +++ b/doc/ru/object/forinloop.md @@ -25,13 +25,13 @@ } } -Эта единственная версия правильного использования. Благодаря использованию `hasOwnPropery` будет выведен **только** `moo`. Если же убрать `hasOwnProperty`, код становится нестабилен и могут возникнуть ошибки, если кто-то изменил встроенные прототипы, такие как `Object.prototype`. +Это единственная версия правильного использования цикла. Благодаря использованию `hasOwnPropery` будет выведено **только** свойство `moo`. Если же убрать `hasOwnProperty`, код становится нестабилен и могут возникнуть ошибки, особенно если кто-то изменил встроенные прототипы, такие как `Object.prototype`. -Один из самых популярных фреймворков [Prototype][1] как раз этим и славится, и если вы его подключаете, то не забудьте использовать `hasOwnProperty` внутри цикла `for in`, иначе гарантированно возникнут проблемы. +Один из самых популярных фреймворков [Prototype][1] как раз этим и славится, и если вы его подключаете, то не забудьте использовать `hasOwnProperty` внутри цикла `for in`, иначе у вас гарантированно возникнут проблемы. ### Рекомендации -Рекомендация одна — **всегда** используйте `hasOwnProperty`. пишите код, который будет в наименьшей мере зависеть от окружения в котором он будет запущен — не стоит гадать, расширял кто-то прототипы или нет и используется ли в ней та или иная библиотека. +Рекомендация одна — **всегда** используйте `hasOwnProperty`. Пишите код, который будет в наименьшей мере зависеть от окружения, в котором он будет запущен — не стоит гадать, расширял кто-то прототипы или нет и используется ли в ней та или иная библиотека. [1]: http://www.prototypejs.org/ diff --git a/doc/ru/object/general.md b/doc/ru/object/general.md index 2ee81e54..862b2c08 100644 --- a/doc/ru/object/general.md +++ b/doc/ru/object/general.md @@ -21,7 +21,7 @@ ### Объекты как тип данных -Объекты в JavaScript могут использоваться как [*хеш-таблицы*][1]: в основном они состоят из именованных свойств (ключей) привязанных к значениям. +Объекты в JavaScript могут использоваться как [*хеш-таблицы*][1]: подавляющей частью состоят из именованных свойств (ключей), привязанных к значениям. Используя объектный литерал — нотацию `{}` — можно создать простой объект. Новый объект [наследуется](#object.prototype) от `Object.prototype` и не имеет [собственных свойств](#object.hasownproperty). @@ -74,7 +74,7 @@ delete: 'Я тоже ключевое слово, так что я' // бросаю SyntaxError }; -Свойства объектов могут записываться и явно символами и в виде закавыченных строк. В связи с другим упущением в парсере JavaScript, этот код выбросит `SyntaxError` во всех версиях ранее ECMAScript 5. +Свойства объектов могут записываться как явно символами, так и в виде закавыченных строк. В связи с другим упущением в парсере JavaScript, этот код выбросит `SyntaxError` во всех версиях ранее ECMAScript 5. Источником ошибки является факт, что `delete` — это *ключевое слово* и поэтому его необходимо записывать как *строчный литерал*: ради уверенности в том, что оно будет корректно опознано более старыми движками JavaScript. diff --git a/doc/ru/object/hasownproperty.md b/doc/ru/object/hasownproperty.md index e1324e32..7e808844 100644 --- a/doc/ru/object/hasownproperty.md +++ b/doc/ru/object/hasownproperty.md @@ -16,7 +16,7 @@ foo.hasOwnProperty('bar'); // false foo.hasOwnProperty('goo'); // true -Только используя hasOwnProperty можно гарантировать правильный результат при переборе свойств объекта. И **нет** иного способа для определения свойств, которые определены в *самом* объекте, а не где-то в цепочке прототипов. +Только используя `hasOwnProperty` можно гарантировать правильный результат при переборе свойств объекта. И **нет** иного способа для определения свойств, которые определены в *самом* объекте, а не где-то в цепочке его прототипов. ### `hasOwnProperty` как свойство diff --git a/doc/ru/object/prototype.md b/doc/ru/object/prototype.md index 6982caa7..30e7f860 100644 --- a/doc/ru/object/prototype.md +++ b/doc/ru/object/prototype.md @@ -8,7 +8,7 @@ Первое важное отличие заключается в том, что наследование в JavaScript выполняется с использованием так называемых *цепочек прототипов*. -> **Замечание:** В результате выполнения конструкции `Bar.prototype = Foo.prototype` оба объекта будут делить друг с другом **один и тот же** прототип. Так что изменение прототипа одного из объектов повлечёт за собой изменение прототипа другого и наоборот — вряд ли это окажется тем, что вы ожидали. +> **Замечание:** В результате выполнения конструкции `Bar.prototype = Foo.prototype` оба объекта будут делить друг с другом **один и тот же** прототип. Так что изменение прототипа одного из объектов повлечёт за собой изменение прототипа другого и наоборот — вряд ли это окажется тем, чего вы ожидали. function Foo() { this.value = 42; @@ -37,7 +37,7 @@ Object.prototype { toString: ... /* и т.д. */ } -В приведённом коде объект `test` наследует оба прототипа: `Bar.prototype` и `Foo.prototype`; следовательно, он имеет доступ к функции `method` которую мы определили в прототипе `Foo`. Также у него есть доступ к свойству `value` **одного уникального** экземпляра `Foo`, который является его протипом. Важно заметить, что код `new Bar()` **не** создаёт новый экземпляр `Foo`, а переиспользует тот, который был назначен его прототипом: таким образом все новые экземпляры `Bar` будут иметь **одинаковое** свойство `value`. +В приведённом коде объект `test` наследует оба прототипа: `Bar.prototype` и `Foo.prototype`; следовательно, он имеет доступ к функции `method` которую мы определили в прототипе `Foo`. Также у него есть доступ к свойству `value` **одного уникального** экземпляра `Foo`, который является его протипом. Важно заметить, что код `new Bar()` **не** создаёт новый экземпляр `Foo`, а повторно вызывает функцию, которая была назначен его прототипом: таким образом все новые экземпляры `Bar` будут иметь **одинаковое** свойство `value`. > **Замечание:** Никогда **не** используйте конструкцию `Bar.prototype = Foo`, поскольку ссылка будет указывать не на прототип `Foo`, а на объект функции `Foo`. Из-за этого цепочка прототипов будет проходить через `Function.prototype`, а не через `Foo.prototype` и в результате функция `method` не будет содержаться в цепочке прототипов. @@ -49,7 +49,7 @@ ### Свойство `prototype` -То, что свойство `prototype` используется языком для построения цепочек прототипов, даёт нам возможность присвоить **любое** значение этому свойству. Однако обычные примитивы будут просто-напросто игнорироваться, если назначать их в качестве прототипа. +То, что свойство `prototype` используется языком для построения цепочек прототипов, даёт нам возможность присвоить **любое** значение этому свойству. Однако обычные примитивы, если назначать их в качестве прототипа, будут просто-напросто игнорироваться. function Foo() {} Foo.prototype = 1; // ничего не произойдёт @@ -63,7 +63,7 @@ Поиск свойств, располагающихся относительно высоко по цепочке прототипов, может негативно сказаться на производительности, особенно в критических местах кода. Если же мы попытаемся найти несуществующее свойство, то поиск будет осуществлён вообще по всей цепочке, со всеми вытекающими последствиями. -Кроме этого, при [переборе](#object.forinloop) свойств объекта, будет обработано **каждое** свойство, существующее в цепочке прототипов. +Вдобавок, при [циклическом переборе](#object.forinloop) свойств объекта, будет обработано **каждое** свойство, существующее в цепочке прототипов. ### Расширение встроенных прототипов @@ -75,7 +75,7 @@ ### Заключение -Перед тем, как вы приступите к разработке сложных приложений на JavaScript, вы **должны** полностью осознать как работают прототипы, и как организовывать наследование на их основе. Также, помните о зависимости между длиной цепочек прототипов и производительностью — разрывайте их при необходимости. Кроме того - **никогда** не расширяйте прототипы встроенных объектов (ну, если только для совместимости с новыми возможностями Javascript). +Перед тем, как вы приступите к разработке сложных приложений на 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 diff --git a/doc/ru/other/timeouts.md b/doc/ru/other/timeouts.md index 94ab9cf1..4453493d 100644 --- a/doc/ru/other/timeouts.md +++ b/doc/ru/other/timeouts.md @@ -2,16 +2,16 @@ Поскольку JavaScript поддерживает асинхронность, есть возможность запланировать выполнение функции, используя функции `setTimeout` и `setInterval`. -> **Замечание:** Таймауты **не** являются частью стандарта ECMAScript, они были разработаны как раздел [DOM][1]. +> **Замечание:** Таймауты **не** являются частью стандарта ECMAScript, они были разработаны как раздел спецификации[DOM][1]. function foo() {} var id = setTimeout(foo, 1000); // возвращает число > 0 -Функция `setTimeout` возвращает идентификатор таймаута и планирует вызвать `foo` через, **примерно**, тысячу миллисекунд. Фунция `foo` будет вызвана ровно **один** раз. +Функция `setTimeout` возвращает идентификатор таймаута и планирует вызвать `foo` через, **примерно**, тысячу миллисекунд. Фунция `foo` при этом будет вызвана ровно **один** раз. -В зависимости от разрешения таймера в используемом для запуска кода движке JavaScript, а также с учётом факта, что JavaScript является однопоточным языком и посторонний код может заблокировать выполнение потока, нет **никакой** гарантии, что код переданный будет выполнен ровно через указанное в вызове `setTimeout` время. +В зависимости от разрешения таймера в используемом для запуска кода движке JavaScript, а также с учётом факта, что JavaScript является однопоточным языком и посторонний код может заблокировать выполнение потока, нет **никакой** гарантии, что переданный код будет выполнен ровно через указанное в вызове `setTimeout` время. -Переданная через первый параметр функция будет вызвана как *глобальный объект* — это значит что оператор [`this`](#function.this) в вызываемой функции будет ссылаться на этот самый объект. +Переданная первым параметром функция будет вызвана как *глобальный объект* — это значит, что оператор [`this`](#function.this) в вызываемой функции будет ссылаться на этот самый объект. function Foo() { this.value = 42; @@ -24,13 +24,13 @@ new Foo(); -> **Замечание:** Поскольку `setTimeout` принимает **объект функции** в качестве первого параметра часто совершается ошибка в использовании `setTimeout(foo(), 1000)`, при котором будет использоваться **возвращённое значение** от вызова `foo`, а **не** вызвана функция `foo`. В большинстве случаев ошибка пройдёт незамеченной, а в случае если функция возвращает `undefined`, `setTimeout` вообще **не** породит никакой ошибки. +> **Замечание:** Поскольку `setTimeout` принимает **объект функции** в качестве первого параметра, часто совершается ошибка в использовании `setTimeout(foo(), 1000)`, при котором будет использоваться **возвращённое значение** от вызова функции `foo`, а **не** вызываться сама функция `foo`. В большинстве случаев ошибка пройдёт незамеченной, а в случае если функция возвращает `undefined`, `setTimeout` вообще **не** породит никакой ошибки. ### Поочерёдные вызовы с использованием `setInterval` `setTimeout` вызывает функцию единожды; `setInterval` — как и предполагает название — вызывает функцию **каждые** `X` миллисекунд. И его использование не рекомендуется. -В то время, когда исполняющийся код будет блокироваться во время вызова с таймаутом, `setInterval` будет продолжать планировать последующие вызовы переданной функции. Это может, особенно в случае небольших интервалов, повлечь за собой выстраивание вызовов функций в очередь. +В то время, когда исполняющийся код будет блокироваться во время вызова с таймаутом, `setInterval` будет продолжать планировать последующие вызовы переданной функции. Это может (особенно в случае небольших интервалов) повлечь за собой выстраивание вызовов функций в очередь. function foo(){ // что-то, что выполняется одну секунду @@ -51,7 +51,7 @@ } foo(); -Такой способ не только инкапсулирует вызов `setTimeout`, но и предотвращает от очередей блокирующих вызовов и обеспечивает дополнительный контроль. Сама функция `foo` теперь принимает решение, хочет ли она запускаться ещё раз или нет. +Такой способ не только инкапсулирует вызов `setTimeout`, но и предотвращает от очередей блокирующих вызовов и при этом обеспечивает дополнительный контроль. Сама функция `foo` теперь принимает решение, хочет ли она запускаться ещё раз или нет. ### Очистка таймаутов вручную @@ -69,7 +69,7 @@ clearTimeout(i); } -Вполне могут остаться таймауты, которые не будут захвачены этим произвольным числом; так что рекомендуется следить за идентификаторами всех создающихся таймаутов, засчёт чего их можно будет удалять индивидуально. +Вполне могут остаться таймауты, которые не будут захвачены этим произвольным числом; так что всё же рекомендуется следить за идентификаторами всех создающихся таймаутов, засчёт чего их можно будет удалять индивидуально. ### Скрытое использование `eval` @@ -107,7 +107,7 @@ ### Заключение -**Никогда** не используйте строки как параметры `setTimeout` или `setInterval`. Это явный признак **действительно** плохого кода, если вызываемой функции необходимо передавать аргументы. Лучше передавать *анонимную функцию*, которая самостоятельно будет отвечать за сам вызов. +**Никогда** не используйте строки как параметры `setTimeout` или `setInterval`. Это явный признак **действительно** плохого кода. Если вызываемой функции необходимо передавать аргументы, лучше передавать *анонимную функцию*, которая самостоятельно будет отвечать за сам вызов. Кроме того, избегайте использования `setInterval` в случаях, когда его планировщик может блокировать выполнение JavaScript. diff --git a/doc/ru/types/casting.md b/doc/ru/types/casting.md index aa7c05d2..bc350cfc 100644 --- a/doc/ru/types/casting.md +++ b/doc/ru/types/casting.md @@ -9,8 +9,8 @@ JavaScript *слабо типизированный* язык, поэтому * 10 == '10'; // Strings преобразуется в Number 10 == '+10 '; // Ещё чуток строко-безумия 10 == '010'; // и ещё - isNaN(null) == false; // null преобразуется в 0 - // который конечно NaN + isNaN(null) == false; // null преобразуется в 0, + // который конечно же NaN // Эти равенства — ложь 10 == 010; @@ -18,7 +18,7 @@ JavaScript *слабо типизированный* язык, поэтому * > **ES5 Замечание:** Числовые литералы, которые начинаются с 0 интерпретируются как восьмеричные (Base 8). В ECMAScript 5 strict mode **удалена** поддержка восьмеричной системы. -Для того, чтобы избежать этого, **настоятельно** рекомендуется использовать [оператор строгого равенства](#types.equality). Хотя это позволяет избежать многих распространенных ошибок, существует ещё много дополнительных вопросов, которые возникают из-за слабости типизации JavaScript. +Для того, чтобы избежать этого, **настоятельно** рекомендуется использовать [оператор строгого равенства](#types.equality). Впрочем, хотя это и позволяет избежать многих распространенных ошибок, существует ещё много дополнительных вопросов, которые возникают из-за слабости типизации JavaScript. ### Конструкторы встроенных типов @@ -28,9 +28,9 @@ JavaScript *слабо типизированный* язык, поэтому * Number(10) === 10; // True, Number и Number new Number(10) + 0 === 10; // True, из-за неявного преобразования -Использование встроенных типов, например `Number`, с конструктором создаёт новый экземпляр объекта Number, но использование без ключевого слова `new` создаст функцию `Number`, которая будет вести себя, как конвертер. +Использование встроенных типов, например `Number`, с конструктором создаёт новый экземпляр объекта Number, но использование без ключевого слова `new` создаст функцию `Number`, которая будет вести себя как конвертер. -Кроме того, наличие литералов или переменных, которые не являются объектами приведет к еще большему насилию над типами. +Кроме того, присутствие литералов или переменных, которые не являются объектами приведет к еще большему насилию над типами. Лучший вариант — это **явное** приведение к одному из трех возможных типов. @@ -48,7 +48,7 @@ JavaScript *слабо типизированный* язык, поэтому * ### Приведение к булеву типу -Используя оператор **not (!)** дважды, значение может быть приведено к логическому (булеву) типу. +Используя оператор **not** (**`!`**) дважды, значение может быть приведено к логическому (булеву) типу. !!'foo'; // true !!''; // false diff --git a/doc/ru/types/equality.md b/doc/ru/types/equality.md index baa95218..25cbae31 100644 --- a/doc/ru/types/equality.md +++ b/doc/ru/types/equality.md @@ -18,9 +18,9 @@ JavaScript имеет 2 различных способа сравнения з null == undefined // true " \t\r\n" == 0 // true -В таблице выше показаны результаты приведения типов и это главная причина почему использование `==` повсеместно считается плохой практикой, это приводит к трудностям в отслеживании ошибок из-за сложных правил преобразования типов. +В таблице выше показаны результаты приведения типов и это главная причина почему использование `==` повсеместно считается плохой практикой: оно приводит к трудностям в отслеживании ошибок из-за сложных правил преобразования типов. -Кроме того, приведение типов во время сравнением также влияет на производительность; например, строка должна быть преобразована в число перед сравнением с другим числом. +Кроме того, приведение типов во время сравнения также влияет на производительность; например, строка должна быть преобразована в число перед сравнением с другим числом. ### Оператор строгого равенства diff --git a/doc/ru/types/instanceof.md b/doc/ru/types/instanceof.md index d1d76ee1..76d770c9 100644 --- a/doc/ru/types/instanceof.md +++ b/doc/ru/types/instanceof.md @@ -1,6 +1,6 @@ ## Оператор `instanceof` -Оператор `instanceof` сравнивает конструкторы двух операндов. Это полезно только когда сравниваются пользовательские объекты. Использование на встроенных типах почти так же бесполезно, как оператор [оператор typeof](#types.typeof). +Оператор `instanceof` сравнивает конструкторы двух операндов. Это полезно только когда сравниваются пользовательские объекты. Использование на встроенных типах почти так же бесполезно, как и [оператор typeof](#types.typeof). ### Сравнение пользовательских объектов @@ -24,9 +24,9 @@ 'foo' instanceof String; // false 'foo' instanceof Object; // false -Здесь надо отметить одну важную вещь: `instanceof` не работает на объектах, которые происходят из разных контекстов JavaScript (например, из различных документы в web-браузере), так как их конструкторы не будут конструкторами *тех самых* объектов. +Здесь надо отметить одну важную вещь: `instanceof` не работает на объектах, которые происходят из разных контекстов JavaScript (например, из различных документов в web-браузере), так как их конструкторы и правда не будут конструкторами *тех самых* объектов. ### Заключение -Оператор instanceof должен использоваться **только** при обращении к пользовательским объектам, происходящим из одного контекста JavaScript. Так же, как и в случае оператора `typeof`, любое другое использование необходимо **избегать**. +Оператор `instanceof` должен использоваться **только** при обращении к пользовательским объектам, происходящим из одного контекста JavaScript. Так же, как и в случае оператора `typeof`, любое другое использование необходимо **избегать**. diff --git a/doc/ru/types/typeof.md b/doc/ru/types/typeof.md index 99724139..96cd7ed1 100644 --- a/doc/ru/types/typeof.md +++ b/doc/ru/types/typeof.md @@ -2,9 +2,9 @@ Оператор `typeof` (вместе с [`instanceof`](#types.instanceof)) — это, вероятно, самая большая недоделка в JavaScript, поскольку, похоже, он **поломан более чем полностью**. -Хотя `instanceof` еще имеет ограниченное применение, `typeof` на самом деле имеет *только один* практический случай применения, который при этом **не** является проверкой типа объекта. +Хотя `instanceof` еще имеет ограниченное применение, `typeof` на самом деле имеет *только один* практический случай применения, который при всём при этом **не** является проверкой типа объекта. -> **Замечаение:** Хотя для вызова `typeof` также можно использовать синтаксис функции, т.е. `typeof(obj)`, на самом деле это не функция. Двойные круглые скобки будут работать нормально и возвращаемое значение будет использоваться как операнд оператора `typeof`. Но функции `typeof` — **нет**. +> **Замечаение:** Хотя для вызова `typeof` также можно использовать синтаксис функции, т.е. `typeof(obj)`, на самом деле это не функция. Двойные круглые скобки будут работать нормально и возвращаемое значение будет использоваться как операнд оператора `typeof`. Но функции `typeof` — **не существует**. ### Таблица типов JavaScript @@ -26,7 +26,7 @@ {} Object object new Object() Object object -В таблице выше *Тип* представляет собой значение, возвращаемое оператором `typeof`. Как хорошо видно, это значение может быть абсолютно любым, но не логичным. +В таблице выше *Тип* представляет собой значение, возвращаемое оператором `typeof`. Как хорошо видно, это значение может быть абсолютно любым, но не логичным результатом. *Класс* представляет собой значение внутреннего свойства `[[Class]]` объекта. @@ -36,7 +36,7 @@ ### Класс объекта -Спецификация предоставляет только один способ доступа к значению `[[Class]]` — с использованием `Object.prototype.toString`. +Спецификация предоставляет только один способ доступа к значению `[[Class]]` — используя `Object.prototype.toString`. function is(type, obj) { var clas = Object.prototype.toString.call(obj).slice(8, -1); @@ -46,7 +46,7 @@ is('String', 'test'); // true is('String', new String('test')); // true -В примере выше `Object.prototype.toString` вызывается со значением [this](#function.this), являющимся объектом, значение [[Class]] которого нужно получить. +В примере выше `Object.prototype.toString` вызывается со значением [this](#function.this), являющимся объектом, значение `[[Class]]` которого нужно получить. > **ES5 Замечание:** Для удобства в ECMAScript 5 возвращаемое значение `Object.prototype.toString `для `null` и `undefined` было изменено с `Object` на `Null` и `Undefined` соответственно. @@ -60,5 +60,5 @@ Для проверки типа объекта настоятельно рекомендуется использовать` Object.prototype.toString` — это единственный надежный способ. Как показано выше в таблице типов, некоторые возвращаемые `typeof` значения не определены в спецификации: таким образом, они могут отличаться в различных реализациях. -Кроме проверки, была ли определена переменная, `typeof` следует избегать **во что бы то ни стало**. +Кроме случая проверки, была ли определена переменная, `typeof` следует избегать **во что бы то ни стало**. From d3ebb823b0c3755e097a250a36ea3b4104942689 Mon Sep 17 00:00:00 2001 From: "sanshi.email" Date: Mon, 21 Mar 2011 15:14:02 +0800 Subject: [PATCH 084/641] Test --- doc/test.txt | 1 + 1 file changed, 1 insertion(+) create mode 100755 doc/test.txt diff --git a/doc/test.txt b/doc/test.txt new file mode 100755 index 00000000..6b2f794e --- /dev/null +++ b/doc/test.txt @@ -0,0 +1 @@ +OK. \ No newline at end of file From 54f2f5591023165355aac68d14087ec17d9370a5 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Mon, 21 Mar 2011 12:40:20 +0200 Subject: [PATCH 085/641] fixes from habr --- doc/ru/intro/authors.md | 2 +- doc/ru/object/general.md | 4 +++- doc/ru/object/prototype.md | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/ru/intro/authors.md b/doc/ru/intro/authors.md index ac04aea0..3c11a24d 100644 --- a/doc/ru/intro/authors.md +++ b/doc/ru/intro/authors.md @@ -1,6 +1,6 @@ ## Авторы -Это руководство является результатом работы двух очаровательных пользователей Stack Overflow: [Иво Ветцель /Ivo Wetzel/][1] (автора текста) и [Чжан И Цзян /Zhang Yi Jiang/][2] (дизайнера). +Это руководство является результатом работы двух заядлых пользователей 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/doc/ru/object/general.md b/doc/ru/object/general.md index 862b2c08..804bf1e3 100644 --- a/doc/ru/object/general.md +++ b/doc/ru/object/general.md @@ -48,7 +48,9 @@ ### Удаление свойств -Единственный способ удалить свойство у объекта — использовать оператор `delete`; устанавливая свойство в `undefined` или `null`, вы удаляете только связанное с ним *значение*, но не *ключ*. +Единственный способ удалить свойство у объекта — использовать оператор `delete`; устанавливая свойство в `undefined` или `null`, вы только заменяете связанное с ним *значение*, но не удаляете *ключ*. + +> **Замечание** от перев.: Если ссылок на значение больше нет, то сборщиком мусора удаляется и само значение, но ключ объекта при этом всё также имеет новое значение. var obj = { bar: 1, diff --git a/doc/ru/object/prototype.md b/doc/ru/object/prototype.md index 30e7f860..15f90ba6 100644 --- a/doc/ru/object/prototype.md +++ b/doc/ru/object/prototype.md @@ -2,7 +2,7 @@ В JavaScript отсутствует классическая модель наследования — вместо неё используется [*прототипная модель*][1]. -Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё можно предельно легко реализовать классическое наследование, а попытки обойти его при этом могут создать только трудности. +Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё можно предельно легко реализовать классическое наследование, а попытки совершить обратное вынудят вас попотеть. Из-за того, что JavaScript — практически единственный широко используемый язык с прототипным наследованием, придётся потратить некоторое время на осознание различий между этими двумя моделями. From 23dcbad1f9282f338e92020a439f105caecb2a67 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Mon, 21 Mar 2011 13:04:17 +0200 Subject: [PATCH 086/641] hasOwnProperty fix --- doc/ru/object/hasownproperty.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ru/object/hasownproperty.md b/doc/ru/object/hasownproperty.md index 7e808844..1c8a617c 100644 --- a/doc/ru/object/hasownproperty.md +++ b/doc/ru/object/hasownproperty.md @@ -33,7 +33,7 @@ JavaScript **не** резервирует свойство с именем `has // Используем метод hasOwnProperty пустого объекта // и передаём foo в качестве this - {}.hasOwnProperty.call(foo, 'bar'); // true + ({}).hasOwnProperty.call(foo, 'bar'); // true ### Заключение From d04c1eafbea197b00eb7dd1f8abd9e54d7fb84c4 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Mon, 21 Mar 2011 19:43:39 +0800 Subject: [PATCH 087/641] New mobile navigation menu --- site/javascript/garden.js | 6 ++-- site/style/garden.css | 64 +++++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/site/javascript/garden.js b/site/javascript/garden.js index 5c7a5212..bc5127ca 100644 --- a/site/javascript/garden.js +++ b/site/javascript/garden.js @@ -196,8 +196,7 @@ Page.prototype = { this.resizeTimeout = setTimeout(function() { that.sections.map(); that.sections.expand(that.section); - - }, 50); + }, 100); } }; @@ -206,8 +205,7 @@ prettyPrint(); // GA tracking code var _gaq = _gaq || []; -_gaq.push(['_setAccount', 'UA-20768522-1']); -_gaq.push(['_trackPageview']); +_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'; diff --git a/site/style/garden.css b/site/style/garden.css index 4dbcaeb6..dd4995fc 100644 --- a/site/style/garden.css +++ b/site/style/garden.css @@ -97,18 +97,21 @@ nav > div li.active a { color: #FFC76D; } -nav > div ul { - height: 40px; +#hide_menu { + display: none; } -#top { - position: absolute; - top: 0; - right: 0; +#top, #hide_menu { + float: right; } -#hide_menu { - display: none; +#nav_main > div { + overflow: hidden; +} + +#nav_main > div ul { + float: left; + height: 40px; } nav li { @@ -278,11 +281,12 @@ 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'; } +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: '*'; } #function:after, nav li.nav_function h1 a:after { content: 'f(x)'; @@ -421,6 +425,7 @@ a abbr { } section, footer { + font-size: inherit; margin-left: 30px; margin-right: 0; } @@ -451,24 +456,27 @@ a abbr { #nav_main li h1 a { background-image: none; - height: 70px; - padding: 20px 0 30px; + height: 5em; + padding: 1.5em 0 2em; } #nav_main li h1 a:after { display: block; font-family: Georgia; - font-size: 40px; + font-size: 3em; font-weight: normal; margin-top: 10px; } - #nav_main > div ul { - height: auto; + #nav_main > div { overflow: hidden; border-bottom: 1px solid rgba(255, 255, 255, 0.3); } + #nav_main > div ul { + height: auto; + } + nav a:hover, nav li.active, nav li.active a { @@ -477,9 +485,6 @@ a abbr { #hide_menu { display: block; - position: absolute; - top: 0; - right: 45px; } #nav_mobile { @@ -500,14 +505,14 @@ a abbr { } #nav_next_section, #nav_prev_section { - font-size: 10px; + font-size: 0.55em; text-transform: uppercase; } #nav_next_section .nav_section_name, #nav_prev_section .nav_section_name { display: block; - font-size: 14px; + font-size: 1.5em; margin-top: 3px; font-weight: bold; line-height: 1.3em; @@ -556,15 +561,11 @@ screen and (-webkit-min-device-pixel-ratio: 2) { aside { position: static; width: auto; - margin: 30px; - padding: 0.6em 1em 0.625em; + margin: 1em; + padding: 0.6em 0.8em 0.625em; border-top: 1px solid #9eabb7; opacity: 0.85; } - - aside p { - font-size: 13px; - } #nav_main > ul > li { width: 25%; @@ -574,8 +575,7 @@ screen and (-webkit-min-device-pixel-ratio: 2) { width: 160px; } - #nav_next_section .nav_section_name, - #nav_prev_section .nav_section_name { - font-size: 13px; + aside.es5:after { + display: none; } } From 3a86bd386d9b401b8555a742c4411f1063d77478 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Mon, 21 Mar 2011 19:48:06 +0800 Subject: [PATCH 088/641] Fixed issue #56 --- doc/en/object/hasownproperty.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/object/hasownproperty.md b/doc/en/object/hasownproperty.md index ea224109..319a8801 100644 --- a/doc/en/object/hasownproperty.md +++ b/doc/en/object/hasownproperty.md @@ -41,8 +41,8 @@ necessary to use an *external* `hasOwnProperty` in order to get correct results. foo.hasOwnProperty('bar'); // always returns false - // Use another hasOwnProperty and call it with 'this' set to foo - {}.hasOwnProperty.call(foo, 'bar'); // true + // Use another Object's hasOwnProperty and call it with 'this' set to foo + ({}).hasOwnProperty.call(foo, 'bar'); // true ### In Conclusion From 82fd3b680d45696e9dd1ec1a73a09893901e7105 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Mon, 21 Mar 2011 20:12:56 +0800 Subject: [PATCH 089/641] Fixed path error for translation JavaScripts --- garden.jade | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/garden.jade b/garden.jade index 5fd1e3a2..f4329578 100644 --- a/garden.jade +++ b/garden.jade @@ -84,7 +84,14 @@ html(lang='en') | template. script(src='http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js') - script(src='javascript/prettify.js') - script(src='javascript/plugin.js') - script(src='javascript/garden.js') + + - if (language === baseLanguage) + script(src='javascript/prettify.js') + script(src='javascript/plugin.js') + script(src='javascript/garden.js') + + - else + script(src='../javascript/prettify.js') + script(src='../javascript/plugin.js') + script(src='../javascript/garden.js') From 647a289e3ddf0143c7b3864ae03e5f1e28bd1b6f Mon Sep 17 00:00:00 2001 From: "sanshi.ustc" Date: Mon, 21 Mar 2011 20:49:12 +0800 Subject: [PATCH 090/641] Delete test.txt --- doc/test.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100755 doc/test.txt diff --git a/doc/test.txt b/doc/test.txt deleted file mode 100755 index 6b2f794e..00000000 --- a/doc/test.txt +++ /dev/null @@ -1 +0,0 @@ -OK. \ No newline at end of file From 5706af380578ecdafca2f4d8fe9b913b9f870b24 Mon Sep 17 00:00:00 2001 From: "sanshi.ustc" Date: Mon, 21 Mar 2011 20:51:38 +0800 Subject: [PATCH 091/641] init zh folder --- doc/zh/index.json | 16 ++++++++++++++++ doc/zh/intro/authors.md | 8 ++++++++ doc/zh/intro/contributors.md | 8 ++++++++ doc/zh/intro/index.md | 4 ++++ doc/zh/intro/license.md | 12 ++++++++++++ 5 files changed, 48 insertions(+) create mode 100755 doc/zh/index.json create mode 100755 doc/zh/intro/authors.md create mode 100755 doc/zh/intro/contributors.md create mode 100755 doc/zh/intro/index.md create mode 100755 doc/zh/intro/license.md diff --git a/doc/zh/index.json b/doc/zh/index.json new file mode 100755 index 00000000..63bb6ea2 --- /dev/null +++ b/doc/zh/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/doc/zh/intro/authors.md b/doc/zh/intro/authors.md new file mode 100755 index 00000000..f88f94a7 --- /dev/null +++ b/doc/zh/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/doc/zh/intro/contributors.md b/doc/zh/intro/contributors.md new file mode 100755 index 00000000..73fb98ea --- /dev/null +++ b/doc/zh/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/doc/zh/intro/index.md b/doc/zh/intro/index.md new file mode 100755 index 00000000..94250e2f --- /dev/null +++ b/doc/zh/intro/index.md @@ -0,0 +1,4 @@ +## Einführung + +Demnächst. + diff --git a/doc/zh/intro/license.md b/doc/zh/intro/license.md new file mode 100755 index 00000000..cd398699 --- /dev/null +++ b/doc/zh/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 + From 568ca0b3a2972cc136761cdfbf4ed640b4a8afd7 Mon Sep 17 00:00:00 2001 From: "sanshi.ustc" Date: Mon, 21 Mar 2011 21:22:24 +0800 Subject: [PATCH 092/641] Move from old repository --- doc/language.json | 2 +- doc/zh/array/constructor.md | 35 + doc/zh/array/general.md | 58 + doc/zh/core/eval.md | 48 + doc/zh/core/semicolon.md | 114 ++ doc/zh/core/undefined.md | 72 ++ doc/zh/function/arguments.md | 124 +++ doc/zh/function/closures.md | 83 ++ doc/zh/function/constructors.md | 128 +++ doc/zh/function/general.md | 41 + doc/zh/function/scopes.md | 231 ++++ doc/zh/function/this.md | 99 ++ doc/zh/index.json | 62 +- doc/zh/intro/authors.md | 6 +- doc/zh/intro/contributors.md | 11 +- doc/zh/intro/index.md | 11 +- doc/zh/intro/license.md | 10 +- doc/zh/object/forinloop.md | 51 + doc/zh/object/general.md | 90 ++ doc/zh/object/hasownproperty.md | 47 + doc/zh/object/prototype.md | 102 ++ doc/zh/other/timeouts.md | 155 +++ doc/zh/types/casting.md | 70 ++ doc/zh/types/equality.md | 71 ++ doc/zh/types/instanceof.md | 38 + doc/zh/types/typeof.md | 87 ++ site/zh/index.html | 1802 +++++++++++++++++++++++++++++++ 27 files changed, 3629 insertions(+), 19 deletions(-) create mode 100755 doc/zh/array/constructor.md create mode 100755 doc/zh/array/general.md create mode 100755 doc/zh/core/eval.md create mode 100755 doc/zh/core/semicolon.md create mode 100755 doc/zh/core/undefined.md create mode 100755 doc/zh/function/arguments.md create mode 100755 doc/zh/function/closures.md create mode 100755 doc/zh/function/constructors.md create mode 100755 doc/zh/function/general.md create mode 100755 doc/zh/function/scopes.md create mode 100755 doc/zh/function/this.md create mode 100755 doc/zh/object/forinloop.md create mode 100755 doc/zh/object/general.md create mode 100755 doc/zh/object/hasownproperty.md create mode 100755 doc/zh/object/prototype.md create mode 100755 doc/zh/other/timeouts.md create mode 100755 doc/zh/types/casting.md create mode 100755 doc/zh/types/equality.md create mode 100755 doc/zh/types/instanceof.md create mode 100755 doc/zh/types/typeof.md create mode 100644 site/zh/index.html diff --git a/doc/language.json b/doc/language.json index 677da532..7c96b826 100644 --- a/doc/language.json +++ b/doc/language.json @@ -1,4 +1,4 @@ { "default": "en", - "listed": ["en"] + "listed": ["en", "zh"] } diff --git a/doc/zh/array/constructor.md b/doc/zh/array/constructor.md new file mode 100755 index 00000000..8004168a --- /dev/null +++ b/doc/zh/array/constructor.md @@ -0,0 +1,35 @@ +## The `Array` Constructor + +Since the `Array` constructor is ambiguous in how it deals with its parameters, +it is highly recommended to always use the array literals - `[]` notation - +when creating new arrays. + + [1, 2, 3]; // Result: [1, 2, 3] + new Array(1, 2, 3); // Result: [1, 2, 3] + + [3]; // Result: [3] + new Array(3); // Result: [] + new Array('3') // Result: ['3'] + +In cases when there is only one argument passed to the `Array` constructor, +and that argument is a `Number`, the constructor will return a new *sparse* +array with the `length` property set to the value of the argument. It should be +noted that **only** the `length` property of the new array will be set this way, +the actual indexes of the array will not be initialized. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, the index was not set + +The behavior of being able to set the length of the array upfront only comes in +handy in a few cases, like repeating a string, in which it avoids the use of a +`for loop` code. + + new Array(count + 1).join(stringToRepeat); + +### In conclusion + +The use of the `Array` constructor should be avoided as much as possible. +Literals are definitely preferred. They are shorter and have a clearer syntax; +therefore, they also increase the readability of the code. + diff --git a/doc/zh/array/general.md b/doc/zh/array/general.md new file mode 100755 index 00000000..7e0ce1d1 --- /dev/null +++ b/doc/zh/array/general.md @@ -0,0 +1,58 @@ +## Array Iteration and Properties + +Although arrays in JavaScript are objects, there are no good reasons to use +the [`for in loop`](#object.forinloop) in for iteration on them. In fact there +are a number of good reasons **against** the use of `for in` on arrays. + +> **Note:** JavaScript arrays are **not** *associative arrays*. JavaScript only +> has [objects](#object.general) for mapping keys to values. And while associative +> arrays **preserve** order, objects **do not**. + +Since the `for in` loop enumerates all the properties that are on the prototype +chain and the only way to exclude those properties is to use +[`hasOwnProperty`](#object.hasownproperty), it is already up to **twenty times** +slower than a normal `for` loop. + +### Iteration + +In order to achieve the best performance when iterating over arrays, it is best +to use the classic `for` loop. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +There is one extra catch in the above example, that is the caching of the +length of the array via `l = list.length`. + +Although the `length` property is defined on the array itself, there is still an +overhead for doing the lookup on each iteration of the loop. And while recent +JavaScript engines **may** apply optimization in this case, there is no way of +telling whether the code will run on one of these newer engines or not. + +In fact, leaving out the caching may result in the loop being only **half as +fast** as with the cached length. + +### The `length` property + +While the *getter* of the `length` property simply returns the number of +elements that are contained in the array, the *setter* can be used to +**truncate** the array. + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo; // [1, 2, 3] + +Assigning a smaller length does truncate the array, but increasing the length +does not have any effect on the array. + +### In conclusion + +For the best performance it is recommended to always use the plain `for` loop +and cache the `length` property. The use of `for in` on an array is a sign of +badly written code that is prone to bugs and bad performance. + diff --git a/doc/zh/core/eval.md b/doc/zh/core/eval.md new file mode 100755 index 00000000..9ec79eed --- /dev/null +++ b/doc/zh/core/eval.md @@ -0,0 +1,48 @@ +## Why not to use `eval` + +The `eval` function will execute a string of JavaScript code in the local scope. + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +But `eval` only executes in local scope when it is being called **directly** *and* +the name of the called function is actually `eval`. + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +The use of `eval` should be avoided at **all costs**. 99.9% of its "uses" can be +achieved **without** it. + +### `eval` in disguise + +The [timeout functions](#other.timeouts) `setTimeout` and `setInterval` can both +take a string as their first argument. This string will **always** get executed +in the global scope since `eval` is not being called directly in that case. + +### Security issues + +`eval` also is a security problem as it executes **any** code given to it, +it should **never** be used with strings of unknown or untrusted origins. + +### In conclusion + +`eval` should never be used, any code that makes use of it is to be questioned in +its workings, performance and security. In case something requires `eval` in +order to work, its design is to be questioned and should **not** be used in the +first place, a *better design* should be used, that does not require the use of +`eval`. + diff --git a/doc/zh/core/semicolon.md b/doc/zh/core/semicolon.md new file mode 100755 index 00000000..d3b9c5b5 --- /dev/null +++ b/doc/zh/core/semicolon.md @@ -0,0 +1,114 @@ +## Automatic semicolon insertion + +Although JavaScript has C style syntax, it does **not** enforce the use of +semicolons in the source code, it is possible to omit them. + +But JavaScript is not a semicolon-less language, it in fact needs the +semicolons in order to understand the source code. Therefore the JavaScript +parser **automatically** inserts them whenever it encounters a parse +error due to a missing semicolon. + + var foo = function() { + } // parse error, semicolon expected + test() + +Insertion happens, and the parser tries again. + + var foo = function() { + }; // no error, parser continues + test() + +The automatic insertion of semicolon is considered to be one of **biggest** +design flaws in the language, as it *can* change the behavior of code. + +### How it works + +The code below has no semicolons in it, so it is up to the parser to decide where +to insert them. + + (function(window, undefined) { + function test(options) { + log('testing!') + + (options.list || []).forEach(function(i) { + + }) + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ) + + return + { + foo: function() {} + } + } + window.test = test + + })(window) + + (function(window) { + window.someLibrary = {} + + })(window) + +Below is the result of the parser's "guessing" game. + + (function(window, undefined) { + function test(options) { + + // Not inserted, lines got merged + log('testing!')(options.list || []).forEach(function(i) { + + }); // <- inserted + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ); // <- inserted + + return; // <- inserted, breaks the return statement + { // treated as a block + + // a label and a single expression statement + foo: function() {} + }; // <- inserted + } + window.test = test; // <- inserted + + // The lines got merged again + })(window)(function(window) { + window.someLibrary = {}; // <- inserted + + })(window); //<- inserted + +> **Note:** The JavaScript parser does not "correctly" handle return statements +> which are followed by a new line, while this is not neccessarily the fault of +> the automatic semicolon insertion, it can still be an unwanted side-effect. + +The parser drastically changed the behavior of the code above, in certain cases +it does the **wrong thing**. + +### Leading parenthesis + +In case of a leading parenthesis, the parser will **not** insert a semicolon. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +This code gets transformed into one line. + + log('testing!')(options.list || []).forEach(function(i) {}) + +Chances are **very** high that `log` does **not** return a function; therefore, +the above will yield a `TypeError` stating that `undefined is not a function`. + +### In conclusion + +It is highly recommended to **never** omit semicolons, it is also advocated to +keep braces on the same line with their corresponding statements and to never omit +them for one single-line `if` / `else` statements. Both of these measures will +not only improve the consistency of the code, they will also prevent the +JavaScript parser from changing its behavior. + diff --git a/doc/zh/core/undefined.md b/doc/zh/core/undefined.md new file mode 100755 index 00000000..e4e0e49f --- /dev/null +++ b/doc/zh/core/undefined.md @@ -0,0 +1,72 @@ +## `undefined` and `null` + +JavaScript has two distinct values for `nothing`, the more useful of these two +being `undefined`. + +### The value `undefined` + +`undefined` is a type with exactly one value: `undefined`. + +The language also defines a global variable that has the value of `undefined`, +this variable is also called `undefined`. But this variable is **not** a constant, +nor is it a keyword of the language. This means that its *value* can be easily +overwritten. + +> **ES5 Note:** `undefined` in ECMAScript 5 is **no longer** *writable* in strict +> mode, but its name can still be shadowed by for example a function with the name +> `undefined`. + +Some examples for when the value `undefined` is returned: + + - Accessing the (unmodified) global variable `undefined`. + - Implicit returns of functions due to missing `return` statements. + - `return` statements which do not explicitly return anything. + - Lookups of non-existent properties. + - Function parameters which do not had any explicit value passed. + - Anything that has been set to the value of `undefined`. + +### Handling changes to the value of `undefined` + +Since the global variable `undefined` only holds a copy of the actual *value* of +`undefined`, assigning a new value to it does **not** change the value of the +*type* `undefined`. + +Still, in order to compare something against the value of `undefined` it is +necessary to retrieve the value of `undefined` first. + +In order to protect code against a possible overwritten `undefined` variable, a +common technique used is to add an additional parameter to an +[anonymous wrapper](#function.scopes), that gets no argument passed to it. + + var undefined = 123; + (function(something, foo, undefined) { + // undefined in the local scope does + // now again refer to the value + + })('Hello World', 42); + +Another way to achieve the same effect would be to use a declaration inside the +wrapper. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +The only difference being here, that this version results in 4 more bytes being +used in case it is minified and there is no other `var` statement inside the +anonymous wrapper. + +### Uses of `null` + +While `undefined` in the context of the JavaScript language is mostly used in +the sense of a traditional *null*, the actual `null` (both a literal and a type) +is more or less just another data type. + +It is used in some JavaScript internals (like declaring the end of the +prototype chain by setting `Foo.prototype = null`), but in almost all cases it +can be replaced by `undefined`. + + diff --git a/doc/zh/function/arguments.md b/doc/zh/function/arguments.md new file mode 100755 index 00000000..cc204b4f --- /dev/null +++ b/doc/zh/function/arguments.md @@ -0,0 +1,124 @@ +## `arguments` 对象(The `arguments` object) + +JavaScript 中每个函数内都能访问一个特别变量 `arguments`。这个变量维护着所有传递到这个函数中的参数列表。 + +> **注意:** 由于 `arguments` 已经被定义为函数内的一个变量。 +> 因此通过 `var` 关键字定义 `arguments` 或者将 `arguments` 声明为一个形式参数, +> 都将导致原生的 `arguments` 不会被创建。 + +`arguments` 变量**不是**一个数组(`Array`)。 +尽管在语法上它有数组相关的属性 `length`,但它不从 `Array.prototype` 继承,实际上它是一个对象(`Object`)。 + +因此,无法对 `arguments` 变量使用标准的数组方法,比如 `push`, `pop` 或者 `slice`。 +虽然使用 `for` 循环遍历也是可以的,但是为了更好的使用数组方法,最好把它转化为一个真正的数组。 + +### 转化为数组(Converting to an array) + +下面的代码将会创建一个新的数组,包含所有 `arguments` 对象中的元素。 + + Array.prototype.slice.call(arguments); + +这个转化比较**慢**,在性能不好的代码中**不推荐**这种做法。 + +### 传递参数(Passing 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); + }; + + // 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); + }; + +译者注:上面的 `Foo.method` 函数和下面代码的效果是一样的。 + + Foo.method = function() { + var args = Array.prototype.slice.call(arguments); + Foo.prototype.method.apply(args[0], args.slice(1)); + }; + + +### 自动更新(Modification "magic") + +`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); + +### 性能真相(Performance myths and truths) + +`arguments` 对象总会被创建,除了两个特殊情况 - 作为局部变量声明和作为形式参数。 +而不管它是否有被使用。 + +`arguments` 的 *getters* 和 *setters* 方法总会被创佳;因此使用 `arguments` 对性能不会有什么影响。 +除非是需要对 `arguments` 对象的属性进行多次访问。 + +> **ES5 提示:** 这些 *getters* 和 *setters* 在严格模式下(strict mode)不会被创建。 + +译者注:在 [MDC][2] 中对 `strict mode` 模式下 `arguments` 的描述有助于我们的理解。 + + // 译者注:来自 [MDC][2] 的代码,说明在 ES5 的严格模式下 `arguments` 的特性。 + function f(a) + { + "use strict"; + a = 42; + return [a, arguments[0]]; + } + var pair = f(17); + assert(pair[0] === 42); + assert(pair[1] === 17); + +然而,的确有一种情况会显著的影响现代 JavaScript 引擎的性能。这就是使用 `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... + } + } + +上面代码中,`foo` 不再是一个单纯的内联函数 [inlining][1](译者注:这里指的是解析器可以做内联处理), +因为它需要知道它自己和它的调用者。 +这不仅抵消了内联函数带来的性能提升,而且破坏了封装,因此现在函数可能要依赖于特定的上下文。 + +因此**强烈**建议大家**不要**使用 `arguments.callee` 和它的属性。 + + +> **ES5 提示:** 在严格模式下,`arguments.callee` 会报错 `TypeError`,因为它已经被废除了。 + +[1]: http://en.wikipedia.org/wiki/Inlining +[2]: https://developer.mozilla.org/en/JavaScript/Strict_mode + diff --git a/doc/zh/function/closures.md b/doc/zh/function/closures.md new file mode 100755 index 00000000..adf184b4 --- /dev/null +++ b/doc/zh/function/closures.md @@ -0,0 +1,83 @@ +## 闭包和引用(Closures and references) + +闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域**总是**能够访问外部作用域中的变量。 +因为 [函数](#scopes) 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。 + +### 模拟私有变量(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 + +这里,`Counter` 函数返回两个闭包,函数 `increment` 和函数 `get`。 这两个函数都维持着 +对外部作用域 `Counter` 的引用,因此总可以访问此作用域内定义的变量 `count`. + +### 为什么不可以在外部访问私有变量(Why private variables work) + +因为 JavaScript 中不可以对作用域进行引用或赋值,因此没有办法在外部访问 `count` 变量。 +唯一的途径就是通过那两个闭包。 + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +上面的代码**不会**改变定义在 `Counter` 作用域中的 `count` 变量的值,因为 `foo.hack` 没有 +定义在那个**作用域**内。它将会创建或者覆盖*全局*变量 `count`。 + +### 循环中的闭包(Closures inside loops) + +一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号(as if they were +copying the value of the loops index variable.)。 + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +上面的代码不会输出数字 `0` 到 `9`,而是会输出数字 `10` 十次。 + +当 `console.log` 被调用的时候,*匿名*函数保持对外部变量 `i` 的引用,此时 `for 循环`已经结束, `i` 的值被修改成了 `10`. + +为了得到想要的结果,需要在每次循环中创建变量 `i` 的**拷贝**。 + +### 避免引用错误(Avoiding the reference problem) + +为了正确的获得循环序号,最好使用 [匿名包裹器](#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) + } + diff --git a/doc/zh/function/constructors.md b/doc/zh/function/constructors.md new file mode 100755 index 00000000..52935451 --- /dev/null +++ b/doc/zh/function/constructors.md @@ -0,0 +1,128 @@ +## Constructors + +Constructors in JavaScript are yet again different from many other languages. Any +function call that is preceded by the `new` keyword acts as a constructor. + +Inside the constructor - the called function - the value of `this` refers to a +newly created `Object`. The [`prototype`](#object.prototype) of this **new** +object is set to the `prototype` of the function object that was invoked as the +constructor. + +If the function that was called has no explicit `return` statement, then it +implicitly returns the value of `this` - the new object. + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +The above calls `Foo` as constructor and sets the `prototype` of the newly +created object to `Foo.prototype`. + +In case of an explicit `return` statement the function returns the value +specified that statement, **but only** if the return value is an `Object`. + + function Bar() { + return 2; + } + new Bar(); // a new object + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // the returned object + +When the `new` keyword is omitted, the function will **not** return a new object. + + function Foo() { + this.bla = 1; // gets set on the global object + } + Foo(); // undefined + +While the above example might still appear to work in some cases, due to the +workings of [`this`](#function.this) in JavaScript, it will use the +*global object* as the value of `this`. + +### Factories + +In order to be able to omit the `new` keyword, the constructor function has to +explicitly return a value. + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +Both calls to `Bar` return the exact same thing, a newly create object which +has a property called `method` on it, that is a +[Closure](#function.closures). + +It is also to note that the call `new Bar()` does **not** affect the prototype +of the returned object. While the prototype will be set on the newly created +object, `Bar` never returns that new object. + +In the above example, there is no functional difference between using and +not using the `new` keyword. + + +### Creating new objects via factories + +An often made recommendation is to **not** use `new` since forgetting its use +may lead to bugs. + +In order to create new object, one should rather use a factory and construct a +new object inside of that factory. + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +While the above is robust against a missing `new` keyword and certainly makes +the use of [private variables](#function.closures) easier, it comes with some +downsides. + + 1. It uses more memory since the created objects do **not** share the methods + on a prototype. + 2. In order to inherit the factory needs to copy all the methods from another + object or put that object on the prototype of the new object. + 3. Dropping the prototype chain just because of a left out `new` keyword + somehow goes against the spirit of the language. + +### In conclusion + +While omitting the `new` keyword might lead to bugs, it is certainly **not** a +reason to drop the use of prototypes altogether. In the end it comes down to +which solution is better suited for the needs of the application, it is +especially important to choose a specific style of object creation **and stick** +with it. + diff --git a/doc/zh/function/general.md b/doc/zh/function/general.md new file mode 100755 index 00000000..c97e5a3a --- /dev/null +++ b/doc/zh/function/general.md @@ -0,0 +1,41 @@ +## 函数声明与表达式(Function Declarations and Expressions) + +函数是JavaScript中的一等对象,这意味着可以把函数像其它值一样传递。 +一个常见的用法是把*匿名函数*作为回调函数传递对异步函数中。 + +### 函数声明(The `function` declaration) + + function foo() {} + +上面的方法会在执行前被 [解析(hoisted)](#scopes),因此它存在于当前上下文的*任意*一个地方, +即使在函数定义体的上面被调用也是对的。 + + foo(); // 正常运行,因为foo在代码运行前已经被创建 + function foo() {} + +### 函数赋值表达式(The `function` expression) + + var foo = function() {}; + +这个例子把一个*匿名*的函数赋值给变量 `foo`。 + + foo; // 'undefined' + foo(); // 出错:TypeError + var foo = function() {}; + +由于 `var` 定义了一个声明语句,对变量 `foo` 的解析是在代码运行之前,因此 `foo` 变量在代码运行时已经被定义过了。 + +但是由于赋值语句只在运行时执行,因此在相应代码执行之前, `foo` 的值缺省为 [undefined](#undefined)。 + +### 命名函数的赋值表达式(Named function expression) + +另外一个特殊的情况是将命名函数赋值给一个变量。 + + var foo = function bar() { + bar(); // 正常运行 + } + bar(); // 出错:ReferenceError + +`bar` 函数声明外是不可见的,这是因为我们已经把函数赋值给了 `foo`; +然而在 `bar` 内部依然可见。这是由于 JavaScript 的 [命名处理](#scopes) 所致, +函数名在函数内*总是*可见的。 diff --git a/doc/zh/function/scopes.md b/doc/zh/function/scopes.md new file mode 100755 index 00000000..95735b07 --- /dev/null +++ b/doc/zh/function/scopes.md @@ -0,0 +1,231 @@ +## Scopes and Namespaces + +Although JavaScript deals fine with the syntax of two matching curly +braces for blocks, it does **not** support block scope; hence, all that is left +is in the language is *function scope*. + + function test() { // a scope + for(var i = 0; i < 10; i++) { // not a scope + // count + } + console.log(i); // 10 + } + +> **Note:** When not used in an assignment, return statement or as a function +> argument, the `{...}` notation will get interpreted as a block statement and +> **not** as an object literal. This, in conjunction with +> [automatic insertion of semicolons](#core.semicolon), can lead to subtle errors. + +There are also no distinct namespaces in JavaScript, that means that everything +gets defined in one *globally shared* namespace. + +Each time a variable is referenced, JavaScript will traverse upwards through all +the scopes until it finds it. In the case that it reaches the global scope and +still has not found the requested name, it will raise a `ReferenceError`. + +### The bane of global variables + + // script A + foo = '42'; + + // script B + var foo = '42' + +The above two scripts do **not** have the same effect. Script A defines a +variable called `foo` in the *global* scope and script B defines a `foo` in the +*current* scope. + +Again, that is **not** at all the *same effect*, not using `var` can have major +implications. + + // global scope + var foo = 42; + function test() { + // local scope + foo = 21; + } + test(); + foo; // 21 + +Leaving out the `var` statement inside the function `test` will override the +value of `foo`. While this might not seem like a big deal at first, having +thousands of lines of JavaScript and not using `var` will introduce horrible and +hard to track down bugs. + + // global scope + var items = [/* some list */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // scope of subLoop + for(i = 0; i < 10; i++) { // missing var statement + // do amazing stuff! + } + } + +The outer loop will terminate after the first call to `subLoop`, since `subLoop` +overwrites the global value of `i`. Using a `var` for the second `for` loop would +have easily avoided this error. The `var` statement should **never** be left out +unless the *desired effect* is to affect the outer scope. + +### Local variables + +The only source for local variables in JavaScript are +[function](#function.general) parameters and variables that were declared via the +`var` statement. + + // global scope + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // local scope of the function test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +While `foo` and `i` are local variables inside the scope of the function `test`, +the assignment of `bar` will override the global variable with the same name. + +### Hoisting + +JavaScript **hoists** declarations. This means that both `var` statements and +`function` declarations will be moved to the top of their enclosing scope. + + bar(); + var bar = function() {}; + var someValue = 42; + + test(); + function test(data) { + if (false) { + goo = 1; + + } else { + var goo = 2; + } + for(var i = 0; i < 100; i++) { + var e = data[i]; + } + } + +The above code gets transformed before any execution is started. JavaScript moves +the `var` statements as well as the `function` declarations to the top of the +nearest surrounding scope. + + // var statements got moved here + var bar, someValue; // default to 'undefined' + + // the function declartion got moved up too + function test(data) { + var goo, i, e; // missing block scope moves these here + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // fails with a TypeError since bar is still 'undefined' + someValue = 42; // assignments are not affected by hoisting + bar = function() {}; + + test(); + +Missing block scoping will not only move `var` statements out of loops and +their bodies, it will also make the results of certain `if` constructs +non-intuitive. + +In the original code the `if` statement seemed to modify the *global +variable* `goo`, while actually it modifies the *local variable* - after hoisting +has been applied. + +Without the knowledge about *hoisting*, below code might seem to raise a +`ReferenceError`. + + // check whether SomeImportantThing has been initiliazed + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +But of course, the above works due to the fact that the `var` statement is being +moved to the top of the *global scope*. + + var SomeImportantThing; + + // other code might initiliaze SomeImportantThing here, or not + + // make sure it's there + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Name resolution order + +All scopes in JavaScript, including the *global scope*, have the special name +[`this`](#function.this) defined in them, which refers to the *current object*. + +Function scopes also have the name [`arguments`](#function.arguments) defined in +them which contains the arguments that were passed to a function. + +For example, when trying to access a variable named `foo` inside the scope of a +function, JavaScript will lookup the name in the following order: + + 1. In case there is a `var foo` statement in the current scope use that. + 2. If one of the function parameters is named `foo` use that. + 3. If the function itself is called `foo` use that. + 4. Go to the next outer scope and start with **#1** again. + +> **Note:** Having a parameter called `arguments` will **prevent** the creation +> of the default `arguments` object. + +### Namespaces + +A common problem of having only one global namespace is the likeliness of running +into problems where variable names clash. In JavaScript, this problem can +easily be avoided with the help of *anonymous wrappers*. + + (function() { + // a self contained "namespace" + + window.foo = function() { + // an exposed closure + }; + + })(); // execute the function immediately + + +Unnamed functions are considered [expressions](#function.general); so in order to +being callable, they must first be evaluated. + + ( // evaluate the function inside the paranthesis + function() {} + ) // and return the function object + () // call the result of the evaluation + +There are other ways for evaluating and calling the function expression; which, +while different in syntax, do behave the exact same way. + + // Two other ways + +function(){}(); + (function(){}()); + +### In conclusion + +It is recommended to always use an *anonymous wrapper* for encapsulating code in +its own namespace. This does not only protect code against name clashes, it +also allows for better modularization of programs. + +Additionally, the use of global variables is considered **bad practice**. **Any** +use of them indicates badly written code that is prone to errors and hard to maintain. + diff --git a/doc/zh/function/this.md b/doc/zh/function/this.md new file mode 100755 index 00000000..d1883afd --- /dev/null +++ b/doc/zh/function/this.md @@ -0,0 +1,99 @@ +## this 的工作原理(How `this` works) + +JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 +在**五**种不同的情况下 ,`this` 指向的各不相同。 + +### 全局范围内(The global scope) + + this; + +当在全部范围内使用 `this`,它将会指向*全局*对象。 +(译者注:浏览器中运行的JavaScript脚本,这个全局对象是 window) + +### 函数调用(Calling a function) + + foo(); + +这里 `this` 也会指向*全局*对象。 + +> **ES5 注意:** 在严格模式下(strict mode),不存在全局变量。 +> 这种情况下 `this` 将会是 `undefined`。 +>(译者注:ES5指的是ECMAScript 5,是2009-12发布的最新的 JavaScript 版本。) + +### 方法调用(Calling a method) + + test.foo(); + +这个例子中,`this` 指向 `test` 对象。 + +### 调用构造函数(Calling a constructor) + + new foo(); + +如果函数倾向于和 `new` 关键词一块使用,则我们称这个函数是 [构造函数](#constructors)。 +在函数内部,`this` 指向*新创建*的对象。 + +### 显式的设置 `this`(Explicit setting of `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` 只可能出现在上述的五种情况中。 +> (译者注:这个例子中,如果是在浏览器中运行,obj.me等于window对象。) + +### 常见误解(Common pitfalls) + +尽管大部分的情况都说的过去,不过第一个规则(译者注:这里指的应该是第二个规则,也就是直接调用函数时,`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` 对象。 +在 [closures](#closures) 一节,我们可以看到 `that` 可以作为参数传递。 + +### 方法的赋值表达式(Assigning methods) + +另一个看起来奇怪的地方是函数别名,也就是将一个方法**赋值**给一个变量。 + + var test = someObject.methodTest; + test(); + +上例中,`test` 就像一个普通的函数被调用;因此,函数内的 `this` 将不再被指向到 `someObject` 对象。 + +虽然 `this` 的晚绑定特性似乎并不友好,但是这确实[基于原型继承](#prototype)赖以生存的土壤。 + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +当 `method` 被调用时,`this` 将会指向 `Bar` 的实例对象。 diff --git a/doc/zh/index.json b/doc/zh/index.json index 63bb6ea2..038f5085 100755 --- a/doc/zh/index.json +++ b/doc/zh/index.json @@ -1,16 +1,72 @@ { "title": "JavaScript Garden", - "langTitle": "JavaScript Garden in Deutsch", - "description": "Ein Guide über JavaScript's Ecken und Kanten.", + "langTitle": "JavaScript Garden in English", + "description": "A Guide to JavaScript's Quirks and Flaws.", "sections": [ { - "title": "Einführung", + "title": "Intro", "dir": "intro", "articles": [ "authors", "contributors", "license" ] + }, + { + "title": "Objects", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "Functions", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Arrays", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "Types", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "Core", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon" + ] + }, + { + "title": "Other", + "dir": "other", + "articles": [ + "timeouts" + ] } ] } diff --git a/doc/zh/intro/authors.md b/doc/zh/intro/authors.md index f88f94a7..7db213ff 100755 --- a/doc/zh/intro/authors.md +++ b/doc/zh/intro/authors.md @@ -1,8 +1,6 @@ -## The Authors +## 关于作者(The Authors) -This guide is the work of two lovely Stack Overflow users, [Ivo Wetzel][1] -(Writing) and [Zhang Yi Jiang][2] (Design). +这篇文章的作者是两位 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/doc/zh/intro/contributors.md b/doc/zh/intro/contributors.md index 73fb98ea..7c681eae 100755 --- a/doc/zh/intro/contributors.md +++ b/doc/zh/intro/contributors.md @@ -1,8 +1,11 @@ -## Contributors +## 贡献者(Contributors) - - [Caio Romão][1] (Spelling corrections) - - [Andreas Blixt][2] (Language corrections) + - [Caio Romão][1] (拼写检查) + - [Andreas Blixt][2] (语言修正) +## 中文翻译(Chinese Translation) + - [三生石上][3] + [1]: https://github.com/caio [2]: https://github.com/blixt - +[3]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/intro/index.md b/doc/zh/intro/index.md index 94250e2f..cfe75f1e 100755 --- a/doc/zh/intro/index.md +++ b/doc/zh/intro/index.md @@ -1,4 +1,11 @@ -## Einführung +## 简介(Intro) -Demnächst. +**JavaScript 秘密花园**是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 +对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, +初学者可以籍此深入了解 JavaScript 的语言特性。 + +JavaScript 秘密花园**不是**用来教你 JavaScript。为了更好的理解这篇文章的内容, +你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习[向导][1]。 + +[1]: https://developer.mozilla.org/en/JavaScript/Guide diff --git a/doc/zh/intro/license.md b/doc/zh/intro/license.md index cd398699..90264edf 100755 --- a/doc/zh/intro/license.md +++ b/doc/zh/intro/license.md @@ -1,9 +1,9 @@ -## License +### 许可(License) + +JavaScript 秘密花园在 [MIT license][1] 许可协议下发布,并存放在开源社区 [GitHub][2]。 +如果你发现错误或者打字错误,请 [file an issue][3] 或者 pull request。 +你也可以在 Stack Overflow 的聊天室 [JavaScript room][4] 找到我们。 -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 diff --git a/doc/zh/object/forinloop.md b/doc/zh/object/forinloop.md new file mode 100755 index 00000000..88411e97 --- /dev/null +++ b/doc/zh/object/forinloop.md @@ -0,0 +1,51 @@ +## The `for in` Loop + +Just like the `in` operator, the `for in` loop also traverses the prototype +chain when iterating over the properties of an object. + +> **Note:** The `for in` loop will **not** iterate over any properties that +> have their `enumerable` attribute set to `false`; for example, the `length` +> property of an array. + + // Poisoning Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // prints both bar and moo + } + +Since it is not possible to change the behavior of the `for in` loop itself, it +is necessary to filter out the unwanted properties inside the loop body , +this is done by using the [`hasOwnProperty`](#object.hasownproperty) method of +`Object.prototype`. + +> **Note:** Since the `for in` always traverses the complete prototype chain, it +> will get slower with each additional layer of inheritance added to an object. + +### Using `hasOwnProperty` for filtering + + // still the foo from above + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +This version is the only correct one to use. Due to the use of `hasOwnProperty` it +will **only** print out `moo`. When `hasOwnProperty` is left out, the code is +prone to errors in cases where the native prototypes - e.g. `Object.prototype` - +have been extended. + +One widely used framework which does this is [Prototype][1]. When this +framework is included, `for in` loops that do not use `hasOwnProperty` are +guaranteed to break. + +### Best practices + +It is recommended to **always** use `hasOwnProperty`. Never should any +assumptions be made about the environment the code is running in, or whether the +native prototypes have been extended or not. + +[1]: http://www.prototypejs.org/ + diff --git a/doc/zh/object/general.md b/doc/zh/object/general.md new file mode 100755 index 00000000..466a0962 --- /dev/null +++ b/doc/zh/object/general.md @@ -0,0 +1,90 @@ +## 对象使用和属性(Object Usage and Properties) + +JavaScript 中所有变量都是对象,除了两个例外 [`null`](#undefined) 和 [`undefined`](#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先被计算 + +### 对象作为数据类型(Objects as a data type) + +JavaScript 的对象可以作为[*哈希表*][1]使用,主要用来保存命名的键与值的对应关系。 + +使用对象的字面语法 - `{}` - 可以创建一个简单对象。这个新创建的对象从 `Object.prototype` +[继承](#prototype)下面,没有任何[自定义属性](#hasownproperty)。 + + var foo = {}; // 一个空对象 + + // 一个新对象,拥有一个值为12的自定义属性'test' + var bar = {test: 12}; + +### 访问属性(Accessing properties) + +有两种方式来访问对象的属性,点操作符或者中括号操作符。 + + var foo = {name: 'Kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // works + +两种语法是等价的,但是中括号操作符在下面两种情况下依然有效 + - 动态设置属性 + - 属性名不是一个有效的变量名(译者注:比如属性名中包含空格,或者属性名是JS的关键词) +(译者注:在 [JSLint][2] 语法检测工具中,点操作符是推荐做法) + +### 删除属性(Deleting properties) + +删除属性的唯一方法是使用 `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` 被真正的删除了,所以从输出结果中消失。 + +### 属性名的语法(Notation of keys) + + var test = { + 'case': 'I am a keyword so I must be notated as a string', + delete: 'I am a keyword too so me' // 出错:SyntaxError + }; + +对象的属性名可以使用字符串或者普通字符声明。但是由于 JavaScript 解析器的另一个错误设计, +上面的第二种声明方式在 ECMAScript 5 之前会抛出 `SyntaxError` 的错误。 + +这个错误的原因是 `delete` 是 JavaScript 语言的一个*关键词*;因此为了在更低版本的 JavaScript 引擎下也能正常运行, +必须使用*字符串字面值*声明方式。 + +[1]: http://en.wikipedia.org/wiki/Hashmap +[2]: http://www.jslint.com/ diff --git a/doc/zh/object/hasownproperty.md b/doc/zh/object/hasownproperty.md new file mode 100755 index 00000000..d8e2cab0 --- /dev/null +++ b/doc/zh/object/hasownproperty.md @@ -0,0 +1,47 @@ +## `hasOwnProperty` + +为了判断一个对象是否包含*自定义*属性而*不是*[原型链](#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` 作为属性(`hasOwnProperty` as a property) + +JavaScript **不会**保护 `hasOwnProperty` 被非法占用,因此如果一个对象碰巧存在这个属性, +就需要使用*外部*的 `hasOwnProperty` 函数来获取正确的结果。 + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // 总是返回 false + + // 使用其它对象的 hasOwnProperty,并将其上下为设置为foo + {}.hasOwnProperty.call(foo, 'bar'); // true + +### 结论(In conclusion) + +当检查对象上某个属性是否存在时,`hasOwnProperty` 是**唯一**可用的方法。 +同时在使用 [`for in` loop](#forinloop) 遍历对象时,推荐**总是**使用 `hasOwnProperty` 方法, +这将会避免[原型](#prototype)对象扩展带来的干扰。 + diff --git a/doc/zh/object/prototype.md b/doc/zh/object/prototype.md new file mode 100755 index 00000000..f6269fe2 --- /dev/null +++ b/doc/zh/object/prototype.md @@ -0,0 +1,102 @@ +## 原型(The prototype) + +JavaScript 不包含传统的类继承模型,而是使用 *prototypical* 原型模型。 + +虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。 +实现传统的类继承模型是很简单,但是实现 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' } + Foo.prototype + {method: ...}; + Object.prototype + {toString: ... /* etc. */}; + +上面的例子中,`test` 对象从 `Bar.prototype` 和 `Foo.prototype` 继承下来;因此, +它能否访问 `Foo` 的原型方法 `method`。但是它不能访问 `Foo` 的实例属性 `value`, +因为这个属性在`Foo`的[构造函数](#constructor)中定义。 +(But it will not have access to the property `value` of a +`Foo` instance, since that property gets defined in the [constructor](#constructor) +of `Foo`. But this constructor has to be called explicitly.) + +(译者注:我认为这个描述是错误的,test.value 是可以访问的。 +因为在设置 Bar.prototype = new Foo(); 时,`value` 也就成为 Bar.prototype 上的一个属性。 +如果你有不同观点,可以到[我的博客][4]评论。) + +> **注意:** **不要**使用 `Bar.prototype = Foo`,因为这不会执行 `Foo` 的原型,而是指向函数 `Foo`。 +> 因此原型链将会回溯到 `Function.prototype` 而不是 `Foo.prototype`,因此 `method` 将不会在 Bar 的原型链上。 + +### 属性查找(Property lookup) + +当查找一个对象的属性时,JavaScript 会**向上**遍历原型链,直到找到给定名称的属性为止。 + +到查找到达原型链的顶部 - 也就是 `Object.prototype` - 但是仍然没有找到指定的属性,就会返回 [undefined](#undefined)。 + +### 原型属性(The prototype property) + +当原型属性用来创建原型链时,可以把**任何**类型的值赋给它(prototype)。 +然而将原子类型赋给 prototype 的操作将会被忽略。 + + function Foo() {} + Foo.prototype = 1; // no effect + +而将对象赋值给 prototype,正如上面的例子所示,将会动态的创建原型链。 + +### 性能(Performance) + +如果一个属性在原型链的上端,则对于查找时间将带来不利影响。特别的,试图获取一个不存在的属性将会遍历整个原型链。 + +并且,当使用 [for-in](#the-for-in-loop) 循环遍历对象的属性时,原型链上的**所有**属性都将被访问。 + +### 扩展内置类型的原型(Extension of native prototypes) + +一个错误特性被经常使用,那就是扩展 `Object.prototype` 或者其他内置类型的原型对象。 + +这种技术被称之为 [monkey patching][1] 并且会破坏*封装*。虽然它被广泛的应用到一些 JavaScript 类库中比如 [Prototype][2], +但是我仍然不认为为内置类型添加一些*非标准*的函数是个好主意。 + +扩展内置类型的**唯一**理由是为了和新的 JavaScript 保持一致,比如 [`Array.forEach`][3]。 +(译者注:这是编程领域常用的一种方式,称之为 [Backport][5],也就是将新的补丁添加到老版本中。) + +### 总结(In conclusion) + +在写复杂的 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 +[4]: http://cnblogs.com/sanshi/ +[5]: http://en.wikipedia.org/wiki/Backport + diff --git a/doc/zh/other/timeouts.md b/doc/zh/other/timeouts.md new file mode 100755 index 00000000..c988702e --- /dev/null +++ b/doc/zh/other/timeouts.md @@ -0,0 +1,155 @@ +### `setTimeout` and `setInterval` + +Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the `setTimeout` and `setInterval` functions. + +> **Note:** Timeouts are **not** part of the ECMAScript Standard, they are +> implemented as part of the [DOM][1]. + + function foo() {} + var id = setTimeout(foo, 1000); // returns a Number > 0 + +When `setTimeout` gets called, it will return the ID of the timeout and schedule +`foo` to run in **approximately** one thousand milliseconds in the future. +`foo` will then get executed exactly **once**. + +Depending on the timer resolution of the JavaScript engine that is running the +code, as well as the fact that JavaScript is single threaded and other code that +gets executed might block the thread, it is by **no means** a safe bet that one +will get the exact delay that was specified in the `setTimeout` call. + +The function that was passed as the first parameter will get called by the +*global object*, that means, that [`this`](#function.this) inside the called function +refers to that very object. + + function Foo() { + this.value = 42; + this.method = function() { + // this refers to the global object + console.log(this.value); // will log undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **Note:** As `setTimeout` takes a **function object** as its first parameter, an +> often made mistake is to use `setTimeout(foo(), 1000)`, which will use the +> **return value** of the call `foo` and **not** `foo`. This is, most of the time, +> a silent error, since when the function returns `undefined` `setTimeout` will +> **not** raise any error. + +### Stacking calls with `setInterval` + +While `setTimeout` only runs the function once, `setInterval` - as the name +suggests - will execute the function **every** `X` milliseconds. But its use is +discouraged. + +When code that is being executed blocks the timeout call, `setInterval` will +still issue more calls to the specified function. This can, especially with small +intervals, result in function calls stacking up. + + function foo(){ + // something that blocks for 1 second + } + setInterval(foo, 100); + +In the above code `foo` will get called once and will then block for one second. + +While `foo` blocks the code `setInterval` will still schedule further calls to +it. Now, when `foo` has finished, there will already be **ten** further calls to +it waiting for execution. + +### Dealing with possible blocking code + +The easiest as well as most controllable solution, is to use `setTimeout` within +the function itself. + + function foo(){ + // something that blocks for 1 second + setTimeout(foo, 100); + } + foo(); + +Not only does this encapsulate the `setTimeout` call, but it also prevents the +stacking of calls and it gives additional control.`foo` itself can now decide +whether it wants to run again or not. + +### Manually clearing timeouts + +Clearing timeouts and intervals works by passing the respective ID to +`clearTimeout` or `clearInterval`, depending which `set` function was used in +the first place. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Clearing all timeouts + +As there is no built-in method for clearing all timeouts and/or intervals, +it is necessary to use brute force in order to achieve this functionality. + + // clear "all" timeouts + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +There might still be timeouts that are unaffected by this arbitrary number; +therefore, is is instead recommended to keep track of all the timeout IDs, so +they can be cleared specifically. + +### Hidden use of `eval` + +`setTimeout` and `setInterval` can also take a string as their first parameter. +This feature should **never** be used, since it internally makes use of `eval`. + +> **Note:** Since the timeout functions are **not** specified by the ECMAScript +> standard, the exact workings when a string is passed to them might differ in +> various JavaScript implementations. As a fact, Microsoft's JScript makes use of +> the `Function` constructor in place of `eval`. + + function foo() { + // will get called + } + + function bar() { + function foo() { + // never gets called + } + setTimeout('foo()', 1000); + } + bar(); + +Since `eval` is not getting called [directly](#core.eval) in this case, the string +passed to `setTimeout` will get executed in the *global scope*; thus, it will +not use the local variable `foo` from the scope of `bar`. + +It is further recommended to **not** use a string for passing arguments to the +function that will get called by either of the timeout functions. + + function foo(a, b, c) {} + + // NEVER use this + setTimeout('foo(1,2, 3)', 1000) + + // Instead use an anonymous function + setTimeout(function() { + foo(a, b, c); + }, 1000) + +> **Note:** While it is also possible to use the syntax +> `setTimeout(foo, 1000, a, b, c)`, it is not recommended, as its use may lead +> to subtle errors when used with [methods](#function.this). + +### In conclusion + +**Never** should a string be used as the parameter of `setTimeout` or +`setInterval`. It is a clear sign of **really** bad code, when arguments need +to be supplied to the function that gets called. An *anonymous function* should +be passed that then takes care of the actual call. + +Further, the use of `setInterval` should be avoided since its scheduler is not +blocked by executing JavaScript. + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model + diff --git a/doc/zh/types/casting.md b/doc/zh/types/casting.md new file mode 100755 index 00000000..34b320d0 --- /dev/null +++ b/doc/zh/types/casting.md @@ -0,0 +1,70 @@ +## Type casting + +JavaScript is a *weakly typed* language, so it will apply *type coercion* +**wherever** possible. + + // These are true + new Number(10) == 10; // Number.toString() is converted + // back to a number + + 10 == '10'; // Strings gets converted to Number + 10 == '+10 '; // More string madness + 10 == '010'; // And more + isNaN(null) == false; // null converts to 0 + // which of course is not NaN + + // These are false + 10 == 010; + 10 == '-10'; + +> **ES5 Note:** Number literals that start with a `0` are interpreted as octal +> (Base 8). Octal support for these has been **removed** in ECMAScript 5 strict +> mode. + +In order to avoid the above, use of the [strict equal operator](#types.equality) +is **highly** recommended. Although this avoids a lot of common pitfalls, there +are still many further issues that arise from JavaScript's weak typing system. + +### Constructors of built-in types + +The constructors of the built in types like `Number` and `String` behave +differently when being used with the `new` keyword and without it. + + new Number(10) === 10; // False, Object and Number + Number(10) === 10; // True, Number and Number + new Number(10) + 0 === 10; // True, due to implicit conversion + +Using a built-in type like `Number` as a constructor will create a new `Number` +object, but leaving out the `new` keyword will make the `Number` function behave +like a converter. + +In addition, having literals or non-object values in there will result in even +more type coercion. + +The best option is to cast to one of the three possible types **explicitly**. + +### Casting to a string + + '' + 10 === '10'; // true + +By prepending a empty string a value can easily be casted to a string. + +### Casting to a number + + +'10' === 10; // true + +Using the **unary** plus operator it is possible to cast to a number. + +### Casting to a boolean + +By using the **not** operator twice, a value can be converted a boolean. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + + diff --git a/doc/zh/types/equality.md b/doc/zh/types/equality.md new file mode 100755 index 00000000..1bc4c9cf --- /dev/null +++ b/doc/zh/types/equality.md @@ -0,0 +1,71 @@ +## Equality and comparisons + +JavaScript has two different ways of comparing the values of objects for equality. + +### The equals operator + +The equals operator consists of two equal signs: `==` + +JavaScript features *weak typing*, that means, that the equals 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 equals operator + +The strict equals operator consists of **three** equal signs: `===` + +Other than the normal equals operator, the strict equals operator does **not** +perform type coercion between its operands. + + "" === "0" // false + 0 === "" // false + 0 === "0" // false + false === "false" // false + false === "0" // false + false === undefined // false + false === null // false + null === undefined // false + " \t\r\n" === 0 // false + +The above results are a lot clearer and allow for early breakage of code. This +hardens code to a certain degree and also gives performance improvements in case +the operands are of different types. + +### Comparing objects + +While both `==` and `===` are stated as **equality** operators, they behave +different when at least one of their operands happens to be an `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Here both operators compare for **identity** and **not** equality; that is, they +will compare for the same **instance** of the object, much like `is` in Python +and a pointer comparison in C do. + +### In conclusion + +It is highly recommended to only use the **strict equals** operator. In cases +where types need to be coerced, it should be done [explicitly](#types.casting) +and not left to the language's complicated coercion rules. + diff --git a/doc/zh/types/instanceof.md b/doc/zh/types/instanceof.md new file mode 100755 index 00000000..6fe0d63e --- /dev/null +++ b/doc/zh/types/instanceof.md @@ -0,0 +1,38 @@ +## The `instanceof` operator + +The `instanceof` operator compares the constructors of its two operands. It is +only useful when comparing custom made objects. Used on built-in types, it is +nearly as useless as the [typeof operator](#types.typeof). + +### Comparing custom objects + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // This just sets Bar.prototype to the function object Foo + // But not to an actual instance of Foo + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### Using `instanceof` with native types + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +One important thing to note here is, that `instanceof` does not work on objects +that origin from different JavaScript contexts (e.g. different documents +in a web browser), since their constructors will not be the exact same object. + +### In conclusion + +The `instanceof` operator should **only** be used when dealing with custom made +objects that origin from the same JavaScript context. Just like the +[`typeof`](#types.typeof) operator, every other use of it should be **avoided**. + diff --git a/doc/zh/types/typeof.md b/doc/zh/types/typeof.md new file mode 100755 index 00000000..200b94d4 --- /dev/null +++ b/doc/zh/types/typeof.md @@ -0,0 +1,87 @@ +## The `typeof` operator + +The `typeof` operator (together with +[`instanceof`](#types.instanceof)) is probably the biggest +design flaw of JavaScript, as it is near of being **completely broken**. + +Although `instanceof` still has its limited uses, `typeof` really has only one +practical use case, which does **not** happen to be checking the type of an +object. + +> **Note:** While `typeof` can also be called with a function like syntax +> i.e. `typeof(obj)`, this is not a function call. The two parenthesis will +> behave like normal and the return value will be used as the operand of the +> `typeof` operator. There is **no** `typeof` function. + +### The JavaScript type table + + Value Class Type + ------------------------------------- + "foo" String string + new String("foo") String object + 1.2 Number number + new Number(1.2) Number object + true Boolean boolean + new Boolean(true) Boolean object + new Date() Date object + new Error() Error object + [1,2,3] Array object + new Array(1, 2, 3) Array object + new Function("") Function function + /abc/g RegExp object (function in Nitro/V8) + new RegExp("meow") RegExp object (function in Nitro/V8) + {} Object object + new Object() Object object + +In the above table *Type* refers to the value, that the `typeof` operator returns. +As can be clearly seen, this value is anything but consistent. + +The *Class* refers to the value of the internal `[[Class]]` property of an object. + +> **From the Specification:** The value of `[[Class]]` can be one of the +> following strings. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +In order to retrieve the value of `[[Class]]` one has to make use of the +`toString` method of `Object.prototype`. + +### The Class of an object + +The specification gives exactly one way of accessing the `[[Class]]` value, +with the use of `Object.prototype.toString`. + + function is(type, obj) { + var clas = Object.prototype.toString.call(obj).slice(8, -1); + return obj !== undefined && obj !== null && clas === type; + } + + is('String', 'test'); // true + is('String', new String('test')); // true + +In the above example, `Object.prototype.toString` gets called with the value of +[this](#function.this) being set to the object whose `[[Class]]` value should be +retrieved. + +> **ES5 Note:** For convenience the return value of `Object.prototype.toString` +> for both `null` and `undefined` was **changed** from `Object` to `Null` and +> `Undefined` in ECMAScript 5. + +### Testing for undefined variables + + typeof foo !== 'undefined' + +The above will check whether `foo` was actually declared or not; just +referencing it would result in a `ReferenceError`. This is the only thing +`typeof` is actually useful for. + +### In conclusion + +In order to check the type of an object, it is highly recommended to use +`Object.prototype.toString`; as this is the only reliable way of doing so. +As shown in the above type table, some return values of `typeof` are not defined +in the specification; thus, they can differ across various implementations. + +Unless checking whether a variable is defined, `typeof` should be avoided at +**all costs**. + + diff --git a/site/zh/index.html b/site/zh/index.html new file mode 100644 index 00000000..c52c4a95 --- /dev/null +++ b/site/zh/index.html @@ -0,0 +1,1802 @@ +JavaScript Garden +

Intro

JavaScript 秘密花园是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 +对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, +初学者可以籍此深入了解 JavaScript 的语言特性。

+ +

JavaScript 秘密花园不是用来教你 JavaScript。为了更好的理解这篇文章的内容, +你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习向导

 关于作者(The Authors)

这篇文章的作者是两位 Stack Overflow 的用户, Ivo Wetzel (写作) 和 Zhang Yi Jiang (设计)。

 许可(License)

JavaScript 秘密花园在 MIT license 许可协议下发布,并存放在开源社区 GitHub。 +如果你发现错误或者打字错误,请 file an issue 或者 pull request。 +你也可以在 Stack Overflow 的聊天室 JavaScript room 找到我们。

Objects

 对象使用和属性(Object Usage and Properties)

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先被计算
+
+ +

对象作为数据类型(Objects as a data type)

+ +

JavaScript 的对象可以作为哈希表使用,主要用来保存命名的键与值的对应关系。

+ +

使用对象的字面语法 - {} - 可以创建一个简单对象。这个新创建的对象从 Object.prototype +继承下面,没有任何自定义属性

+ +
var foo = {}; // 一个空对象
+
+// 一个新对象,拥有一个值为12的自定义属性'test'
+var bar = {test: 12}; 
+
+ +

访问属性(Accessing properties)

+ +

有两种方式来访问对象的属性,点操作符或者中括号操作符。

+ +
var foo = {name: 'Kitten'}
+foo.name; // kitten
+foo['name']; // kitten
+
+var get = 'name';
+foo[get]; // kitten
+
+foo.1234; // SyntaxError
+foo['1234']; // works
+
+ +

两种语法是等价的,但是中括号操作符在下面两种情况下依然有效 + - 动态设置属性 + - 属性名不是一个有效的变量名(译者注:比如属性名中包含空格,或者属性名是JS的关键词) +(译者注:在 JSLint 语法检测工具中,点操作符是推荐做法)

+ +

删除属性(Deleting properties)

+ +

删除属性的唯一方法是使用 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 被真正的删除了,所以从输出结果中消失。

+ +

属性名的语法(Notation of keys)

+ +
var test = {
+    'case': 'I am a keyword so I must be notated as a string',
+    delete: 'I am a keyword too so me' // 出错:SyntaxError
+};
+
+ +

对象的属性名可以使用字符串或者普通字符声明。但是由于 JavaScript 解析器的另一个错误设计, +上面的第二种声明方式在 ECMAScript 5 之前会抛出 SyntaxError 的错误。

+ +

这个错误的原因是 delete 是 JavaScript 语言的一个关键词;因此为了在更低版本的 JavaScript 引擎下也能正常运行, +必须使用字符串字面值声明方式。

 原型(The prototype)

JavaScript 不包含传统的类继承模型,而是使用 prototypical 原型模型。

+ +

虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。 +实现传统的类继承模型是很简单,但是实现 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' }
+        Foo.prototype
+            {method: ...};
+            Object.prototype
+                {toString: ... /* etc. */};
+
+ +

上面的例子中,test 对象从 Bar.prototypeFoo.prototype 继承下来;因此, +它能否访问 Foo 的原型方法 method。但是它不能访问 Foo 的实例属性 value, +因为这个属性在Foo构造函数中定义。 +(But it will not have access to the property value of a +Foo instance, since that property gets defined in the constructor +of Foo. But this constructor has to be called explicitly.)

+ +

(译者注:我认为这个描述是错误的,test.value 是可以访问的。 +因为在设置 Bar.prototype = new Foo(); 时,value 也就成为 Bar.prototype 上的一个属性。 +如果你有不同观点,可以到我的博客评论。)

+ + + +

属性查找(Property lookup)

+ +

当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。

+ +

到查找到达原型链的顶部 - 也就是 Object.prototype - 但是仍然没有找到指定的属性,就会返回 undefined

+ +

原型属性(The prototype property)

+ +

当原型属性用来创建原型链时,可以把任何类型的值赋给它(prototype)。 +然而将原子类型赋给 prototype 的操作将会被忽略。

+ +
function Foo() {}
+Foo.prototype = 1; // no effect
+
+ +

而将对象赋值给 prototype,正如上面的例子所示,将会动态的创建原型链。

+ +

性能(Performance)

+ +

如果一个属性在原型链的上端,则对于查找时间将带来不利影响。特别的,试图获取一个不存在的属性将会遍历整个原型链。

+ +

并且,当使用 for-in 循环遍历对象的属性时,原型链上的所有属性都将被访问。

+ +

扩展内置类型的原型(Extension of native prototypes)

+ +

一个错误特性被经常使用,那就是扩展 Object.prototype 或者其他内置类型的原型对象。

+ +

这种技术被称之为 monkey patching 并且会破坏封装。虽然它被广泛的应用到一些 JavaScript 类库中比如 Prototype, +但是我仍然不认为为内置类型添加一些非标准的函数是个好主意。

+ +

扩展内置类型的唯一理由是为了和新的 JavaScript 保持一致,比如 Array.forEach。 +(译者注:这是编程领域常用的一种方式,称之为 Backport,也就是将新的补丁添加到老版本中。)

+ +

总结(In conclusion)

+ +

在写复杂的 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 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 +没有其它方法可以用来排除原型链上的属性,而不是定义在对象自身上的属性。

+ +

hasOwnProperty 作为属性(hasOwnProperty as a property)

+ +

JavaScript 不会保护 hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性, +就需要使用外部hasOwnProperty 函数来获取正确的结果。

+ +
var foo = {
+    hasOwnProperty: function() {
+        return false;
+    },
+    bar: 'Here be dragons'
+};
+
+foo.hasOwnProperty('bar'); // 总是返回 false
+
+// 使用其它对象的 hasOwnProperty,并将其上下为设置为foo
+{}.hasOwnProperty.call(foo, 'bar'); // true
+
+ +

结论(In conclusion)

+ +

当检查对象上某个属性是否存在时,hasOwnProperty唯一可用的方法。 +同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法, +这将会避免原型对象扩展带来的干扰。

The for in Loop

Just like the in operator, the for in loop also traverses the prototype +chain when iterating over the properties of an object.

+ + + +
// Poisoning Object.prototype
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+    console.log(i); // prints both bar and moo
+}
+
+ +

Since it is not possible to change the behavior of the for in loop itself, it +is necessary to filter out the unwanted properties inside the loop body , +this is done by using the hasOwnProperty method of +Object.prototype.

+ + + +

Using hasOwnProperty for filtering

+ +
// still the foo from above
+for(var i in foo) {
+    if (foo.hasOwnProperty(i)) {
+        console.log(i);
+    }
+}
+
+ +

This version is the only correct one to use. Due to the use of hasOwnProperty it +will only print out moo. When hasOwnProperty is left out, the code is +prone to errors in cases where the native prototypes - e.g. Object.prototype - +have been extended.

+ +

One widely used framework which does this is Prototype. When this +framework is included, for in loops that do not use hasOwnProperty are +guaranteed to break.

+ +

Best practices

+ +

It is recommended to always use hasOwnProperty. Never should any +assumptions be made about the environment the code is running in, or whether the +native prototypes have been extended or not.

Functions

 函数声明与表达式(Function Declarations and Expressions)

函数是JavaScript中的一等对象,这意味着可以把函数像其它值一样传递。 +一个常见的用法是把匿名函数作为回调函数传递对异步函数中。

+ +

函数声明(The function declaration)

+ +
function foo() {}
+
+ +

上面的方法会在执行前被 解析(hoisted),因此它存在于当前上下文的任意一个地方, +即使在函数定义体的上面被调用也是对的。

+ +
foo(); // 正常运行,因为foo在代码运行前已经被创建
+function foo() {}
+
+ +

函数赋值表达式(The function expression)

+ +
var foo = function() {};
+
+ +

这个例子把一个匿名的函数赋值给变量 foo

+ +
foo; // 'undefined'
+foo(); // 出错:TypeError
+var foo = function() {};
+
+ +

由于 var 定义了一个声明语句,对变量 foo 的解析是在代码运行之前,因此 foo 变量在代码运行时已经被定义过了。

+ +

但是由于赋值语句只在运行时执行,因此在相应代码执行之前, foo 的值缺省为 undefined

+ +

命名函数的赋值表达式(Named function expression)

+ +

另外一个特殊的情况是将命名函数赋值给一个变量。

+ +
var foo = function bar() {
+    bar(); // 正常运行
+}
+bar(); // 出错:ReferenceError
+
+ +

bar 函数声明外是不可见的,这是因为我们已经把函数赋值给了 foo; +然而在 bar 内部依然可见。这是由于 JavaScript 的 命名处理 所致, +函数名在函数内总是可见的。

 this 的工作原理(How this works)

JavaScript 有一套完全不同于其它语言的对 this 的处理机制。 +在种不同的情况下 ,this 指向的各不相同。

+ +

全局范围内(The global scope)

+ +
this;
+
+ +

当在全部范围内使用 this,它将会指向全局对象。 +(译者注:浏览器中运行的JavaScript脚本,这个全局对象是 window)

+ +

函数调用(Calling a function)

+ +
foo();
+
+ +

这里 this 也会指向全局对象。

+ + + +

方法调用(Calling a method)

+ +
test.foo(); 
+
+ +

这个例子中,this 指向 test 对象。

+ +

调用构造函数(Calling a constructor)

+ +
new foo(); 
+
+ +

如果函数倾向于和 new 关键词一块使用,则我们称这个函数是 构造函数。 +在函数内部,this 指向新创建的对象。

+ +

显式的设置 this(Explicit setting of 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

+ + + +

常见误解(Common pitfalls)

+ +

尽管大部分的情况都说的过去,不过第一个规则(译者注:这里指的应该是第二个规则,也就是直接调用函数时,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 对象。 +在 closures 一节,我们可以看到 that 可以作为参数传递。

+ +

方法的赋值表达式(Assigning methods)

+ +

另一个看起来奇怪的地方是函数别名,也就是将一个方法赋值给一个变量。

+ +
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 and references)

闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 +因为 函数 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。

+ +

模拟私有变量(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
+
+ +

这里,Counter 函数返回两个闭包,函数 increment 和函数 get。 这两个函数都维持着 +对外部作用域 Counter 的引用,因此总可以访问此作用域内定义的变量 count.

+ +

为什么不可以在外部访问私有变量(Why private variables work)

+ +

因为 JavaScript 中不可以对作用域进行引用或赋值,因此没有办法在外部访问 count 变量。 +唯一的途径就是通过那两个闭包。

+ +
var foo = new Counter(4);
+foo.hack = function() {
+    count = 1337;
+};
+
+ +

上面的代码不会改变定义在 Counter 作用域中的 count 变量的值,因为 foo.hack 没有 +定义在那个作用域内。它将会创建或者覆盖全局变量 count

+ +

循环中的闭包(Closures inside loops)

+ +

一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号(as if they were +copying the value of the loops index variable.)。

+ +
for(var i = 0; i < 10; i++) {
+    setTimeout(function() {
+        console.log(i);  
+    }, 1000);
+}
+
+ +

上面的代码不会输出数字 09,而是会输出数字 10 十次。

+ +

console.log 被调用的时候,匿名函数保持对外部变量 i 的引用,此时 for 循环已经结束, i 的值被修改成了 10.

+ +

为了得到想要的结果,需要在每次循环中创建变量 i拷贝

+ +

避免引用错误(Avoiding the reference problem)

+ +

为了正确的获得循环序号,最好使用 匿名包裹器(译者注:其实就是我们通常说的自执行匿名函数)。

+ +
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 对象(The arguments object)

JavaScript 中每个函数内都能访问一个特别变量 arguments。这个变量维护着所有传递到这个函数中的参数列表。

+ + + +

arguments 变量不是一个数组(Array)。 +尽管在语法上它有数组相关的属性 length,但它不从 Array.prototype 继承,实际上它是一个对象(Object)。

+ +

因此,无法对 arguments 变量使用标准的数组方法,比如 push, pop 或者 slice。 +虽然使用 for 循环遍历也是可以的,但是为了更好的使用数组方法,最好把它转化为一个真正的数组。

+ +

转化为数组(Converting to an array)

+ +

下面的代码将会创建一个新的数组,包含所有 arguments 对象中的元素。

+ +
Array.prototype.slice.call(arguments);
+
+ +

这个转化比较,在性能不好的代码中不推荐这种做法。

+ +

传递参数(Passing 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);
+};
+
+// 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);
+};
+
+ +

译者注:上面的 Foo.method 函数和下面代码的效果是一样的。

+ +
Foo.method = function() {
+    var args = Array.prototype.slice.call(arguments);
+    Foo.prototype.method.apply(args[0], args.slice(1));
+};
+
+ +

自动更新(Modification "magic")

+ +

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);
+
+ +

性能真相(Performance myths and truths)

+ +

arguments 对象总会被创建,除了两个特殊情况 - 作为局部变量声明和作为形式参数。 +而不管它是否有被使用。

+ +

argumentsgetterssetters 方法总会被创佳;因此使用 arguments 对性能不会有什么影响。 +除非是需要对 arguments 对象的属性进行多次访问。

+ + + +

译者注:在 MDC 中对 strict mode 模式下 arguments 的描述有助于我们的理解。

+ +
// 译者注:来自 [MDC][2] 的代码,说明在 ES5 的严格模式下 `arguments` 的特性。
+function f(a)
+{
+  "use strict";
+  a = 42;
+  return [a, arguments[0]];
+}
+var pair = f(17);
+assert(pair[0] === 42);
+assert(pair[1] === 17);
+
+ +

然而,的确有一种情况会显著的影响现代 JavaScript 引擎的性能。这就是使用 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...
+    }
+}
+
+ +

上面代码中,foo 不再是一个单纯的内联函数 inlining(译者注:这里指的是解析器可以做内联处理), +因为它需要知道它自己和它的调用者。 +这不仅抵消了内联函数带来的性能提升,而且破坏了封装,因此现在函数可能要依赖于特定的上下文。

+ +

因此强烈建议大家不要使用 arguments.callee 和它的属性。

+ +

Constructors

Constructors in JavaScript are yet again different from many other languages. Any +function call that is preceded by the new keyword acts as a constructor.

+ +

Inside the constructor - the called function - the value of this refers to a +newly created Object. The prototype of this new +object is set to the prototype of the function object that was invoked as the +constructor.

+ +

If the function that was called has no explicit return statement, then it +implicitly returns the value of this - the new object.

+ +
function Foo() {
+    this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+    console.log(this.bla);
+};
+
+var test = new Foo();
+
+ +

The above calls Foo as constructor and sets the prototype of the newly +created object to Foo.prototype.

+ +

In case of an explicit return statement the function returns the value +specified that statement, but only if the return value is an Object.

+ +
function Bar() {
+    return 2;
+}
+new Bar(); // a new object
+
+function Test() {
+    this.value = 2;
+
+    return {
+        foo: 1
+    };
+}
+new Test(); // the returned object
+
+ +

When the new keyword is omitted, the function will not return a new object.

+ +
function Foo() {
+    this.bla = 1; // gets set on the global object
+}
+Foo(); // undefined
+
+ +

While the above example might still appear to work in some cases, due to the +workings of this in JavaScript, it will use the +global object as the value of this.

+ +

Factories

+ +

In order to be able to omit the new keyword, the constructor function has to +explicitly return a value.

+ +
function Bar() {
+    var value = 1;
+    return {
+        method: function() {
+            return value;
+        }
+    }
+}
+Bar.prototype = {
+    foo: function() {}
+};
+
+new Bar();
+Bar();
+
+ +

Both calls to Bar return the exact same thing, a newly create object which +has a property called method on it, that is a +Closure.

+ +

It is also to note that the call new Bar() does not affect the prototype +of the returned object. While the prototype will be set on the newly created +object, Bar never returns that new object.

+ +

In the above example, there is no functional difference between using and +not using the new keyword.

+ +

Creating new objects via factories

+ +

An often made recommendation is to not use new since forgetting its use +may lead to bugs.

+ +

In order to create new object, one should rather use a factory and construct a +new object inside of that factory.

+ +
function Foo() {
+    var obj = {};
+    obj.value = 'blub';
+
+    var private = 2;
+    obj.someMethod = function(value) {
+        this.value = value;
+    }
+
+    obj.getPrivate = function() {
+        return private;
+    }
+    return obj;
+}
+
+ +

While the above is robust against a missing new keyword and certainly makes +the use of private variables easier, it comes with some +downsides.

+ +
    +
  1. It uses more memory since the created objects do not share the methods +on a prototype.
  2. +
  3. In order to inherit the factory needs to copy all the methods from another +object or put that object on the prototype of the new object.
  4. +
  5. Dropping the prototype chain just because of a left out new keyword +somehow goes against the spirit of the language.
  6. +
+ +

In conclusion

+ +

While omitting the new keyword might lead to bugs, it is certainly not a +reason to drop the use of prototypes altogether. In the end it comes down to +which solution is better suited for the needs of the application, it is +especially important to choose a specific style of object creation and stick +with it.

Scopes and Namespaces

Although JavaScript deals fine with the syntax of two matching curly +braces for blocks, it does not support block scope; hence, all that is left +is in the language is function scope.

+ +
function test() { // a scope
+    for(var i = 0; i < 10; i++) { // not a scope
+        // count
+    }
+    console.log(i); // 10
+}
+
+ + + +

There are also no distinct namespaces in JavaScript, that means that everything +gets defined in one globally shared namespace.

+ +

Each time a variable is referenced, JavaScript will traverse upwards through all +the scopes until it finds it. In the case that it reaches the global scope and +still has not found the requested name, it will raise a ReferenceError.

+ +

The bane of global variables

+ +
// script A
+foo = '42';
+
+// script B
+var foo = '42'
+
+ +

The above two scripts do not have the same effect. Script A defines a +variable called foo in the global scope and script B defines a foo in the +current scope.

+ +

Again, that is not at all the same effect, not using var can have major +implications.

+ +
// global scope
+var foo = 42;
+function test() {
+    // local scope
+    foo = 21;
+}
+test();
+foo; // 21
+
+ +

Leaving out the var statement inside the function test will override the +value of foo. While this might not seem like a big deal at first, having +thousands of lines of JavaScript and not using var will introduce horrible and +hard to track down bugs.

+ +
// global scope
+var items = [/* some list */];
+for(var i = 0; i < 10; i++) {
+    subLoop();
+}
+
+function subLoop() {
+    // scope of subLoop
+    for(i = 0; i < 10; i++) { // missing var statement
+        // do amazing stuff!
+    }
+}
+
+ +

The outer loop will terminate after the first call to subLoop, since subLoop +overwrites the global value of i. Using a var for the second for loop would +have easily avoided this error. The var statement should never be left out +unless the desired effect is to affect the outer scope.

+ +

Local variables

+ +

The only source for local variables in JavaScript are +function parameters and variables that were declared via the +var statement.

+ +
// global scope
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+    // local scope of the function test
+    i = 5;
+
+    var foo = 3;
+    bar = 4;
+}
+test(10);
+
+ +

While foo and i are local variables inside the scope of the function test, +the assignment of bar will override the global variable with the same name.

+ +

Hoisting

+ +

JavaScript hoists declarations. This means that both var statements and +function declarations will be moved to the top of their enclosing scope.

+ +
bar();
+var bar = function() {};
+var someValue = 42;
+
+test();
+function test(data) {
+    if (false) {
+        goo = 1;
+
+    } else {
+        var goo = 2;
+    }
+    for(var i = 0; i < 100; i++) {
+        var e = data[i];
+    }
+}
+
+ +

The above code gets transformed before any execution is started. JavaScript moves +the var statements as well as the function declarations to the top of the +nearest surrounding scope.

+ +
// var statements got moved here
+var bar, someValue; // default to 'undefined'
+
+// the function declartion got moved up too
+function test(data) {
+    var goo, i, e; // missing block scope moves these here
+    if (false) {
+        goo = 1;
+
+    } else {
+        goo = 2;
+    }
+    for(i = 0; i < 100; i++) {
+        e = data[i];
+    }
+}
+
+bar(); // fails with a TypeError since bar is still 'undefined'
+someValue = 42; // assignments are not affected by hoisting
+bar = function() {};
+
+test();
+
+ +

Missing block scoping will not only move var statements out of loops and +their bodies, it will also make the results of certain if constructs +non-intuitive.

+ +

In the original code the if statement seemed to modify the global +variable goo, while actually it modifies the local variable - after hoisting +has been applied.

+ +

Without the knowledge about hoisting, below code might seem to raise a +ReferenceError.

+ +
// check whether SomeImportantThing has been initiliazed
+if (!SomeImportantThing) {
+    var SomeImportantThing = {};
+}
+
+ +

But of course, the above works due to the fact that the var statement is being +moved to the top of the global scope.

+ +
var SomeImportantThing;
+
+// other code might initiliaze SomeImportantThing here, or not
+
+// make sure it's there
+if (!SomeImportantThing) {
+    SomeImportantThing = {};
+}
+
+ +

Name resolution order

+ +

All scopes in JavaScript, including the global scope, have the special name +this defined in them, which refers to the current object.

+ +

Function scopes also have the name arguments defined in +them which contains the arguments that were passed to a function.

+ +

For example, when trying to access a variable named foo inside the scope of a +function, JavaScript will lookup the name in the following order:

+ +
    +
  1. In case there is a var foo statement in the current scope use that.
  2. +
  3. If one of the function parameters is named foo use that.
  4. +
  5. If the function itself is called foo use that.
  6. +
  7. Go to the next outer scope and start with #1 again.
  8. +
+ + + +

Namespaces

+ +

A common problem of having only one global namespace is the likeliness of running +into problems where variable names clash. In JavaScript, this problem can +easily be avoided with the help of anonymous wrappers.

+ +
(function() {
+    // a self contained "namespace"
+
+    window.foo = function() {
+        // an exposed closure
+    };
+
+})(); // execute the function immediately
+
+ +

Unnamed functions are considered expressions; so in order to +being callable, they must first be evaluated.

+ +
( // evaluate the function inside the paranthesis
+function() {}
+) // and return the function object
+() // call the result of the evaluation
+
+ +

There are other ways for evaluating and calling the function expression; which, +while different in syntax, do behave the exact same way.

+ +
// Two other ways
++function(){}();
+(function(){}());
+
+ +

In conclusion

+ +

It is recommended to always use an anonymous wrapper for encapsulating code in +its own namespace. This does not only protect code against name clashes, it +also allows for better modularization of programs.

+ +

Additionally, the use of global variables is considered bad practice. Any +use of them indicates badly written code that is prone to errors and hard to maintain.

Arrays

Array Iteration and Properties

Although arrays in JavaScript are objects, there are no good reasons to use +the for in loop in for iteration on them. In fact there +are a number of good reasons against the use of for in on arrays.

+ + + +

Since the for in loop enumerates all the properties that are on the prototype +chain and the only way to exclude those properties is to use +hasOwnProperty, it is already up to twenty times +slower than a normal for loop.

+ +

Iteration

+ +

In order to achieve the best performance when iterating over arrays, it is best +to use the classic for loop.

+ +
var list = [1, 2, 3, 4, 5, ...... 100000000];
+for(var i = 0, l = list.length; i < l; i++) {
+    console.log(list[i]);
+}
+
+ +

There is one extra catch in the above example, that is the caching of the +length of the array via l = list.length.

+ +

Although the length property is defined on the array itself, there is still an +overhead for doing the lookup on each iteration of the loop. And while recent +JavaScript engines may apply optimization in this case, there is no way of +telling whether the code will run on one of these newer engines or not.

+ +

In fact, leaving out the caching may result in the loop being only half as +fast as with the cached length.

+ +

The length property

+ +

While the getter of the length property simply returns the number of +elements that are contained in the array, the setter can be used to +truncate the array.

+ +
var foo = [1, 2, 3, 4, 5, 6];
+foo.length = 3;
+foo; // [1, 2, 3]
+
+foo.length = 6;
+foo; // [1, 2, 3]
+
+ +

Assigning a smaller length does truncate the array, but increasing the length +does not have any effect on the array.

+ +

In conclusion

+ +

For the best performance it is recommended to always use the plain for loop +and cache the length property. The use of for in on an array is a sign of +badly written code that is prone to bugs and bad performance.

The Array Constructor

Since the Array constructor is ambiguous in how it deals with its parameters, +it is highly recommended to always use the array literals - [] notation - +when creating new arrays.

+ +
[1, 2, 3]; // Result: [1, 2, 3]
+new Array(1, 2, 3); // Result: [1, 2, 3]
+
+[3]; // Result: [3]
+new Array(3); // Result: []
+new Array('3') // Result: ['3']
+
+ +

In cases when there is only one argument passed to the Array constructor, +and that argument is a Number, the constructor will return a new sparse +array with the length property set to the value of the argument. It should be +noted that only the length property of the new array will be set this way, +the actual indexes of the array will not be initialized.

+ +
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // false, the index was not set
+
+ +

The behavior of being able to set the length of the array upfront only comes in +handy in a few cases, like repeating a string, in which it avoids the use of a +for loop code.

+ +
new Array(count + 1).join(stringToRepeat);
+
+ +

In conclusion

+ +

The use of the Array constructor should be avoided as much as possible. +Literals are definitely preferred. They are shorter and have a clearer syntax; +therefore, they also increase the readability of the code.

Types

Equality and comparisons

JavaScript has two different ways of comparing the values of objects for equality.

+ +

The equals operator

+ +

The equals operator consists of two equal signs: ==

+ +

JavaScript features weak typing, that means, that the equals 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 equals operator

+ +

The strict equals operator consists of three equal signs: ===

+ +

Other than the normal equals operator, the strict equals operator does not +perform type coercion between its operands.

+ +
""           ===   "0"           // false
+0            ===   ""            // false
+0            ===   "0"           // false
+false        ===   "false"       // false
+false        ===   "0"           // false
+false        ===   undefined     // false
+false        ===   null          // false
+null         ===   undefined     // false
+" \t\r\n"    ===   0             // false
+
+ +

The above results are a lot clearer and allow for early breakage of code. This +hardens code to a certain degree and also gives performance improvements in case +the operands are of different types.

+ +

Comparing objects

+ +

While both == and === are stated as equality operators, they behave +different when at least one of their operands happens to be an Object.

+ +
{} === {};                   // false
+new String('foo') === 'foo'; // false
+new Number(10) === 10;       // false
+var foo = {};
+foo === foo;                 // true
+
+ +

Here both operators compare for identity and not equality; that is, they +will compare for the same instance of the object, much like is in Python +and a pointer comparison in C do.

+ +

In conclusion

+ +

It is highly recommended to only use the strict equals operator. In cases +where types need to be coerced, it should be done explicitly +and not left to the language's complicated coercion rules.

The typeof operator

The typeof operator (together with +instanceof) is probably the biggest +design flaw of JavaScript, as it is near of being completely broken.

+ +

Although instanceof still has its limited uses, typeof really has only one +practical use case, which does not happen to be checking the type of an +object.

+ + + +

The JavaScript type table

+ +
Value               Class      Type
+-------------------------------------
+"foo"               String     string
+new String("foo")   String     object
+1.2                 Number     number
+new Number(1.2)     Number     object
+true                Boolean    boolean
+new Boolean(true)   Boolean    object
+new Date()          Date       object
+new Error()         Error      object
+[1,2,3]             Array      object
+new Array(1, 2, 3)  Array      object
+new Function("")    Function   function
+/abc/g              RegExp     object (function in Nitro/V8)
+new RegExp("meow")  RegExp     object (function in Nitro/V8)
+{}                  Object     object
+new Object()        Object     object
+
+ +

In the above table Type refers to the value, that the typeof operator returns. +As can be clearly seen, this value is anything but consistent.

+ +

The Class refers to the value of the internal [[Class]] property of an object.

+ + + +

In order to retrieve the value of [[Class]] one has to make use of the +toString method of Object.prototype.

+ +

The Class of an object

+ +

The specification gives exactly one way of accessing the [[Class]] value, +with the use of Object.prototype.toString.

+ +
function is(type, obj) {
+    var clas = Object.prototype.toString.call(obj).slice(8, -1);
+    return obj !== undefined && obj !== null && clas === type;
+}
+
+is('String', 'test'); // true
+is('String', new String('test')); // true
+
+ +

In the above example, Object.prototype.toString gets called with the value of +this being set to the object whose [[Class]] value should be +retrieved.

+ + + +

Testing for undefined variables

+ +
typeof foo !== 'undefined'
+
+ +

The above will check whether foo was actually declared or not; just +referencing it would result in a ReferenceError. This is the only thing +typeof is actually useful for.

+ +

In conclusion

+ +

In order to check the type of an object, it is highly recommended to use +Object.prototype.toString; as this is the only reliable way of doing so. +As shown in the above type table, some return values of typeof are not defined +in the specification; thus, they can differ across various implementations.

+ +

Unless checking whether a variable is defined, typeof should be avoided at +all costs.

The instanceof operator

The instanceof operator compares the constructors of its two operands. It is +only useful when comparing custom made objects. Used on built-in types, it is +nearly as useless as the typeof operator.

+ +

Comparing custom objects

+ +
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // true
+new Bar() instanceof Foo; // true
+
+// This just sets Bar.prototype to the function object Foo
+// But not to an actual instance of Foo
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // false
+
+ +

Using instanceof with native types

+ +
new String('foo') instanceof String; // true
+new String('foo') instanceof Object; // true
+
+'foo' instanceof String; // false
+'foo' instanceof Object; // false
+
+ +

One important thing to note here is, that instanceof does not work on objects +that origin from different JavaScript contexts (e.g. different documents +in a web browser), since their constructors will not be the exact same object.

+ +

In conclusion

+ +

The instanceof operator should only be used when dealing with custom made +objects that origin from the same JavaScript context. Just like the +typeof operator, every other use of it should be avoided.

Type casting

JavaScript is a weakly typed language, so it will apply type coercion +wherever possible.

+ +
// These are true
+new Number(10) == 10; // Number.toString() is converted
+                      // back to a number
+
+10 == '10';           // Strings gets converted to Number
+10 == '+10 ';         // More string madness
+10 == '010';          // And more 
+isNaN(null) == false; // null converts to 0
+                      // which of course is not NaN
+
+// These are false
+10 == 010;
+10 == '-10';
+
+ + + +

In order to avoid the above, use of the strict equal operator +is highly recommended. Although this avoids a lot of common pitfalls, there +are still many further issues that arise from JavaScript's weak typing system.

+ +

Constructors of built-in types

+ +

The constructors of the built in types like Number and String behave +differently when being used with the new keyword and without it.

+ +
new Number(10) === 10;     // False, Object and Number
+Number(10) === 10;         // True, Number and Number
+new Number(10) + 0 === 10; // True, due to implicit conversion
+
+ +

Using a built-in type like Number as a constructor will create a new Number +object, but leaving out the new keyword will make the Number function behave +like a converter.

+ +

In addition, having literals or non-object values in there will result in even +more type coercion.

+ +

The best option is to cast to one of the three possible types explicitly.

+ +

Casting to a string

+ +
'' + 10 === '10'; // true
+
+ +

By prepending a empty string a value can easily be casted to a string.

+ +

Casting to a number

+ +
+'10' === 10; // true
+
+ +

Using the unary plus operator it is possible to cast to a number.

+ +

Casting to a boolean

+ +

By using the not operator twice, a value can be converted a boolean.

+ +
!!'foo';   // true
+!!'';      // false
+!!'0';     // true
+!!'1';     // true
+!!'-1'     // true
+!!{};      // true
+!!true;    // true
+

Core

Why not to use eval

The eval function will execute a string of JavaScript code in the local scope.

+ +
var foo = 1;
+function test() {
+    var foo = 2;
+    eval('foo = 3');
+    return foo;
+}
+test(); // 3
+foo; // 1
+
+ +

But eval only executes in local scope when it is being called directly and +the name of the called function is actually eval.

+ +
var foo = 1;
+function test() {
+    var foo = 2;
+    var bar = eval;
+    bar('foo = 3');
+    return foo;
+}
+test(); // 2
+foo; // 3
+
+ +

The use of eval should be avoided at all costs. 99.9% of its "uses" can be +achieved without it.

+ +

eval in disguise

+ +

The timeout functions setTimeout and setInterval can both +take a string as their first argument. This string will always get executed +in the global scope since eval is not being called directly in that case.

+ +

Security issues

+ +

eval also is a security problem as it executes any code given to it, +it should never be used with strings of unknown or untrusted origins.

+ +

In conclusion

+ +

eval should never be used, any code that makes use of it is to be questioned in +its workings, performance and security. In case something requires eval in +order to work, its design is to be questioned and should not be used in the +first place, a better design should be used, that does not require the use of +eval.

undefined and null

JavaScript has two distinct values for nothing, the more useful of these two +being undefined.

+ +

The value undefined

+ +

undefined is a type with exactly one value: undefined.

+ +

The language also defines a global variable that has the value of undefined, +this variable is also called undefined. But this variable is not a constant, +nor is it a keyword of the language. This means that its value can be easily +overwritten.

+ + + +

Some examples for when the value undefined is returned:

+ +
    +
  • Accessing the (unmodified) global variable undefined.
  • +
  • Implicit returns of functions due to missing return statements.
  • +
  • return statements which do not explicitly return anything.
  • +
  • Lookups of non-existent properties.
  • +
  • Function parameters which do not had any explicit value passed.
  • +
  • Anything that has been set to the value of undefined.
  • +
+ +

Handling changes to the value of undefined

+ +

Since the global variable undefined only holds a copy of the actual value of +undefined, assigning a new value to it does not change the value of the +type undefined.

+ +

Still, in order to compare something against the value of undefined it is +necessary to retrieve the value of undefined first.

+ +

In order to protect code against a possible overwritten undefined variable, a +common technique used is to add an additional parameter to an +anonymous wrapper, that gets no argument passed to it.

+ +
var undefined = 123;
+(function(something, foo, undefined) {
+    // undefined in the local scope does 
+    // now again refer to the value
+
+})('Hello World', 42);
+
+ +

Another way to achieve the same effect would be to use a declaration inside the +wrapper.

+ +
var undefined = 123;
+(function(something, foo) {
+    var undefined;
+    ...
+
+})('Hello World', 42);
+
+ +

The only difference being here, that this version results in 4 more bytes being +used in case it is minified and there is no other var statement inside the +anonymous wrapper.

+ +

Uses of null

+ +

While undefined in the context of the JavaScript language is mostly used in +the sense of a traditional null, the actual null (both a literal and a type) +is more or less just another data type.

+ +

It is used in some JavaScript internals (like declaring the end of the +prototype chain by setting Foo.prototype = null), but in almost all cases it +can be replaced by undefined.

Automatic semicolon insertion

Although JavaScript has C style syntax, it does not enforce the use of +semicolons in the source code, it is possible to omit them.

+ +

But JavaScript is not a semicolon-less language, it in fact needs the +semicolons in order to understand the source code. Therefore the JavaScript +parser automatically inserts them whenever it encounters a parse +error due to a missing semicolon.

+ +
var foo = function() {
+} // parse error, semicolon expected
+test()
+
+ +

Insertion happens, and the parser tries again.

+ +
var foo = function() {
+}; // no error, parser continues
+test()
+
+ +

The automatic insertion of semicolon is considered to be one of biggest +design flaws in the language, as it can change the behavior of code.

+ +

How it works

+ +

The code below has no semicolons in it, so it is up to the parser to decide where +to insert them.

+ +
(function(window, undefined) {
+    function test(options) {
+        log('testing!')
+
+        (options.list || []).forEach(function(i) {
+
+        })
+
+        options.value.test(
+            'long string to pass here',
+            'and another long string to pass'
+        )
+
+        return
+        {
+            foo: function() {}
+        }
+    }
+    window.test = test
+
+})(window)
+
+(function(window) {
+    window.someLibrary = {}
+
+})(window)
+
+ +

Below is the result of the parser's "guessing" game.

+ +
(function(window, undefined) {
+    function test(options) {
+
+        // Not inserted, lines got merged
+        log('testing!')(options.list || []).forEach(function(i) {
+
+        }); // <- inserted
+
+        options.value.test(
+            'long string to pass here',
+            'and another long string to pass'
+        ); // <- inserted
+
+        return; // <- inserted, breaks the return statement
+        { // treated as a block
+
+            // a label and a single expression statement
+            foo: function() {} 
+        }; // <- inserted
+    }
+    window.test = test; // <- inserted
+
+// The lines got merged again
+})(window)(function(window) {
+    window.someLibrary = {}; // <- inserted
+
+})(window); //<- inserted
+
+ + + +

The parser drastically changed the behavior of the code above, in certain cases +it does the wrong thing.

+ +

Leading parenthesis

+ +

In case of a leading parenthesis, the parser will not insert a semicolon.

+ +
log('testing!')
+(options.list || []).forEach(function(i) {})
+
+ +

This code gets transformed into one line.

+ +
log('testing!')(options.list || []).forEach(function(i) {})
+
+ +

Chances are very high that log does not return a function; therefore, +the above will yield a TypeError stating that undefined is not a function.

+ +

In conclusion

+ +

It is highly recommended to never omit semicolons, it is also advocated to +keep braces on the same line with their corresponding statements and to never omit +them for one single-line if / else statements. Both of these measures will +not only improve the consistency of the code, they will also prevent the +JavaScript parser from changing its behavior.

Other

setTimeout and setInterval

Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the setTimeout and setInterval functions.

+ + + +
function foo() {}
+var id = setTimeout(foo, 1000); // returns a Number > 0
+
+ +

When setTimeout gets called, it will return the ID of the timeout and schedule +foo to run in approximately one thousand milliseconds in the future. +foo will then get executed exactly once.

+ +

Depending on the timer resolution of the JavaScript engine that is running the +code, as well as the fact that JavaScript is single threaded and other code that +gets executed might block the thread, it is by no means a safe bet that one +will get the exact delay that was specified in the setTimeout call.

+ +

The function that was passed as the first parameter will get called by the +global object, that means, that this inside the called function +refers to that very object.

+ +
function Foo() {
+    this.value = 42;
+    this.method = function() {
+        // this refers to the global object
+        console.log(this.value); // will log undefined
+    };
+    setTimeout(this.method, 500);
+}
+new Foo();
+
+ + + +

Stacking calls with setInterval

+ +

While setTimeout only runs the function once, setInterval - as the name +suggests - will execute the function every X milliseconds. But its use is +discouraged.

+ +

When code that is being executed blocks the timeout call, setInterval will +still issue more calls to the specified function. This can, especially with small +intervals, result in function calls stacking up.

+ +
function foo(){
+    // something that blocks for 1 second
+}
+setInterval(foo, 100);
+
+ +

In the above code foo will get called once and will then block for one second.

+ +

While foo blocks the code setInterval will still schedule further calls to +it. Now, when foo has finished, there will already be ten further calls to +it waiting for execution.

+ +

Dealing with possible blocking code

+ +

The easiest as well as most controllable solution, is to use setTimeout within +the function itself.

+ +
function foo(){
+    // something that blocks for 1 second
+    setTimeout(foo, 100);
+}
+foo();
+
+ +

Not only does this encapsulate the setTimeout call, but it also prevents the +stacking of calls and it gives additional control.foo itself can now decide +whether it wants to run again or not.

+ +

Manually clearing timeouts

+ +

Clearing timeouts and intervals works by passing the respective ID to +clearTimeout or clearInterval, depending which set function was used in +the first place.

+ +
var id = setTimeout(foo, 1000);
+clearTimeout(id);
+
+ +

Clearing all timeouts

+ +

As there is no built-in method for clearing all timeouts and/or intervals, +it is necessary to use brute force in order to achieve this functionality.

+ +
// clear "all" timeouts
+for(var i = 1; i < 1000; i++) {
+    clearTimeout(i);
+}
+
+ +

There might still be timeouts that are unaffected by this arbitrary number; +therefore, is is instead recommended to keep track of all the timeout IDs, so +they can be cleared specifically.

+ +

Hidden use of eval

+ +

setTimeout and setInterval can also take a string as their first parameter. +This feature should never be used, since it internally makes use of eval.

+ + + +
function foo() {
+    // will get called
+}
+
+function bar() {
+    function foo() {
+        // never gets called
+    }
+    setTimeout('foo()', 1000);
+}
+bar();
+
+ +

Since eval is not getting called directly in this case, the string +passed to setTimeout will get executed in the global scope; thus, it will +not use the local variable foo from the scope of bar.

+ +

It is further recommended to not use a string for passing arguments to the +function that will get called by either of the timeout functions.

+ +
function foo(a, b, c) {}
+
+// NEVER use this
+setTimeout('foo(1,2, 3)', 1000)
+
+// Instead use an anonymous function
+setTimeout(function() {
+    foo(a, b, c);
+}, 1000)
+
+ + + +

In conclusion

+ +

Never should a string be used as the parameter of setTimeout or +setInterval. It is a clear sign of really bad code, when arguments need +to be supplied to the function that gets called. An anonymous function should +be passed that then takes care of the actual call.

+ +

Further, the use of setInterval should be avoided since its scheduler is not +blocked by executing JavaScript.

Copyright © 2011. Built with +Node.jsusing a +jadetemplate. +

\ No newline at end of file From dd2f117cb8cfa8d17b07412c49c8cfd29dc1f4aa Mon Sep 17 00:00:00 2001 From: "sanshi.ustc" Date: Mon, 21 Mar 2011 21:53:47 +0800 Subject: [PATCH 093/641] Add forinloop section --- doc/zh/object/forinloop.md | 45 +++++++++++++++----------------------- site/zh/index.html | 43 ++++++++++++++---------------------- 2 files changed, 34 insertions(+), 54 deletions(-) diff --git a/doc/zh/object/forinloop.md b/doc/zh/object/forinloop.md index 88411e97..9c594dcc 100755 --- a/doc/zh/object/forinloop.md +++ b/doc/zh/object/forinloop.md @@ -1,51 +1,42 @@ -## The `for in` Loop +## `for in` 循环(The `for in` Loop) -Just like the `in` operator, the `for in` loop also traverses the prototype -chain when iterating over the properties of an object. +和 `in` 操作符一样,`for in` 循环同样在查找对象属性时遍历原型链上的所有属性。 -> **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. +> **注意:** `for in` 循环**不会**遍历那些 `enumerable` 设置为 `false` 的属性;比如数组的 `length` 属性。 - // Poisoning Object.prototype + // 修改 Object.prototype Object.prototype.bar = 1; var foo = {moo: 2}; for(var i in foo) { - console.log(i); // prints both bar and moo + console.log(i); // 输出两个属性:bar 和 moo } -Since it is not possible to change the behavior of the `for in` loop itself, it -is necessary to filter out the unwanted properties inside the loop body , -this is done by using the [`hasOwnProperty`](#object.hasownproperty) method of -`Object.prototype`. +由于不可能改变 `for in` 自身的行为,因此有必要过滤出那些不希望出现在循环体中的属性, +这可以通过 `Object.prototype` 原型上的 [`hasOwnProperty`](#object.hasownproperty) 函数来完成。 -> **Note:** Since the `for in` always traverses the complete prototype chain, it -> will get slower with each additional layer of inheritance added to an object. +> **注意:** 由于 `for in` 总是要遍历整个原型链,因此如果一个对象的继承层次太深的话会影响性能。 -### Using `hasOwnProperty` for filtering +### 使用 `hasOwnProperty` 过滤(Using `hasOwnProperty` for filtering) - // still the foo from above + // foo 变量是上例中的 for(var i in foo) { if (foo.hasOwnProperty(i)) { console.log(i); } } -This version is the only correct one to use. Due to the use of `hasOwnProperty` it -will **only** print out `moo`. When `hasOwnProperty` is left out, the code is -prone to errors in cases where the native prototypes - e.g. `Object.prototype` - -have been extended. +这个版本的代码是唯一正确的写法。由于我们使用了 `hasOwnProperty`,所以这次**只**输出 `moo`。 +如果不使用 `hasOwnProperty`,则这段代码在原生对象原型(比如 `Object.prototype`)被扩展时可能会出错。 -One widely used framework which does this is [Prototype][1]. When this -framework is included, `for in` loops that do not use `hasOwnProperty` are -guaranteed to break. +一个广泛使用的类库 [Prototype][1] 就扩展了原生的 JavaScript 对象。 +因此,但这个类库被包含在页面中时,不使用 `hasOwnProperty` 过滤的 `for in` 循环难免会出问题。 -### Best practices -It is recommended to **always** use `hasOwnProperty`. Never should any -assumptions be made about the environment the code is running in, or whether the -native prototypes have been extended or not. +### 最佳实践(Best practices) + +推荐**总是**使用 `hasOwnProperty`。不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。 + [1]: http://www.prototypejs.org/ diff --git a/site/zh/index.html b/site/zh/index.html index c52c4a95..2be82a47 100644 --- a/site/zh/index.html +++ b/site/zh/index.html @@ -1,7 +1,7 @@ JavaScript Garden -

Intro

JavaScript 秘密花园是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 +

Intro

JavaScript 秘密花园是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, 初学者可以籍此深入了解 JavaScript 的语言特性。

@@ -251,37 +251,31 @@

中文翻译(Chinese Translation)

当检查对象上某个属性是否存在时,hasOwnProperty唯一可用的方法。 同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法, -这将会避免原型对象扩展带来的干扰。

The for in Loop

Just like the in operator, the for in loop also traverses the prototype -chain when iterating over the properties of an object.

+这将会避免原型对象扩展带来的干扰。

 for in 循环(The for in Loop)

in 操作符一样,for in 循环同样在查找对象属性时遍历原型链上的所有属性。

-
// Poisoning Object.prototype
+
// 修改 Object.prototype
 Object.prototype.bar = 1;
 
 var foo = {moo: 2};
 for(var i in foo) {
-    console.log(i); // prints both bar and moo
+    console.log(i); // 输出两个属性:bar 和 moo
 }
 
-

Since it is not possible to change the behavior of the for in loop itself, it -is necessary to filter out the unwanted properties inside the loop body , -this is done by using the hasOwnProperty method of -Object.prototype.

+

由于不可能改变 for in 自身的行为,因此有必要过滤出那些不希望出现在循环体中的属性, +这可以通过 Object.prototype 原型上的 hasOwnProperty 函数来完成。

-

Using hasOwnProperty for filtering

+

使用 hasOwnProperty 过滤(Using hasOwnProperty for filtering)

-
// still the foo from above
+
// foo 变量是上例中的
 for(var i in foo) {
     if (foo.hasOwnProperty(i)) {
         console.log(i);
@@ -289,20 +283,15 @@ 

中文翻译(Chinese Translation)

}
-

This version is the only correct one to use. Due to the use of hasOwnProperty it -will only print out moo. When hasOwnProperty is left out, the code is -prone to errors in cases where the native prototypes - e.g. Object.prototype - -have been extended.

+

这个版本的代码是唯一正确的写法。由于我们使用了 hasOwnProperty,所以这次输出 moo。 +如果不使用 hasOwnProperty,则这段代码在原生对象原型(比如 Object.prototype)被扩展时可能会出错。

-

One widely used framework which does this is Prototype. When this -framework is included, for in loops that do not use hasOwnProperty are -guaranteed to break.

+

一个广泛使用的类库 Prototype 就扩展了原生的 JavaScript 对象。 +因此,但这个类库被包含在页面中时,不使用 hasOwnProperty 过滤的 for in 循环难免会出问题。

-

Best practices

+

最佳实践(Best practices)

-

It is recommended to always use hasOwnProperty. Never should any -assumptions be made about the environment the code is running in, or whether the -native prototypes have been extended or not.

Functions

 函数声明与表达式(Function Declarations and Expressions)

函数是JavaScript中的一等对象,这意味着可以把函数像其它值一样传递。 +

推荐总是使用 hasOwnProperty。不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。

Functions

 函数声明与表达式(Function Declarations and Expressions)

函数是JavaScript中的一等对象,这意味着可以把函数像其它值一样传递。 一个常见的用法是把匿名函数作为回调函数传递对异步函数中。

函数声明(The function declaration)

From 6320fef0f1413b291104a5be7fa9d3f33b977d96 Mon Sep 17 00:00:00 2001 From: "sanshi.ustc" Date: Mon, 21 Mar 2011 21:58:17 +0800 Subject: [PATCH 094/641] Add scopes section --- doc/zh/function/scopes.md | 195 ++++++++++++++++++-------------------- site/zh/index.html | 190 +++++++++++++++++-------------------- 2 files changed, 181 insertions(+), 204 deletions(-) diff --git a/doc/zh/function/scopes.md b/doc/zh/function/scopes.md index 95735b07..567b95b6 100755 --- a/doc/zh/function/scopes.md +++ b/doc/zh/function/scopes.md @@ -1,29 +1,33 @@ -## Scopes and Namespaces +## 作用域与命名空间(Scopes and namespaces) -Although JavaScript deals fine with the syntax of two matching curly -braces for blocks, it does **not** support block scope; hence, all that is left -is in the language is *function scope*. +尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; +而仅仅支持 *函数作用域*。 - function test() { // a scope - for(var i = 0; i < 10; i++) { // not a scope + function test() { // 一个作用域 + for(var i = 0; i < 10; i++) { // 不是一个作用域 // 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. +> **注意:** 如果不是在赋值语句中,而是在 return 表达式或者函数参数中,`{...}` 将会作为代码段解析, +> 而不是作为对象的字面语法解析。如果考虑到 [自动分号插入](#semicolon),这可能会导致一些不易察觉的错误。 -There are also no distinct namespaces in JavaScript, that means that everything -gets defined in one *globally shared* namespace. +译者注:如果 return 对象的左括号和 return 不在一行上就会出错。 + + // 译者注:下面输出 undefined + function add(a, b) { + return + a + b; + } + console.log(add(1, 2)); -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`. +JavaScript 中没有显式的命名空间定义,这就意味着所有对象都定义在一个*全局共享*的命名空间下面。 -### The bane of global variables +每次引用一个变量,JavaScript 会向上遍历整个作用域直到找到这个变量为止。 +如果到达全局作用域但是这个变量仍未找到,则会抛出 `ReferenceError` 异常。 + +### 隐式的全局变量(The bane of global variables) // script A foo = '42'; @@ -31,58 +35,50 @@ still has not found the requested name, it will raise a `ReferenceError`. // 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. +上面两段脚本效果**不同**。脚本 A 在*全局*作用域内定义了变量 `foo`,而脚本 B 在*当前*作用域内定义变量 `foo`。 -Again, that is **not** at all the *same effect*, not using `var` can have major -implications. +再次强调,上面的效果**完全不同**,不使用 `var` 声明变量将会导致隐式的全局变量产生。 - // global scope + // 全局作用域 var foo = 42; function test() { - // local scope + // 局部作用域 foo = 21; } test(); foo; // 21 -Leaving out the `var` statement inside the function `test` will override the -value of `foo`. While this might not seem like a big deal at first, having -thousands of lines of JavaScript and not using `var` will introduce horrible and -hard to track down bugs. +在函数 `test` 内不使用 `var` 关键字声明 `foo` 变量将会覆盖外部的同名变量。 +起初这看起来并不是大问题,但是当有成千上万行代码时,不使用 `var` 声明变量将会带来难以跟踪的 BUG。 - // 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 + // subLoop 函数作用域 + for(i = 0; i < 10; i++) { // 没有使用 var 声明变量 // 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 +外部循环在第一次调用 `subLoop` 之后就会终止,因为 `subLoop` 覆盖了全局变量 `i`。 +在第二个 `for` 循环中使用 `var` 声明变量可以避免这种错误。 +声明变量时**绝对不要**遗漏 `var` 关键字,除非这就是*期望*的影响外部作用域的行为。 + +### 局部变量(Local variables) -The only source for local variables in JavaScript are -[function](#function.general) parameters and variables that were declared via the -`var` statement. +JavaScript 中局部变量只可能通过两种方式声明,一个是作为[函数](#functions)参数,另一个是通过 `var` 关键字声明。 - // global scope + // 全局变量 var foo = 1; var bar = 2; var i = 2; function test(i) { - // local scope of the function test + // 函数 test 内的局部作用域 i = 5; var foo = 3; @@ -90,13 +86,11 @@ The only source for local variables in JavaScript are } 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. +`foo` 和 `i` 是函数 `test` 内的局部变量,而对 `bar` 的赋值将会覆盖全局作用域内的同名变量。 -### Hoisting +### 变量声明提升(Hoisting) -JavaScript **hoists** declarations. This means that both `var` statements and -`function` declarations will be moved to the top of their enclosing scope. +JavaScript 会**提升**变量声明。这意味着 `var` 表达式和 `function` 声明都将会被提升到当前作用域的顶部。 bar(); var bar = function() {}; @@ -115,16 +109,14 @@ JavaScript **hoists** declarations. This means that both `var` statements and } } -The above code gets transformed before any execution is started. JavaScript moves -the `var` statements as well as the `function` declarations to the top of the -nearest surrounding scope. +上面代码在运行之前将会被转化。JavaScript 将会把 `var` 表达式和 `function` 声明提升到当前作用域的顶部。 - // var statements got moved here - var bar, someValue; // default to 'undefined' + // var 表达式被移动到这里 + var bar, someValue; // 缺省值是 'undefined' - // the function declartion got moved up too + // 函数声明也会提升 function test(data) { - var goo, i, e; // missing block scope moves these here + var goo, i, e; // 没有块级作用域,这些变量被移动到函数顶部 if (false) { goo = 1; @@ -136,96 +128,93 @@ nearest surrounding scope. } } - bar(); // fails with a TypeError since bar is still 'undefined' - someValue = 42; // assignments are not affected by hoisting + bar(); // 出错:TypeError,因为 bar 依然是 'undefined' + someValue = 42; // 赋值语句不会被提升规则(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. +没有块级作用域不仅导致 `var` 表达式被从循环内移到外部,而且使一些 `if` 表达式更难看懂。 -In the original code the `if` statement seemed to modify the *global -variable* `goo`, while actually it modifies the *local variable* - after hoisting -has been applied. +在原来代码中,`if` 表达式看起来修改了*全部变量* `goo`,实际上在提升规则(hoisting)被应用后,却是在修改*局部变量*。 -Without the knowledge about *hoisting*, below code might seem to raise a -`ReferenceError`. +如果没有提升规则(hoisting)的知识,下面的代码看起来会抛出异常 `ReferenceError`。 - // check whether SomeImportantThing has been initiliazed + // 检查 SomeImportantThing 是否已经被初始化 if (!SomeImportantThing) { var SomeImportantThing = {}; } -But of course, the above works due to the fact that the `var` statement is being -moved to the top of the *global scope*. +实际上,上面的代码正常运行,因为 `var` 表达式会被提升到*全局作用域*的顶部。 var SomeImportantThing; - // other code might initiliaze SomeImportantThing here, or not + // 其它一些代码,可能会初始化 SomeImportantThing,也可能不会 - // make sure it's there + // 检查是否已经被初始化 if (!SomeImportantThing) { SomeImportantThing = {}; } -### Name resolution order + +译者注:在 Nettuts+ 网站有一篇介绍 hoisting 的[文章][1],其中的代码很有启发性。 -All scopes in JavaScript, including the *global scope*, have the special name -[`this`](#function.this) defined in them, which refers to the *current object*. + // 译者注:来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则 + var myvar = 'my value'; + + (function() { + alert(myvar); // undefined + var myvar = 'local value'; + })(); + + +### 名称解析顺序(Name resolution order) -Function scopes also have the name [`arguments`](#function.arguments) defined in -them which contains the arguments that were passed to a function. +JavaScript 中的所有作用域,包括*全局作用域*,都有一个特别的名称 [`this`](#this) 指向当前对象。 -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: +函数作用域内也有默认的变量 [`arguments`](#arguments),其中包含了传递到函数中的参数。 - 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. +比如,当访问函数内的 `foo` 变量时,JavaScript 会按照下面顺序查找: -> **Note:** Having a parameter called `arguments` will **prevent** the creation -> of the default `arguments` object. + 1. 当前作用域内是否有 `var foo` 的定义。 + 2. 函数形式参数是否有使用 `foo` 名称的。 + 3. 函数自身是否叫做 `foo`。 + 4. 回溯到上一级作用域,然后从 **#1** 重新开始。 -### Namespaces +> **注意:** 自定义 `arguments` 参数将会阻止原生的 `arguments` 对象的创建。 -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*. +### 命名空间(Namespaces) + +只有一个全局作用域导致的常见错误是命名冲突。在 JavaScript中,这可以通过 *匿名包装器* 轻松解决。 (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. +匿名函数被认为是 [表达式](#functions);因此为了可调用性,它们首先会被执行(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 +### 结论(In conclusion) + +推荐使用*匿名包装器*(译者注:也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, +而且有利于程序的模块化。 -It is recommended to always use an *anonymous wrapper* for encapsulating code in -its own namespace. This does not only protect code against name clashes, it -also allows for better modularization of programs. +另外,使用全局变量被认为是**不好的习惯**。这样的代码倾向于产生错误和带来高的维护成本。 -Additionally, the use of global variables is considered **bad practice**. **Any** -use of them indicates badly written code that is prone to errors and hard to maintain. +[1]: http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/ \ No newline at end of file diff --git a/site/zh/index.html b/site/zh/index.html index 2be82a47..0671f9d2 100644 --- a/site/zh/index.html +++ b/site/zh/index.html @@ -1,7 +1,7 @@ JavaScript Garden -

Intro

JavaScript 秘密花园是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 +

Intro

JavaScript 秘密花园是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, 初学者可以籍此深入了解 JavaScript 的语言特性。

@@ -782,12 +782,11 @@

中文翻译(Chinese Translation)

reason to drop the use of prototypes altogether. In the end it comes down to which solution is better suited for the needs of the application, it is especially important to choose a specific style of object creation and stick -with it.

Scopes and Namespaces

Although JavaScript deals fine with the syntax of two matching curly -braces for blocks, it does not support block scope; hence, all that is left -is in the language is function scope.

+with it.

 作用域与命名空间(Scopes and namespaces)

尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; +而仅仅支持 函数作用域

-
function test() { // a scope
-    for(var i = 0; i < 10; i++) { // not a scope
+
function test() { // 一个作用域
+    for(var i = 0; i < 10; i++) { // 不是一个作用域
         // count
     }
     console.log(i); // 10
@@ -795,20 +794,26 @@ 

中文翻译(Chinese Translation)

-

There are also no distinct namespaces in JavaScript, that means that everything -gets defined in one globally shared namespace.

+

译者注:如果 return 对象的左括号和 return 不在一行上就会出错。

-

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.

+
// 译者注:下面输出 undefined
+function add(a, b) {
+    return 
+        a + b;
+}
+console.log(add(1, 2));
+
+ +

JavaScript 中没有显式的命名空间定义,这就意味着所有对象都定义在一个全局共享的命名空间下面。

-

The bane of global variables

+

每次引用一个变量,JavaScript 会向上遍历整个作用域直到找到这个变量为止。 +如果到达全局作用域但是这个变量仍未找到,则会抛出 ReferenceError 异常。

+ +

隐式的全局变量(The bane of global variables)

// script A
 foo = '42';
@@ -817,60 +822,52 @@ 

中文翻译(Chinese Translation)

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.

+

上面两段脚本效果不同。脚本 A 在全局作用域内定义了变量 foo,而脚本 B 在当前作用域内定义变量 foo

-

Again, that is not at all the same effect, not using var can have major -implications.

+

再次强调,上面的效果完全不同,不使用 var 声明变量将会导致隐式的全局变量产生。

-
// global scope
+
// 全局作用域
 var foo = 42;
 function test() {
-    // local scope
+    // 局部作用域
     foo = 21;
 }
 test();
 foo; // 21
 
-

Leaving out the var statement inside the function test will override the -value of foo. While this might not seem like a big deal at first, having -thousands of lines of JavaScript and not using var will introduce horrible and -hard to track down bugs.

+

在函数 test 内不使用 var 关键字声明 foo 变量将会覆盖外部的同名变量。 +起初这看起来并不是大问题,但是当有成千上万行代码时,不使用 var 声明变量将会带来难以跟踪的 BUG。

-
// 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
+    // subLoop 函数作用域
+    for(i = 0; i < 10; i++) { // 没有使用 var 声明变量
         // 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.

+

外部循环在第一次调用 subLoop 之后就会终止,因为 subLoop 覆盖了全局变量 i。 +在第二个 for 循环中使用 var 声明变量可以避免这种错误。 +声明变量时绝对不要遗漏 var 关键字,除非这就是期望的影响外部作用域的行为。

-

Local variables

+

局部变量(Local variables)

-

The only source for local variables in JavaScript are -function parameters and variables that were declared via the -var statement.

+

JavaScript 中局部变量只可能通过两种方式声明,一个是作为函数参数,另一个是通过 var 关键字声明。

-
// global scope
+
// 全局变量
 var foo = 1;
 var bar = 2;
 var i = 2;
 
 function test(i) {
-    // local scope of the function test
+    // 函数 test 内的局部作用域
     i = 5;
 
     var foo = 3;
@@ -879,13 +876,11 @@ 

中文翻译(Chinese Translation)

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.

+

fooi 是函数 test 内的局部变量,而对 bar 的赋值将会覆盖全局作用域内的同名变量。

-

Hoisting

+

变量声明提升(Hoisting)

-

JavaScript hoists declarations. This means that both var statements and -function declarations will be moved to the top of their enclosing scope.

+

JavaScript 会提升变量声明。这意味着 var 表达式和 function 声明都将会被提升到当前作用域的顶部。

bar();
 var bar = function() {};
@@ -905,16 +900,14 @@ 

中文翻译(Chinese Translation)

}
-

The above code gets transformed before any execution is started. JavaScript moves -the var statements as well as the function declarations to the top of the -nearest surrounding scope.

+

上面代码在运行之前将会被转化。JavaScript 将会把 var 表达式和 function 声明提升到当前作用域的顶部。

-
// var statements got moved here
-var bar, someValue; // default to 'undefined'
+
// var 表达式被移动到这里
+var bar, someValue; // 缺省值是 'undefined'
 
-// the function declartion got moved up too
+// 函数声明也会提升
 function test(data) {
-    var goo, i, e; // missing block scope moves these here
+    var goo, i, e; // 没有块级作用域,这些变量被移动到函数顶部
     if (false) {
         goo = 1;
 
@@ -926,107 +919,102 @@ 

中文翻译(Chinese Translation)

} } -bar(); // fails with a TypeError since bar is still 'undefined' -someValue = 42; // assignments are not affected by hoisting +bar(); // 出错:TypeError,因为 bar 依然是 'undefined' +someValue = 42; // 赋值语句不会被提升规则(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.

+

没有块级作用域不仅导致 var 表达式被从循环内移到外部,而且使一些 if 表达式更难看懂。

-

In the original code the if statement seemed to modify the global -variable goo, while actually it modifies the local variable - after hoisting -has been applied.

+

在原来代码中,if 表达式看起来修改了全部变量 goo,实际上在提升规则(hoisting)被应用后,却是在修改局部变量

-

Without the knowledge about hoisting, below code might seem to raise a -ReferenceError.

+

如果没有提升规则(hoisting)的知识,下面的代码看起来会抛出异常 ReferenceError

-
// check whether SomeImportantThing has been initiliazed
+
// 检查 SomeImportantThing 是否已经被初始化
 if (!SomeImportantThing) {
     var SomeImportantThing = {};
 }
 
-

But of course, the above works due to the fact that the var statement is being -moved to the top of the global scope.

+

实际上,上面的代码正常运行,因为 var 表达式会被提升到全局作用域的顶部。

var SomeImportantThing;
 
-// other code might initiliaze SomeImportantThing here, or not
+// 其它一些代码,可能会初始化 SomeImportantThing,也可能不会
 
-// make sure it's there
+// 检查是否已经被初始化
 if (!SomeImportantThing) {
     SomeImportantThing = {};
 }
 
-

Name resolution order

+

译者注:在 Nettuts+ 网站有一篇介绍 hoisting 的文章,其中的代码很有启发性。

+ +
// 译者注:来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则
+var myvar = 'my value';  
+
+(function() {  
+    alert(myvar); // undefined  
+    var myvar = 'local value';  
+})();  
+
+ +

名称解析顺序(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.

+

JavaScript 中的所有作用域,包括全局作用域,都有一个特别的名称 this 指向当前对象。

-

Function scopes also have the name arguments defined in -them which contains the arguments that were passed to a function.

+

函数作用域内也有默认的变量 arguments,其中包含了传递到函数中的参数。

-

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:

+

比如,当访问函数内的 foo 变量时,JavaScript 会按照下面顺序查找:

    -
  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. +
  9. 当前作用域内是否有 var foo 的定义。
  10. +
  11. 函数形式参数是否有使用 foo 名称的。
  12. +
  13. 函数自身是否叫做 foo
  14. +
  15. 回溯到上一级作用域,然后从 #1 重新开始。
-

Namespaces

+

命名空间(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.

+

只有一个全局作用域导致的常见错误是命名冲突。在 JavaScript中,这可以通过 匿名包装器 轻松解决。

(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.

+

匿名函数被认为是 表达式;因此为了可调用性,它们首先会被执行(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

+

结论(In conclusion)

-

It is recommended to always use an anonymous wrapper for encapsulating code in -its own namespace. This does not only protect code against name clashes, it -also allows for better modularization of programs.

+

推荐使用匿名包装器(译者注:也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, +而且有利于程序的模块化。

-

Additionally, the use of global variables is considered bad practice. Any -use of them indicates badly written code that is prone to errors and hard to maintain.

Arrays

Array Iteration and Properties

Although arrays in JavaScript are objects, there are no good reasons to use +

另外,使用全局变量被认为是不好的习惯。这样的代码倾向于产生错误和带来高的维护成本。

Arrays

Array Iteration and Properties

Although arrays in JavaScript are objects, there are no good reasons to use the for in loop in for iteration on them. In fact there are a number of good reasons against the use of for in on arrays.

From 9d2a559e3dcf4bdf87a7db650c61f04d3b01e699 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Mon, 21 Mar 2011 12:34:15 -0500 Subject: [PATCH 095/641] add package.json --- .gitignore | 1 + package.json | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 package.json diff --git a/.gitignore b/.gitignore index b918c19b..95b3c21d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /site/index.html /build /html +/node_modules /site/de /site/ru *.md~ diff --git a/package.json b/package.json new file mode 100644 index 00000000..d3d14e77 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "javascript-garden", + "description": "A collection of documentation about the most quirky parts of the JavaScript language.", + "version": "0.0.0", + "dependencies": { + "fomatto": "0.5.0", + "jade": "0.9.1", + "neko": "1.1.2", + "node-markdown": "0.1.0" + } +} From 821a9b5c3836db895dfeb109b4002563c1e72721 Mon Sep 17 00:00:00 2001 From: "sanshi.ustc" Date: Tue, 22 Mar 2011 23:17:20 +0800 Subject: [PATCH 096/641] Add equality array constructor sections --- doc/language.json | 2 +- doc/zh/array/constructor.md | 47 +- doc/zh/array/general.md | 63 +- doc/zh/function/arguments.md | 2 +- doc/zh/function/closures.md | 2 +- doc/zh/function/constructors.md | 107 +- doc/zh/function/general.md | 2 +- doc/zh/function/scopes.md | 2 +- doc/zh/function/this.md | 2 +- doc/zh/index.json | 14 +- doc/zh/intro/authors.md | 2 +- doc/zh/intro/contributors.md | 4 +- doc/zh/intro/index.md | 2 +- doc/zh/intro/license.md | 2 +- doc/zh/object/forinloop.md | 2 +- doc/zh/object/general.md | 2 +- doc/zh/object/hasownproperty.md | 2 +- doc/zh/object/prototype.md | 2 +- doc/zh/types/equality.md | 51 +- site/en/index.html | 1859 +++++++++++++++++++++++++++++++ site/zh/index.html | 135 +-- 21 files changed, 2082 insertions(+), 224 deletions(-) create mode 100644 site/en/index.html diff --git a/doc/language.json b/doc/language.json index 7c96b826..5fc878b4 100644 --- a/doc/language.json +++ b/doc/language.json @@ -1,4 +1,4 @@ { - "default": "en", + "default": "zh", "listed": ["en", "zh"] } diff --git a/doc/zh/array/constructor.md b/doc/zh/array/constructor.md index 8004168a..93f2c891 100755 --- a/doc/zh/array/constructor.md +++ b/doc/zh/array/constructor.md @@ -1,35 +1,38 @@ -## The `Array` Constructor +## `Array` 构造函数 -Since the `Array` constructor is ambiguous in how it deals with its parameters, -it is highly recommended to always use the array literals - `[]` notation - -when creating new arrays. +由于 `Array` 的构造函数在如何处理参数时有点模棱两可,因此总是推荐使用数组的字面语法 - `[]` - 来创建数组。 - [1, 2, 3]; // Result: [1, 2, 3] - new Array(1, 2, 3); // Result: [1, 2, 3] + [1, 2, 3]; // 结果: [1, 2, 3] + new Array(1, 2, 3); // 结果: [1, 2, 3] - [3]; // Result: [3] - new Array(3); // Result: [] - new Array('3') // Result: ['3'] + [3]; // 结果: [3] + new Array(3); // 结果: [] + new Array('3') // 结果: ['3'] -In cases when there is only one argument passed to the `Array` constructor, -and that argument is a `Number`, the constructor will return a new *sparse* -array with the `length` property set to the value of the argument. It should be -noted that **only** the `length` property of the new array will be set this way, -the actual indexes of the array will not be initialized. +译者注:这里的模棱两可指的是数组的[两种构造函数语法][1] +var arr1 = new Array(arrayLength); +var arr2 = new Array(element0, element1, ..., elementN); + + // 译者注:因此下面的代码将会使人很迷惑 + new Array(3, 4, 5); // 结果: [3, 4, 5] + new Array(3) // 结果: [],此数组长度为 3 + +由于只有一个参数传递到构造函数中(译者注:指的是 `new Array(3);` 这种调用方式),并且这个参数是数字,构造函数会返回一个 `length` 属性被设置为此参数的空数组。 +需要特别注意的是,此时只有 `length` 属性被设置,真正的数组并没有生成。 +译者注:在 Firebug 中,你会看到 [undefined, undefined, undefined],这其实是不对的。在上一节有详细的分析。 var arr = new Array(3); arr[1]; // undefined - 1 in arr; // false, the index was not set + 1 in arr; // false, 数组还没有生成 -The behavior of being able to set the length of the array upfront only comes in -handy in a few cases, like repeating a string, in which it avoids the use of a -`for loop` code. +这种优先于设置数组长度属性的做法只在少数几种情况下有用,比如需要循环字符串,可以避免 `for` 循环的麻烦。 new Array(count + 1).join(stringToRepeat); + // 译者注:new Array(3).join('#') 将会返回 "##" -### In conclusion +### 结论(In conclusion) -The use of the `Array` constructor should be avoided as much as possible. -Literals are definitely preferred. They are shorter and have a clearer syntax; -therefore, they also increase the readability of the code. +应该尽量避免使用数组构造函数创建新数组。推荐使用数组的字面语法。它们更加短小和简洁,因此增加了代码的可读性。 + +[1]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array diff --git a/doc/zh/array/general.md b/doc/zh/array/general.md index 7e0ce1d1..07bc2228 100755 --- a/doc/zh/array/general.md +++ b/doc/zh/array/general.md @@ -1,44 +1,34 @@ -## Array Iteration and Properties +## 数组遍历与属性 -Although arrays in JavaScript are objects, there are no good reasons to use -the [`for in loop`](#object.forinloop) in for iteration on them. In fact there -are a number of good reasons **against** the use of `for in` on arrays. +虽然在 JavaScript 中数组是是对象,但是没有好的理由去使用 [`for in` 循环](#object.forinloop) 遍历数组。 +相反,有一些好的理由**不去**使用 `for in` 遍历数组。 -> **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**. +> **注意:** JavaScript 中数组**不是** *关联数组*。 +> JavaScript 中只有[对象](#object.general) 来管理键值的对应关系。但是关联数组是**保持**顺序的,而对象**不是**。 -Since the `for in` loop enumerates all the properties that are on the prototype -chain and the only way to exclude those properties is to use -[`hasOwnProperty`](#object.hasownproperty), it is already up to **twenty times** -slower than a normal `for` loop. +由于 `for in` 循环会枚举原型链上的所有属性,唯一过滤这些属性的方式是使用 [`hasOwnProperty`](#object.hasownproperty) 函数, +因此会比普通的 `for` 循环慢上好多倍。 -### Iteration +### 遍历(Iteration) -In order to achieve the best performance when iterating over arrays, it is best -to use the classic `for` loop. +为了达到遍历数组的最佳性能,推荐使用经典的 `for` 循环。 var list = [1, 2, 3, 4, 5, ...... 100000000]; for(var i = 0, l = list.length; i < l; i++) { console.log(list[i]); } -There is one extra catch in the above example, that is the caching of the -length of the array via `l = list.length`. +上面代码有一个处理,就是通过 `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. +虽然 `length` 是数组的一个属性,但是在每次循环中访问它还是有性能开销。 +**可能**最新的 JavaScript 引擎在这点上做了优化,但是我们没法保证自己的代码是否运行在这些最近的引擎之上。 -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. +### `length` 属性(The `length` property) + +`length` 属性的 *getter* 方式会简单的返回数组的长度,而 *setter* 方式会**截断**数组。 var foo = [1, 2, 3, 4, 5, 6]; foo.length = 3; @@ -47,12 +37,21 @@ elements that are contained in the array, the *setter* can be used to foo.length = 6; foo; // [1, 2, 3] -Assigning a smaller length does truncate the array, but increasing the length -does not have any effect on the array. +译者注: +在 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` 属性值不会对数组产生影响。 -### In conclusion +### 结论(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. +为了更好的性能,推荐使用普通的 `for` 循环并缓存数组的 `length` 属性。 +使用 `for in` 遍历数组被认为是不好的代码习惯并倾向于产生错误和导致性能问题。 diff --git a/doc/zh/function/arguments.md b/doc/zh/function/arguments.md index cc204b4f..ccf1405c 100755 --- a/doc/zh/function/arguments.md +++ b/doc/zh/function/arguments.md @@ -1,4 +1,4 @@ -## `arguments` 对象(The `arguments` object) +## `arguments` 对象 JavaScript 中每个函数内都能访问一个特别变量 `arguments`。这个变量维护着所有传递到这个函数中的参数列表。 diff --git a/doc/zh/function/closures.md b/doc/zh/function/closures.md index adf184b4..5aaf2cb5 100755 --- a/doc/zh/function/closures.md +++ b/doc/zh/function/closures.md @@ -1,4 +1,4 @@ -## 闭包和引用(Closures and references) +## 闭包和引用 闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域**总是**能够访问外部作用域中的变量。 因为 [函数](#scopes) 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。 diff --git a/doc/zh/function/constructors.md b/doc/zh/function/constructors.md index 52935451..5ee17f6c 100755 --- a/doc/zh/function/constructors.md +++ b/doc/zh/function/constructors.md @@ -1,15 +1,12 @@ -## 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. +JavaScript 中的构造函数和其它语言中的构造函数是不同的。 +通过 `new` 关键字方式调用的函数都被认为是构造函数。 -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. +在构造函数内部 - 也就是被调用的函数内 - `this` 指向新创建的对象 `Object`。 +这个**新创建**的对象的 [`prototype`](#object.prototype) 被指向到构造函数的 `prototype`。 -If the function that was called has no explicit `return` statement, then it -implicitly returns the value of `this` - the new object. +如果被调用的函数没有显式的 `return` 表达式,则隐式的会返回 `this` 对象 - 也就是新创建的对象。 function Foo() { this.bla = 1; @@ -21,17 +18,24 @@ implicitly returns the value of `this` - the new object. var test = new Foo(); -The above calls `Foo` as constructor and sets the `prototype` of the newly -created object to `Foo.prototype`. +上面代码把 `Foo` 作为构造函数调用,并设置新创建对象的 `prototype` 为 `Foo.prototype`。 -In case of an explicit `return` statement the function returns the value -specified that statement, **but only** if the return value is an `Object`. +显式的 `return` 表达式将会影响返回结果,但**仅限**于返回的是一个对象。 function Bar() { return 2; } - new Bar(); // a new object - + new Bar(); // 返回新创建的对象 + + // 译者注:new Bar() 返回的是新创建的对象,而不是数字的字面值 2。 + // 因此 new Bar().constructor === Bar + // 但是如果返回的是数字对象,结果就不同了 + // function Bar() { + // return new Number(2); + // } + // new Bar().constructor === Number + + function Test() { this.value = 2; @@ -39,23 +43,23 @@ specified that statement, **but only** if the return value is an `Object`. foo: 1 }; } - new Test(); // the returned object + new Test(); // 返回的对象 + // 译者注:这里得到的是函数返回的对象,而不是通过 new 关键字新创建的对象 + // 所有 (new Test()).value 为 undefined,但是 (new Test()).foo === 1。 -When the `new` keyword is omitted, the function will **not** return a new object. +如果 `new` 被遗漏了,则函数**不会**返回新创建的对象。 function Foo() { - this.bla = 1; // gets set on the global object + this.bla = 1; // 获取设置全局参数 } Foo(); // undefined -While the above example might still appear to work in some cases, due to the -workings of [`this`](#function.this) in JavaScript, it will use the -*global object* as the value of `this`. +虽然上例在有些情况下也能正常运行,但是由于 JavaScript 中 [`this`](#function.this) 的工作原理, +这里的 `this` 指向*全局对象*。 -### Factories +### 工厂模式(Factories) -In order to be able to omit the `new` keyword, the constructor function has to -explicitly return a value. +为了不使用 `new` 关键字,构造函数必须显式的返回一个值。 function Bar() { var value = 1; @@ -72,25 +76,30 @@ explicitly return a value. new Bar(); Bar(); -Both calls to `Bar` return the exact same thing, a newly create object which -has a property called `method` on it, that is a -[Closure](#function.closures). +上面两种对 `Bar` 函数的调用返回的值完全相同,一个新创建的拥有 `method` 属性的对象被返回, +其实这里创建了一个[闭包](#function.closures)。 + -It is also to note that the call `new Bar()` does **not** affect the prototype -of the returned object. While the prototype will be set on the newly created -object, `Bar` never returns that new object. +还需要注意,`new Bar()` 并**不会**改变返回对象的原型(译者注:也就是返回对象的原型不会指向 Bar.prototype)。 +因为构造函数的原型会被指向到刚刚创建的新对象,而这里的 `Bar` 没有把这个新对象返回(译者注:而是返回了一个包含 `method` 属性的自定义对象)。 -In the above example, there is no functional difference between using and -not using the `new` keyword. +在上面的例子中,使用或者不使用 `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" -### Creating new objects via factories -An often made recommendation is to **not** use `new` since forgetting its use -may lead to bugs. +### 通过工厂模式创建新对象(Creating new objects via factories) -In order to create new object, one should rather use a factory and construct a -new object inside of that factory. +我们常听到的一条忠告是**不要**使用 `new` 关键字来调用函数,因为如果忘记使用它就会导致错误。 + +为了创建新对象,我们可以创建一个工厂方法,并且在方法内构造一个新对象。 function Foo() { var obj = {}; @@ -107,22 +116,16 @@ new object inside of that factory. return obj; } -While the above is robust against a missing `new` keyword and certainly makes -the use of [private variables](#function.closures) easier, it comes with some -downsides. +虽然上面的方式比起 `new` 的调用方式不容易出错,并且可以充分利用[私有变量](#function.closures)带来的便利, +但是随之而来的是一些不好的地方。 + - 1. It uses more memory since the created objects do **not** share the methods - on a prototype. - 2. In order to inherit the factory needs to copy all the methods from another - object or put that object on the prototype of the new object. - 3. Dropping the prototype chain just because of a left out `new` keyword - somehow goes against the spirit of the language. + 1. 会占用更多的内存,因为新创建的对象**不能**共享原型上的方法。 + 2. 为了实现继承,工厂方法需要从另外一个对象拷贝所有属性,或者把一个对象作为新创建对象的原型。 + 3. 放弃原型链仅仅是因为防止遗漏 `new` 带来的问题,这似乎和语言本身的思想相违背。 -### In conclusion +### 总结(In conclusion) -While omitting the `new` keyword might lead to bugs, it is certainly **not** a -reason to drop the use of prototypes altogether. In the end it comes down to -which solution is better suited for the needs of the application, it is -especially important to choose a specific style of object creation **and stick** -with it. +虽然遗漏 `new` 关键字可能会导致问题,但这并**不是**放弃使用原型链的借口。 +最终使用哪种方式取决于应用程序的需求,选择一种代码书写风格并**坚持**下去才是最重要的。 diff --git a/doc/zh/function/general.md b/doc/zh/function/general.md index c97e5a3a..14da958b 100755 --- a/doc/zh/function/general.md +++ b/doc/zh/function/general.md @@ -1,4 +1,4 @@ -## 函数声明与表达式(Function Declarations and Expressions) +## 函数声明与表达式 函数是JavaScript中的一等对象,这意味着可以把函数像其它值一样传递。 一个常见的用法是把*匿名函数*作为回调函数传递对异步函数中。 diff --git a/doc/zh/function/scopes.md b/doc/zh/function/scopes.md index 567b95b6..47921dd8 100755 --- a/doc/zh/function/scopes.md +++ b/doc/zh/function/scopes.md @@ -1,4 +1,4 @@ -## 作用域与命名空间(Scopes and namespaces) +## 作用域与命名空间 尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; 而仅仅支持 *函数作用域*。 diff --git a/doc/zh/function/this.md b/doc/zh/function/this.md index d1883afd..fb648302 100755 --- a/doc/zh/function/this.md +++ b/doc/zh/function/this.md @@ -1,4 +1,4 @@ -## this 的工作原理(How `this` works) +## this 的工作原理 JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 在**五**种不同的情况下 ,`this` 指向的各不相同。 diff --git a/doc/zh/index.json b/doc/zh/index.json index 038f5085..7ad3654c 100755 --- a/doc/zh/index.json +++ b/doc/zh/index.json @@ -4,7 +4,7 @@ "description": "A Guide to JavaScript's Quirks and Flaws.", "sections": [ { - "title": "Intro", + "title": "简介", "dir": "intro", "articles": [ "authors", @@ -13,7 +13,7 @@ ] }, { - "title": "Objects", + "title": "对象", "dir": "object", "articles": [ "general", @@ -23,7 +23,7 @@ ] }, { - "title": "Functions", + "title": "函数", "dir": "function", "articles": [ "general", @@ -35,7 +35,7 @@ ] }, { - "title": "Arrays", + "title": "数组", "dir": "array", "articles": [ "general", @@ -43,7 +43,7 @@ ] }, { - "title": "Types", + "title": "类型", "dir": "types", "articles": [ "equality", @@ -53,7 +53,7 @@ ] }, { - "title": "Core", + "title": "核心", "dir": "core", "articles": [ "eval", @@ -62,7 +62,7 @@ ] }, { - "title": "Other", + "title": "其它", "dir": "other", "articles": [ "timeouts" diff --git a/doc/zh/intro/authors.md b/doc/zh/intro/authors.md index 7db213ff..022a8259 100755 --- a/doc/zh/intro/authors.md +++ b/doc/zh/intro/authors.md @@ -1,4 +1,4 @@ -## 关于作者(The Authors) +## 关于作者 这篇文章的作者是两位 Stack Overflow 的用户, [Ivo Wetzel][1] (写作) 和 [Zhang Yi Jiang][2] (设计)。 diff --git a/doc/zh/intro/contributors.md b/doc/zh/intro/contributors.md index 7c681eae..6bb7ab4b 100755 --- a/doc/zh/intro/contributors.md +++ b/doc/zh/intro/contributors.md @@ -1,9 +1,9 @@ -## 贡献者(Contributors) +## 贡献者 - [Caio Romão][1] (拼写检查) - [Andreas Blixt][2] (语言修正) -## 中文翻译(Chinese Translation) +## 中文翻译 - [三生石上][3] [1]: https://github.com/caio diff --git a/doc/zh/intro/index.md b/doc/zh/intro/index.md index cfe75f1e..4503e0a7 100755 --- a/doc/zh/intro/index.md +++ b/doc/zh/intro/index.md @@ -1,4 +1,4 @@ -## 简介(Intro) +## 简介 **JavaScript 秘密花园**是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, diff --git a/doc/zh/intro/license.md b/doc/zh/intro/license.md index 90264edf..4deb0fae 100755 --- a/doc/zh/intro/license.md +++ b/doc/zh/intro/license.md @@ -1,4 +1,4 @@ -### 许可(License) +### 许可 JavaScript 秘密花园在 [MIT license][1] 许可协议下发布,并存放在开源社区 [GitHub][2]。 如果你发现错误或者打字错误,请 [file an issue][3] 或者 pull request。 diff --git a/doc/zh/object/forinloop.md b/doc/zh/object/forinloop.md index 9c594dcc..ef437adc 100755 --- a/doc/zh/object/forinloop.md +++ b/doc/zh/object/forinloop.md @@ -1,4 +1,4 @@ -## `for in` 循环(The `for in` Loop) +## `for in` 循环 和 `in` 操作符一样,`for in` 循环同样在查找对象属性时遍历原型链上的所有属性。 diff --git a/doc/zh/object/general.md b/doc/zh/object/general.md index 466a0962..23685397 100755 --- a/doc/zh/object/general.md +++ b/doc/zh/object/general.md @@ -1,4 +1,4 @@ -## 对象使用和属性(Object Usage and Properties) +## 对象使用和属性 JavaScript 中所有变量都是对象,除了两个例外 [`null`](#undefined) 和 [`undefined`](#undefined)。 diff --git a/doc/zh/object/hasownproperty.md b/doc/zh/object/hasownproperty.md index d8e2cab0..1a04cb2b 100755 --- a/doc/zh/object/hasownproperty.md +++ b/doc/zh/object/hasownproperty.md @@ -1,4 +1,4 @@ -## `hasOwnProperty` +## `hasOwnProperty` 函数 为了判断一个对象是否包含*自定义*属性而*不是*[原型链](#prototype)上的属性, 我们需要使用继承自 `Object.prototype` 的 `hasOwnProperty` 方法。 diff --git a/doc/zh/object/prototype.md b/doc/zh/object/prototype.md index f6269fe2..505076f2 100755 --- a/doc/zh/object/prototype.md +++ b/doc/zh/object/prototype.md @@ -1,4 +1,4 @@ -## 原型(The prototype) +## 原型 JavaScript 不包含传统的类继承模型,而是使用 *prototypical* 原型模型。 diff --git a/doc/zh/types/equality.md b/doc/zh/types/equality.md index 1bc4c9cf..60335f8e 100755 --- a/doc/zh/types/equality.md +++ b/doc/zh/types/equality.md @@ -1,13 +1,12 @@ -## Equality and comparisons +## 相等与比较 -JavaScript has two different ways of comparing the values of objects for equality. +JavaScript 有两种方式判断两个值是否相等。 -### The equals operator +### 等于操作符(The equals operator) -The equals operator consists of two equal signs: `==` +等于操作符由两个等号组成:`==` -JavaScript features *weak typing*, that means, that the equals operator -**coerces** types in order to compare them. +JavaScript 是*弱类型*语言,这就意味着,等于操作符会为了比较两个值而进行**强制类型转换**。 "" == "0" // false 0 == "" // true @@ -19,20 +18,17 @@ JavaScript features *weak typing*, that means, that the equals operator 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 equals operator -The strict equals operator consists of **three** equal signs: `===` +### 严格的等于操作符(The strict equals operator) -Other than the normal equals operator, the strict equals operator does **not** -perform type coercion between its operands. +严格的等于操作符由**三**个等号组成:`===` + +不想普通的等于操作符,严格的等于操作符**不会**进行强制类型转换。 "" === "0" // false 0 === "" // false @@ -44,14 +40,12 @@ perform type coercion between its operands. 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 +### 比较对象(Comparing objects) -While both `==` and `===` are stated as **equality** operators, they behave -different when at least one of their operands happens to be an `Object`. +虽然 `==` 和 `===` 操作符都是等于操作符,但是当其中有一个操作数为对象时,行为就不同了。 {} === {}; // false new String('foo') === 'foo'; // false @@ -59,13 +53,10 @@ different when at least one of their operands happens to be an `Object`. 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 a pointer comparison in C do. - -### In conclusion +这里等于操作符比较的**不是**值是否相等,而是是否属于同一个**身份**;也就是说,只有对象的同一个实例才被认为是相等的。 +这有点像 Python 中的 `is` 和 C 中的指针比较。 -It is highly recommended to only use the **strict equals** 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. +### 结论(In conclusion) +强烈推荐使用**严格的等于操作符**。如果类型需要转换,应该在比较之前[显式](#types.casting)的转换, +而不是使用语言本身复杂的强制转换规则。 diff --git a/site/en/index.html b/site/en/index.html new file mode 100644 index 00000000..8a91e498 --- /dev/null +++ b/site/en/index.html @@ -0,0 +1,1859 @@ +JavaScript Garden +

Intro

JavaScript Garden is a growing collection of documentation about the most +quirky parts of the JavaScript programming language. It gives advice to +avoid common mistakes, subtle bugs, as well as performance issues and bad +practices that non-expert JavaScript programmers may encounter on their +endeavours into the depths of the language.

+ +

JavaScript Garden does not aim to teach you JavaScript. Former knowledge +of the language is strongly recommended in order to understand the topics covered +in this guide. In order to learn the basics of the language, please head over to +the excellent guide 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).

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 Foo(){}
+Foo.bar = 1;
+Foo.bar; // 1
+
+ +

A common misconception is that number literals cannot be used as +objects. That is because a flaw in JavaScript's parser tries to parse the dot +notation on a number as a floating point literal.

+ +
2.toString(); // raises SyntaxError
+
+ +

There are a couple of workarounds which can be used in order make number +literals act as objects too.

+ +
2..toString(); // the second point is correctly recognized
+2 .toString(); // note the space left to the dot
+(2).toString(); // 2 is evaluated first
+
+ +

Objects as a data type

+ +

Objects in JavaScript can also be used as a Hashmap, they mainly consist +of named properties mapping to values.

+ +

Using a object literal - {} notation - it is possible to create a +plain object. This new object inherits from Object.prototype and +has no own properties defined on it.

+ +
var foo = {}; // a new empty object
+
+// a new object with a property called 'test' with value 12
+var bar = {test: 12}; 
+
+ +

Accessing properties

+ +

The properties of an object can be accessed in two ways, via either the dot +notation, or the square bracket notation.

+ +
var foo = {name: 'Kitten'}
+foo.name; // kitten
+foo['name']; // kitten
+
+var get = 'name';
+foo[get]; // kitten
+
+foo.1234; // SyntaxError
+foo['1234']; // works
+
+ +

Both notations are identical in their workings, with the only difference being that +the square bracket notation allows for dynamic setting of properties, as well as +the use of property names that would otherwise lead to a syntax error.

+ +

Deleting properties

+ +

The only way to actually remove a property from an object is to use the delete +operator; setting the property to undefined or null only remove the +value associated with the property, but not the key.

+ +
var obj = {
+    bar: 1,
+    foo: 2,
+    baz: 3
+};
+obj.bar = undefined;
+obj.foo = null;
+delete obj.baz;
+
+for(var i in obj) {
+    if (obj.hasOwnProperty(i)) {
+        console.log(i, '' + obj[i]);
+    }
+}
+
+ +

The above outputs both bar undefined and foo null - only baz was +removed and is therefore missing from the output.

+ +

Notation of keys

+ +
var test = {
+    'case': 'I am a keyword so I must be notated as a string',
+    delete: 'I am a keyword too so me' // raises SyntaxError
+};
+
+ +

Object properties can be both notated as plain characters and as strings. Due to +another mis-design in JavaScript's parser, the above will throw +a SyntaxError prior to ECMAScript 5.

+ +

This error arises from the fact that delete is a keyword; therefore, it must be +notated as a string literal to ensure that it will be correctly interpreted by +older JavaScript engines.

The Prototype

JavaScript does not feature a classical inheritance model, instead it uses a +prototypal one.

+ +

While this is often considered to be one of JavaScript's weaknesses, the +prototypal inheritance model is in fact more powerful than the classic model. +It is for example fairly trivial to build a classic model on top of it, while the +other way around is a far more difficult task.

+ +

Due to the fact that JavaScript is basically the only widely used language that +features prototypal inheritance, it takes some time to adjust to the +differences between the two models.

+ +

The first major difference is that inheritance in JavaScript is done by using so +called prototype chains.

+ + + +
function Foo() {
+    this.value = 42;
+}
+Foo.prototype = {
+    method: function() {}
+};
+
+function Bar() {}
+
+// Set Bar's prototype to a new instance of Foo
+Bar.prototype = new Foo();
+Bar.prototype.foo = 'Hello World';
+
+// Make sure to list Bar as the actual constructor
+Bar.prototype.constructor = Bar;
+
+var test = new Bar() // create a new bar instance
+
+// The resulting prototype chain
+test [instance of Bar]
+    Bar.prototype [instance of Foo] 
+        { foo: 'Hello World' }
+        Foo.prototype
+            { method: ... }
+            Object.prototype
+                { toString: ... /* etc. */ }
+
+ +

In the above, the object test will inherit from both Bar.prototype and +Foo.prototype; hence, it will have access to the function method that was +defined on Foo. It will also have access to the property value of the +one Foo instance that is its prototype. It is important to note that new +Bar() does not create a new Foo instance, but reuses the one assigned to +its prototype; thus, all Bar instances will share the same value property.

+ + + +

Property lookup

+ +

When accessing the properties of an object, JavaScript will traverse the +prototype chain upwards until it finds a property with the requested name.

+ +

When it reaches the top of the chain - namely Object.prototype - and still +hasn't found the specified property, it will return the value +undefined instead.

+ +

The prototype property

+ +

While the prototype property is used by the language to build the prototype +chains, it is still possible to assign any given value to it. Although +primitives will simply get ignored when assigned as a prototype.

+ +
function Foo() {}
+Foo.prototype = 1; // no effect
+
+ +

Assigning objects, as shown in the example above, will work, and allows for dynamic +creation of prototype chains.

+ +

Performance

+ +

The lookup time for properties that are high up on the prototype chain can have a +negative impact on performance critical sections of code. Additionally, trying to +access non-existent properties will always traverse the full prototype chain.

+ +

Also, when iterating over the properties of an object +every property that is on the prototype chain will get enumerated.

+ +

Extension of native prototypes

+ +

One mis-feature that is often used is to extend Object.prototype or one of the +other built in prototypes.

+ +

This technique is called monkey patching and breaks encapsulation. While +used by widely spread frameworks such as Prototype, there is still no good +reason for cluttering built-in types with additional non-standard functionality.

+ +

The only good reason for extending a built-in prototype is to backport +the features of newer JavaScript engines; for example, +Array.forEach.

+ +

In conclusion

+ +

It is a must to understand the prototypal inheritance model completely +before writing complex code which makes use of it. Also, watch the length of +the prototype chains and break them up if necessary to avoid possible +performance issues. Further, the native prototypes should never be extended +unless it is for the sake of compatibility with newer JavaScript features.

hasOwnProperty

In order to check whether a object has a property defined on itself and not +somewhere on its prototype chain, it is necessary to use the +hasOwnProperty method which all objects inherit from Object.prototype.

+ + + +

hasOwnProperty is the only thing in JavaScript which deals with properties and +does not traverse the prototype chain.

+ +
// Poisoning Object.prototype
+Object.prototype.bar = 1; 
+var foo = {goo: undefined};
+
+foo.bar; // 1
+'bar' in foo; // true
+
+foo.hasOwnProperty('bar'); // false
+foo.hasOwnProperty('goo'); // true
+
+ +

Only hasOwnProperty will give the correct and expected result, this is +essential when iterating over the properties of any object. There is no other +way to exclude properties that are not defined on the object itself, but +somewhere on its prototype chain.

+ +

hasOwnProperty as a property

+ +

JavaScript does not protect the property name hasOwnProperty; thus, if the +possibility exists that an object might have a property with this name, it is +necessary to use an external hasOwnProperty in order to get correct results.

+ +
var foo = {
+    hasOwnProperty: function() {
+        return false;
+    },
+    bar: 'Here be dragons'
+};
+
+foo.hasOwnProperty('bar'); // always returns false
+
+// Use another hasOwnProperty and call it with 'this' set to foo
+{}.hasOwnProperty.call(foo, 'bar'); // true
+
+ +

In conclusion

+ +

When checking for the existence of a property on a object, hasOwnProperty is +the only method of doing so. It is also recommended to make hasOwnProperty +part of every for in loop, this will avoid errors from +extended native prototypes.

The for in Loop

Just like the in operator, the for in loop also traverses the prototype +chain when iterating over the properties of an object.

+ + + +
// Poisoning Object.prototype
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+    console.log(i); // prints both bar and moo
+}
+
+ +

Since it is not possible to change the behavior of the for in loop itself, it +is necessary to filter out the unwanted properties inside the loop body , +this is done by using the hasOwnProperty method of +Object.prototype.

+ + + +

Using hasOwnProperty for filtering

+ +
// still the foo from above
+for(var i in foo) {
+    if (foo.hasOwnProperty(i)) {
+        console.log(i);
+    }
+}
+
+ +

This version is the only correct one to use. Due to the use of hasOwnProperty it +will only print out moo. When hasOwnProperty is left out, the code is +prone to errors in cases where the native prototypes - e.g. Object.prototype - +have been extended.

+ +

One widely used framework which does this is Prototype. When this +framework is included, for in loops that do not use hasOwnProperty are +guaranteed to break.

+ +

Best practices

+ +

It is recommended to always use hasOwnProperty. Never should any +assumptions be made about the environment the code is running in, or whether the +native prototypes have been extended or not.

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 asynchronous function.

+ +

The function declaration

+ +
function foo() {}
+
+ +

The above function gets hoisted before the execution of the +program starts; thus, it is available everywhere in the scope it was defined +in, even if called before the actual definition in the source.

+ +
foo(); // Works because foo was created before this code runs
+function foo() {}
+
+ +

The function expression

+ +
var foo = function() {};
+
+ +

This example assigns the unnamed and anonymous function to the variable foo.

+ +
foo; // 'undefined'
+foo(); // this raises a TypeError
+var foo = function() {};
+
+ +

Due to the fact that var is a declaration, that hoists the variable name foo +before the actual execution of the code starts, foo is already defined when +the script gets executed.

+ +

But since assignments only happen at runtime, the value of foo will default +to undefined before the corresponding code is executed.

+ +

Named function expression

+ +

Another special case is the assignment of named functions.

+ +
var foo = function bar() {
+    bar(); // Works
+}
+bar(); // ReferenceError
+
+ +

Here bar is not available in the outer scope, since the function only gets +assigned to foo; however, inside of bar it is available. This is due to +how name resolution in JavaScript works, the name of the +function is always made available in the local scope of the function itself.

How this Works

JavaScript has a different concept of what the special name this refers to +than most other programming languages do. There are exactly five different +ways in which the value of this can be bound in the language.

+ +

The global scope

+ +
this;
+
+ +

When using this in global scope, it will simply refer to the global object.

+ +

Calling a function

+ +
foo();
+
+ +

Here this will again refer to the global object.

+ + + +

Calling a method

+ +
test.foo(); 
+
+ +

In this example this will refer to test.

+ +

Calling a constructor

+ +
new foo(); 
+
+ +

A function call that is preceded by the new keyword acts as +a constructor. Inside the function this will refer +to a newly created Object.

+ +

Explicit setting of this

+ +
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // array will expand to the below
+foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3
+
+ +

When using the call or apply methods of Function.prototype, the value of +this inside the called function gets explicitly set to the first argument +of the corresponding function call.

+ +

As a result, the above example the method case does not apply, and this +inside of foo will be set to bar.

+ + + +

Common pitfalls

+ +

While most of these cases make sense, the first one is to be considered another +mis-design of the language, as it never has any practical use.

+ +
Foo.method = function() {
+    function test() {
+        // this is set to the global object
+    }
+    test();
+}
+
+ +

A common misconception is that this inside of test refers to Foo, while in +fact it does not.

+ +

In order to gain access to Foo from within test it is necessary to create a +local variable inside of method which refers to Foo.

+ +
Foo.method = function() {
+    var that = this;
+    function test() {
+        // Use that instead of this here
+    }
+    test();
+}
+
+ +

that is just a normal name, but it is commonly used for the reference to an +outer this. In combination with closures, it can also +be used to pass this values around.

+ +

Assigning methods

+ +

Another thing that does not work in JavaScript is function aliasing, that is, +assigning a method to a variable.

+ +
var test = someObject.methodTest;
+test();
+
+ +

Due to the first case test now acts like like a plain function call; therefore, +this inside it will no longer refer to someObject.

+ +

While the late binding of this might seem like a bad idea at first, it is in +fact what makes prototypal inheritance work.

+ +
function Foo() {}
+Foo.prototype.method = function() {};
+
+function Bar() {}
+Bar.prototype = Foo.prototype;
+
+new Bar().method();
+
+ +

When method gets called on a instance of Bar, this will now refer to that +very instance.

Closures and References

One of JavaScript's most powerful features is the availability of closures, +this means that scopes always keep access to the outer scope they were +defined in. Since the only scoping that JavaScript has is +function scope, all functions, by default, act as closures.

+ +

Emulating private variables

+ +
function Counter(start) {
+    var count = start;
+    return {
+        increment: function() {
+            count++;
+        },
+
+        get: function() {
+            return count;
+        }
+    }
+}
+
+var foo = Counter(4);
+foo.increment();
+foo.get(); // 5
+
+ +

Here, Counter returns two closures. The function increment as well as +the function get. Both of these functions keep a reference to the scope of +Counter and, therefore, always keep access to the count variable that was +defined in that very scope.

+ +

Why private variables work

+ +

Since it is not possible to reference or assign scopes in JavaScript, there is +no way of accessing the variable count from the outside. The only way to +interact with it is via the two closures.

+ +
var foo = new Counter(4);
+foo.hack = function() {
+    count = 1337;
+};
+
+ +

The above code will not change the variable count in the scope of Counter, +since foo.hack was not defined in that scope. It will instead create - or +override - the global variable count.

+ +

Closures inside loops

+ +

One often made mistake is to use closures inside of loops, as if they were +copying the value of the loops index variable.

+ +
for(var i = 0; i < 10; i++) {
+    setTimeout(function() {
+        console.log(i);  
+    }, 1000);
+}
+
+ +

The above will not output the numbers 0 through 9, but will simply print +the number 10 ten times.

+ +

The anonymous function keeps a reference to i and at the time +console.log gets called, the for loop has already finished and the value of +i as been set to 10.

+ +

In order to get the desired behavior, it is necessary to create a copy of +the value of i.

+ +

Avoiding the reference problem

+ +

In order to copy the value of the loop's index variable, it is best to use an +anonymous wrapper.

+ +
for(var i = 0; i < 10; i++) {
+    (function(e) {
+        setTimeout(function() {
+            console.log(e);  
+        }, 1000);
+    })(i);
+}
+
+ +

The anonymous outer function gets called immediately with i as its first +argument and will receive a copy of the value of i as its parameter e.

+ +

The anonymous function that gets passed to setTimeout now has a reference to +e, whose value does not get changed by the loop.

+ +

There is another possible way of achieving this; that is to return a function +from the anonymous wrapper, that will then have the same behavior as the code +above.

+ +
for(var i = 0; i < 10; i++) {
+    setTimeout((function(e) {
+        return function() {
+            console.log(e);
+        }
+    })(i), 1000)
+}
+

The arguments Object

Every function scope in JavaScript can access the special variable arguments. +This variable holds a list of all the arguments that were passed to the function.

+ + + +

The arguments object is not an Array. While it has some of the +semantics of an array - namely the length property - it does not inherit from +Array.prototype and is in fact an Object.

+ +

Due to this, it is not possible to use standard array methods like push, +pop or slice on arguments. While iteration with a plain for loop works +just fine, it is necessary to convert it to a real Array in order to use the +standard Array methods on it.

+ +

Converting to an array

+ +

The code below will return a new Array containing all the elements of the +arguments object.

+ +
Array.prototype.slice.call(arguments);
+
+ +

This conversion is slow, it is not recommended to use it in performance +critical sections of code.

+ +

Passing arguments

+ +

The following is the recommended way of passing arguments from one function to +another.

+ +
function foo() {
+    bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+    // do stuff here
+}
+
+ +

Another trick is to use both call and apply together to create fast, unbound +wrappers.

+ +
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+    console.log(this, a, b, c);
+};
+
+// Create an unbound version of "method" 
+// It takes the parameters: this, arg1, arg2...argN
+Foo.method = function() {
+
+    // Result: Foo.prototype.method.call(this, arg1, arg2... argN)
+    Function.call.apply(Foo.prototype.method, arguments);
+};
+
+ +

Formal parameters and arguments indexes

+ +

The arguments object creates getter and setter functions for both its +properties as well as the function's formal parameters.

+ +

As a result, changing the value of a formal parameter will also change the value +of the corresponding property on the arguments object, and the other way around.

+ +
function foo(a, b, c) {
+    arguments[0] = 2;
+    a; // 2                                                           
+
+    b = 4;
+    arguments[1]; // 4
+
+    var d = c;
+    d = 9;
+    c; // 3
+}
+foo(1, 2, 3);
+
+ +

Performance myths and truths

+ +

The arguments object is always created with the only two exceptions being the +cases where it is declared as a name inside of a function or one of its formal +parameters. It does not matter whether it is used or not.

+ +

Both getters and setters are always created; thus, using it has nearly +no performance impact at all, especially not in real world code where there is +more than a simple access to the arguments object's properties.

+ + + +

However, there is one case which will drastically reduce the performance in +modern JavaScript engines. That case is the use of arguments.callee.

+ +
function foo() {
+    arguments.callee; // do something with this function object
+    arguments.callee.caller; // and the calling function object
+}
+
+function bigLoop() {
+    for(var i = 0; i < 100000; i++) {
+        foo(); // Would normally be inlined...
+    }
+}
+
+ +

In the above code, foo can no longer be a subject to inlining since it +needs to know about both itself and its caller. This not only defeats possible +performance gains that would arise from inlining, it also breaks encapsulation +since the function may now be dependent on a specific calling context.

+ +

It is highly recommended to never make use of arguments.callee or any of +its properties.

+ +

Constructors

Constructors in JavaScript are yet again different from many other languages. Any +function call that is preceded by the new keyword acts as a constructor.

+ +

Inside the constructor - the called function - the value of this refers to a +newly created Object. The prototype of this new +object is set to the prototype of the function object that was invoked as the +constructor.

+ +

If the function that was called has no explicit return statement, then it +implicitly returns the value of this - the new object.

+ +
function Foo() {
+    this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+    console.log(this.bla);
+};
+
+var test = new Foo();
+
+ +

The above calls Foo as constructor and sets the prototype of the newly +created object to Foo.prototype.

+ +

In case of an explicit return statement the function returns the value +specified that statement, but only if the return value is an Object.

+ +
function Bar() {
+    return 2;
+}
+new Bar(); // a new object
+
+function Test() {
+    this.value = 2;
+
+    return {
+        foo: 1
+    };
+}
+new Test(); // the returned object
+
+ +

When the new keyword is omitted, the function will not return a new object.

+ +
function Foo() {
+    this.bla = 1; // gets set on the global object
+}
+Foo(); // undefined
+
+ +

While the above example might still appear to work in some cases, due to the +workings of this in JavaScript, it will use the +global object as the value of this.

+ +

Factories

+ +

In order to be able to omit the new keyword, the constructor function has to +explicitly return a value.

+ +
function Bar() {
+    var value = 1;
+    return {
+        method: function() {
+            return value;
+        }
+    }
+}
+Bar.prototype = {
+    foo: function() {}
+};
+
+new Bar();
+Bar();
+
+ +

Both calls to Bar return the exact same thing, a newly create object which +has a property called method on it, that is a +Closure.

+ +

It is also to note that the call new Bar() does not affect the prototype +of the returned object. While the prototype will be set on the newly created +object, Bar never returns that new object.

+ +

In the above example, there is no functional difference between using and +not using the new keyword.

+ +

Creating new objects via factories

+ +

An often made recommendation is to not use new since forgetting its use +may lead to bugs.

+ +

In order to create new object, one should rather use a factory and construct a +new object inside of that factory.

+ +
function Foo() {
+    var obj = {};
+    obj.value = 'blub';
+
+    var private = 2;
+    obj.someMethod = function(value) {
+        this.value = value;
+    }
+
+    obj.getPrivate = function() {
+        return private;
+    }
+    return obj;
+}
+
+ +

While the above is robust against a missing new keyword and certainly makes +the use of private variables easier, it comes with some +downsides.

+ +
    +
  1. It uses more memory since the created objects do not share the methods +on a prototype.
  2. +
  3. In order to inherit the factory needs to copy all the methods from another +object or put that object on the prototype of the new object.
  4. +
  5. Dropping the prototype chain just because of a left out new keyword +somehow goes against the spirit of the language.
  6. +
+ +

In conclusion

+ +

While omitting the new keyword might lead to bugs, it is certainly not a +reason to drop the use of prototypes altogether. In the end it comes down to +which solution is better suited for the needs of the application, it is +especially important to choose a specific style of object creation and stick +with it.

Scopes and Namespaces

Although JavaScript deals fine with the syntax of two matching curly +braces for blocks, it does not support block scope; hence, all that is left +is in the language is function scope.

+ +
function test() { // a scope
+    for(var i = 0; i < 10; i++) { // not a scope
+        // count
+    }
+    console.log(i); // 10
+}
+
+ + + +

There are also no distinct namespaces in JavaScript, that means that everything +gets defined in one globally shared namespace.

+ +

Each time a variable is referenced, JavaScript will traverse upwards through all +the scopes until it finds it. In the case that it reaches the global scope and +still has not found the requested name, it will raise a ReferenceError.

+ +

The bane of global variables

+ +
// script A
+foo = '42';
+
+// script B
+var foo = '42'
+
+ +

The above two scripts do not have the same effect. Script A defines a +variable called foo in the global scope and script B defines a foo in the +current scope.

+ +

Again, that is not at all the same effect, not using var can have major +implications.

+ +
// global scope
+var foo = 42;
+function test() {
+    // local scope
+    foo = 21;
+}
+test();
+foo; // 21
+
+ +

Leaving out the var statement inside the function test will override the +value of foo. While this might not seem like a big deal at first, having +thousands of lines of JavaScript and not using var will introduce horrible and +hard to track down bugs.

+ +
// global scope
+var items = [/* some list */];
+for(var i = 0; i < 10; i++) {
+    subLoop();
+}
+
+function subLoop() {
+    // scope of subLoop
+    for(i = 0; i < 10; i++) { // missing var statement
+        // do amazing stuff!
+    }
+}
+
+ +

The outer loop will terminate after the first call to subLoop, since subLoop +overwrites the global value of i. Using a var for the second for loop would +have easily avoided this error. The var statement should never be left out +unless the desired effect is to affect the outer scope.

+ +

Local variables

+ +

The only source for local variables in JavaScript are +function parameters and variables that were declared via the +var statement.

+ +
// global scope
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+    // local scope of the function test
+    i = 5;
+
+    var foo = 3;
+    bar = 4;
+}
+test(10);
+
+ +

While foo and i are local variables inside the scope of the function test, +the assignment of bar will override the global variable with the same name.

+ +

Hoisting

+ +

JavaScript hoists declarations. This means that both var statements and +function declarations will be moved to the top of their enclosing scope.

+ +
bar();
+var bar = function() {};
+var someValue = 42;
+
+test();
+function test(data) {
+    if (false) {
+        goo = 1;
+
+    } else {
+        var goo = 2;
+    }
+    for(var i = 0; i < 100; i++) {
+        var e = data[i];
+    }
+}
+
+ +

The above code gets transformed before any execution is started. JavaScript moves +the var statements as well as the function declarations to the top of the +nearest surrounding scope.

+ +
// var statements got moved here
+var bar, someValue; // default to 'undefined'
+
+// the function declartion got moved up too
+function test(data) {
+    var goo, i, e; // missing block scope moves these here
+    if (false) {
+        goo = 1;
+
+    } else {
+        goo = 2;
+    }
+    for(i = 0; i < 100; i++) {
+        e = data[i];
+    }
+}
+
+bar(); // fails with a TypeError since bar is still 'undefined'
+someValue = 42; // assignments are not affected by hoisting
+bar = function() {};
+
+test();
+
+ +

Missing block scoping will not only move var statements out of loops and +their bodies, it will also make the results of certain if constructs +non-intuitive.

+ +

In the original code the if statement seemed to modify the global +variable goo, while actually it modifies the local variable - after hoisting +has been applied.

+ +

Without the knowledge about hoisting, below code might seem to raise a +ReferenceError.

+ +
// check whether SomeImportantThing has been initiliazed
+if (!SomeImportantThing) {
+    var SomeImportantThing = {};
+}
+
+ +

But of course, the above works due to the fact that the var statement is being +moved to the top of the global scope.

+ +
var SomeImportantThing;
+
+// other code might initiliaze SomeImportantThing here, or not
+
+// make sure it's there
+if (!SomeImportantThing) {
+    SomeImportantThing = {};
+}
+
+ +

Name resolution order

+ +

All scopes in JavaScript, including the global scope, have the special name +this defined in them, which refers to the current object.

+ +

Function scopes also have the name arguments defined in +them which contains the arguments that were passed to a function.

+ +

For example, when trying to access a variable named foo inside the scope of a +function, JavaScript will lookup the name in the following order:

+ +
    +
  1. In case there is a var foo statement in the current scope use that.
  2. +
  3. If one of the function parameters is named foo use that.
  4. +
  5. If the function itself is called foo use that.
  6. +
  7. Go to the next outer scope and start with #1 again.
  8. +
+ + + +

Namespaces

+ +

A common problem of having only one global namespace is the likeliness of running +into problems where variable names clash. In JavaScript, this problem can +easily be avoided with the help of anonymous wrappers.

+ +
(function() {
+    // a self contained "namespace"
+
+    window.foo = function() {
+        // an exposed closure
+    };
+
+})(); // execute the function immediately
+
+ +

Unnamed functions are considered expressions; so in order to +being callable, they must first be evaluated.

+ +
( // evaluate the function inside the paranthesis
+function() {}
+) // and return the function object
+() // call the result of the evaluation
+
+ +

There are other ways for evaluating and calling the function expression; which, +while different in syntax, do behave the exact same way.

+ +
// Two other ways
++function(){}();
+(function(){}());
+
+ +

In conclusion

+ +

It is recommended to always use an anonymous wrapper for encapsulating code in +its own namespace. This does not only protect code against name clashes, it +also allows for better modularization of programs.

+ +

Additionally, the use of global variables is considered bad practice. Any +use of them indicates badly written code that is prone to errors and hard to maintain.

Arrays

Array Iteration and Properties

Although arrays in JavaScript are objects, there are no good reasons to use +the for in loop in for iteration on them. In fact there +are a number of good reasons against the use of for in on arrays.

+ + + +

Since the for in loop enumerates all the properties that are on the prototype +chain and the only way to exclude those properties is to use +hasOwnProperty, it is already up to twenty times +slower than a normal for loop.

+ +

Iteration

+ +

In order to achieve the best performance when iterating over arrays, it is best +to use the classic for loop.

+ +
var list = [1, 2, 3, 4, 5, ...... 100000000];
+for(var i = 0, l = list.length; i < l; i++) {
+    console.log(list[i]);
+}
+
+ +

There is one extra catch in the above example, that is the caching of the +length of the array via l = list.length.

+ +

Although the length property is defined on the array itself, there is still an +overhead for doing the lookup on each iteration of the loop. And while recent +JavaScript engines may apply optimization in this case, there is no way of +telling whether the code will run on one of these newer engines or not.

+ +

In fact, leaving out the caching may result in the loop being only half as +fast as with the cached length.

+ +

The length property

+ +

While the getter of the length property simply returns the number of +elements that are contained in the array, the setter can be used to +truncate the array.

+ +
var foo = [1, 2, 3, 4, 5, 6];
+foo.length = 3;
+foo; // [1, 2, 3]
+
+foo.length = 6;
+foo; // [1, 2, 3]
+
+ +

Assigning a smaller length does truncate the array, but increasing the length +does not have any effect on the array.

+ +

In conclusion

+ +

For the best performance it is recommended to always use the plain for loop +and cache the length property. The use of for in on an array is a sign of +badly written code that is prone to bugs and bad performance.

The Array Constructor

Since the Array constructor is ambiguous in how it deals with its parameters, +it is highly recommended to always use the array literals - [] notation - +when creating new arrays.

+ +
[1, 2, 3]; // Result: [1, 2, 3]
+new Array(1, 2, 3); // Result: [1, 2, 3]
+
+[3]; // Result: [3]
+new Array(3); // Result: []
+new Array('3') // Result: ['3']
+
+ +

In cases when there is only one argument passed to the Array constructor, +and that argument is a Number, the constructor will return a new sparse +array with the length property set to the value of the argument. It should be +noted that only the length property of the new array will be set this way, +the actual indexes of the array will not be initialized.

+ +
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // false, the index was not set
+
+ +

The behavior of being able to set the length of the array upfront only comes in +handy in a few cases, like repeating a string, in which it avoids the use of a +for loop code.

+ +
new Array(count + 1).join(stringToRepeat);
+
+ +

In conclusion

+ +

The use of the Array constructor should be avoided as much as possible. +Literals are definitely preferred. They are shorter and have a clearer syntax; +therefore, they also increase the readability of the code.

Types

Equality and comparisons

JavaScript has two different ways of comparing the values of objects for equality.

+ +

The equals operator

+ +

The equals operator consists of two equal signs: ==

+ +

JavaScript features weak typing, that means, that the equals 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 equals operator

+ +

The strict equals operator consists of three equal signs: ===

+ +

Other than the normal equals operator, the strict equals operator does not +perform type coercion between its operands.

+ +
""           ===   "0"           // false
+0            ===   ""            // false
+0            ===   "0"           // false
+false        ===   "false"       // false
+false        ===   "0"           // false
+false        ===   undefined     // false
+false        ===   null          // false
+null         ===   undefined     // false
+" \t\r\n"    ===   0             // false
+
+ +

The above results are a lot clearer and allow for early breakage of code. This +hardens code to a certain degree and also gives performance improvements in case +the operands are of different types.

+ +

Comparing objects

+ +

While both == and === are stated as equality operators, they behave +different when at least one of their operands happens to be an Object.

+ +
{} === {};                   // false
+new String('foo') === 'foo'; // false
+new Number(10) === 10;       // false
+var foo = {};
+foo === foo;                 // true
+
+ +

Here both operators compare for identity and not equality; that is, they +will compare for the same instance of the object, much like is in Python +and a pointer comparison in C do.

+ +

In conclusion

+ +

It is highly recommended to only use the strict equals operator. In cases +where types need to be coerced, it should be done explicitly +and not left to the language's complicated coercion rules.

The typeof operator

The typeof operator (together with +instanceof) is probably the biggest +design flaw of JavaScript, as it is near of being completely broken.

+ +

Although instanceof still has its limited uses, typeof really has only one +practical use case, which does not happen to be checking the type of an +object.

+ + + +

The JavaScript type table

+ +
Value               Class      Type
+-------------------------------------
+"foo"               String     string
+new String("foo")   String     object
+1.2                 Number     number
+new Number(1.2)     Number     object
+true                Boolean    boolean
+new Boolean(true)   Boolean    object
+new Date()          Date       object
+new Error()         Error      object
+[1,2,3]             Array      object
+new Array(1, 2, 3)  Array      object
+new Function("")    Function   function
+/abc/g              RegExp     object (function in Nitro/V8)
+new RegExp("meow")  RegExp     object (function in Nitro/V8)
+{}                  Object     object
+new Object()        Object     object
+
+ +

In the above table Type refers to the value, that the typeof operator returns. +As can be clearly seen, this value is anything but consistent.

+ +

The Class refers to the value of the internal [[Class]] property of an object.

+ + + +

In order to retrieve the value of [[Class]] one has to make use of the +toString method of Object.prototype.

+ +

The Class of an object

+ +

The specification gives exactly one way of accessing the [[Class]] value, +with the use of Object.prototype.toString.

+ +
function is(type, obj) {
+    var clas = Object.prototype.toString.call(obj).slice(8, -1);
+    return obj !== undefined && obj !== null && clas === type;
+}
+
+is('String', 'test'); // true
+is('String', new String('test')); // true
+
+ +

In the above example, Object.prototype.toString gets called with the value of +this being set to the object whose [[Class]] value should be +retrieved.

+ + + +

Testing for undefined variables

+ +
typeof foo !== 'undefined'
+
+ +

The above will check whether foo was actually declared or not; just +referencing it would result in a ReferenceError. This is the only thing +typeof is actually useful for.

+ +

In conclusion

+ +

In order to check the type of an object, it is highly recommended to use +Object.prototype.toString; as this is the only reliable way of doing so. +As shown in the above type table, some return values of typeof are not defined +in the specification; thus, they can differ across various implementations.

+ +

Unless checking whether a variable is defined, typeof should be avoided at +all costs.

The instanceof operator

The instanceof operator compares the constructors of its two operands. It is +only useful when comparing custom made objects. Used on built-in types, it is +nearly as useless as the typeof operator.

+ +

Comparing custom objects

+ +
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // true
+new Bar() instanceof Foo; // true
+
+// This just sets Bar.prototype to the function object Foo
+// But not to an actual instance of Foo
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // false
+
+ +

Using instanceof with native types

+ +
new String('foo') instanceof String; // true
+new String('foo') instanceof Object; // true
+
+'foo' instanceof String; // false
+'foo' instanceof Object; // false
+
+ +

One important thing to note here is, that instanceof does not work on objects +that origin from different JavaScript contexts (e.g. different documents +in a web browser), since their constructors will not be the exact same object.

+ +

In conclusion

+ +

The instanceof operator should only be used when dealing with custom made +objects that origin from the same JavaScript context. Just like the +typeof operator, every other use of it should be avoided.

Type casting

JavaScript is a weakly typed language, so it will apply type coercion +wherever possible.

+ +
// These are true
+new Number(10) == 10; // Number.toString() is converted
+                      // back to a number
+
+10 == '10';           // Strings gets converted to Number
+10 == '+10 ';         // More string madness
+10 == '010';          // And more 
+isNaN(null) == false; // null converts to 0
+                      // which of course is not NaN
+
+// These are false
+10 == 010;
+10 == '-10';
+
+ + + +

In order to avoid the above, use of the strict equal operator +is highly recommended. Although this avoids a lot of common pitfalls, there +are still many further issues that arise from JavaScript's weak typing system.

+ +

Constructors of built-in types

+ +

The constructors of the built in types like Number and String behave +differently when being used with the new keyword and without it.

+ +
new Number(10) === 10;     // False, Object and Number
+Number(10) === 10;         // True, Number and Number
+new Number(10) + 0 === 10; // True, due to implicit conversion
+
+ +

Using a built-in type like Number as a constructor will create a new Number +object, but leaving out the new keyword will make the Number function behave +like a converter.

+ +

In addition, having literals or non-object values in there will result in even +more type coercion.

+ +

The best option is to cast to one of the three possible types explicitly.

+ +

Casting to a string

+ +
'' + 10 === '10'; // true
+
+ +

By prepending a empty string a value can easily be casted to a string.

+ +

Casting to a number

+ +
+'10' === 10; // true
+
+ +

Using the unary plus operator it is possible to cast to a number.

+ +

Casting to a boolean

+ +

By using the not operator twice, a value can be converted a boolean.

+ +
!!'foo';   // true
+!!'';      // false
+!!'0';     // true
+!!'1';     // true
+!!'-1'     // true
+!!{};      // true
+!!true;    // true
+

Core

Why not to use eval

The eval function will execute a string of JavaScript code in the local scope.

+ +
var foo = 1;
+function test() {
+    var foo = 2;
+    eval('foo = 3');
+    return foo;
+}
+test(); // 3
+foo; // 1
+
+ +

But eval only executes in local scope when it is being called directly and +the name of the called function is actually eval.

+ +
var foo = 1;
+function test() {
+    var foo = 2;
+    var bar = eval;
+    bar('foo = 3');
+    return foo;
+}
+test(); // 2
+foo; // 3
+
+ +

The use of eval should be avoided at all costs. 99.9% of its "uses" can be +achieved without it.

+ +

eval in disguise

+ +

The timeout functions setTimeout and setInterval can both +take a string as their first argument. This string will always get executed +in the global scope since eval is not being called directly in that case.

+ +

Security issues

+ +

eval also is a security problem as it executes any code given to it, +it should never be used with strings of unknown or untrusted origins.

+ +

In conclusion

+ +

eval should never be used, any code that makes use of it is to be questioned in +its workings, performance and security. In case something requires eval in +order to work, its design is to be questioned and should not be used in the +first place, a better design should be used, that does not require the use of +eval.

undefined and null

JavaScript has two distinct values for nothing, the more useful of these two +being undefined.

+ +

The value undefined

+ +

undefined is a type with exactly one value: undefined.

+ +

The language also defines a global variable that has the value of undefined, +this variable is also called undefined. But this variable is not a constant, +nor is it a keyword of the language. This means that its value can be easily +overwritten.

+ + + +

Some examples for when the value undefined is returned:

+ +
    +
  • Accessing the (unmodified) global variable undefined.
  • +
  • Implicit returns of functions due to missing return statements.
  • +
  • return statements which do not explicitly return anything.
  • +
  • Lookups of non-existent properties.
  • +
  • Function parameters which do not had any explicit value passed.
  • +
  • Anything that has been set to the value of undefined.
  • +
+ +

Handling changes to the value of undefined

+ +

Since the global variable undefined only holds a copy of the actual value of +undefined, assigning a new value to it does not change the value of the +type undefined.

+ +

Still, in order to compare something against the value of undefined it is +necessary to retrieve the value of undefined first.

+ +

In order to protect code against a possible overwritten undefined variable, a +common technique used is to add an additional parameter to an +anonymous wrapper, that gets no argument passed to it.

+ +
var undefined = 123;
+(function(something, foo, undefined) {
+    // undefined in the local scope does 
+    // now again refer to the value
+
+})('Hello World', 42);
+
+ +

Another way to achieve the same effect would be to use a declaration inside the +wrapper.

+ +
var undefined = 123;
+(function(something, foo) {
+    var undefined;
+    ...
+
+})('Hello World', 42);
+
+ +

The only difference being here, that this version results in 4 more bytes being +used in case it is minified and there is no other var statement inside the +anonymous wrapper.

+ +

Uses of null

+ +

While undefined in the context of the JavaScript language is mostly used in +the sense of a traditional null, the actual null (both a literal and a type) +is more or less just another data type.

+ +

It is used in some JavaScript internals (like declaring the end of the +prototype chain by setting Foo.prototype = null), but in almost all cases it +can be replaced by undefined.

Automatic semicolon insertion

Although JavaScript has C style syntax, it does not enforce the use of +semicolons in the source code, it is possible to omit them.

+ +

But JavaScript is not a semicolon-less language, it in fact needs the +semicolons in order to understand the source code. Therefore the JavaScript +parser automatically inserts them whenever it encounters a parse +error due to a missing semicolon.

+ +
var foo = function() {
+} // parse error, semicolon expected
+test()
+
+ +

Insertion happens, and the parser tries again.

+ +
var foo = function() {
+}; // no error, parser continues
+test()
+
+ +

The automatic insertion of semicolon is considered to be one of biggest +design flaws in the language, as it can change the behavior of code.

+ +

How it works

+ +

The code below has no semicolons in it, so it is up to the parser to decide where +to insert them.

+ +
(function(window, undefined) {
+    function test(options) {
+        log('testing!')
+
+        (options.list || []).forEach(function(i) {
+
+        })
+
+        options.value.test(
+            'long string to pass here',
+            'and another long string to pass'
+        )
+
+        return
+        {
+            foo: function() {}
+        }
+    }
+    window.test = test
+
+})(window)
+
+(function(window) {
+    window.someLibrary = {}
+
+})(window)
+
+ +

Below is the result of the parser's "guessing" game.

+ +
(function(window, undefined) {
+    function test(options) {
+
+        // Not inserted, lines got merged
+        log('testing!')(options.list || []).forEach(function(i) {
+
+        }); // <- inserted
+
+        options.value.test(
+            'long string to pass here',
+            'and another long string to pass'
+        ); // <- inserted
+
+        return; // <- inserted, breaks the return statement
+        { // treated as a block
+
+            // a label and a single expression statement
+            foo: function() {} 
+        }; // <- inserted
+    }
+    window.test = test; // <- inserted
+
+// The lines got merged again
+})(window)(function(window) {
+    window.someLibrary = {}; // <- inserted
+
+})(window); //<- inserted
+
+ + + +

The parser drastically changed the behavior of the code above, in certain cases +it does the wrong thing.

+ +

Leading parenthesis

+ +

In case of a leading parenthesis, the parser will not insert a semicolon.

+ +
log('testing!')
+(options.list || []).forEach(function(i) {})
+
+ +

This code gets transformed into one line.

+ +
log('testing!')(options.list || []).forEach(function(i) {})
+
+ +

Chances are very high that log does not return a function; therefore, +the above will yield a TypeError stating that undefined is not a function.

+ +

In conclusion

+ +

It is highly recommended to never omit semicolons, it is also advocated to +keep braces on the same line with their corresponding statements and to never omit +them for one single-line if / else statements. Both of these measures will +not only improve the consistency of the code, they will also prevent the +JavaScript parser from changing its behavior.

Other

setTimeout and setInterval

Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the setTimeout and setInterval functions.

+ + + +
function foo() {}
+var id = setTimeout(foo, 1000); // returns a Number > 0
+
+ +

When setTimeout gets called, it will return the ID of the timeout and schedule +foo to run in approximately one thousand milliseconds in the future. +foo will then get executed exactly once.

+ +

Depending on the timer resolution of the JavaScript engine that is running the +code, as well as the fact that JavaScript is single threaded and other code that +gets executed might block the thread, it is by no means a safe bet that one +will get the exact delay that was specified in the setTimeout call.

+ +

The function that was passed as the first parameter will get called by the +global object, that means, that this inside the called function +refers to that very object.

+ +
function Foo() {
+    this.value = 42;
+    this.method = function() {
+        // this refers to the global object
+        console.log(this.value); // will log undefined
+    };
+    setTimeout(this.method, 500);
+}
+new Foo();
+
+ + + +

Stacking calls with setInterval

+ +

While setTimeout only runs the function once, setInterval - as the name +suggests - will execute the function every X milliseconds. But its use is +discouraged.

+ +

When code that is being executed blocks the timeout call, setInterval will +still issue more calls to the specified function. This can, especially with small +intervals, result in function calls stacking up.

+ +
function foo(){
+    // something that blocks for 1 second
+}
+setInterval(foo, 100);
+
+ +

In the above code foo will get called once and will then block for one second.

+ +

While foo blocks the code setInterval will still schedule further calls to +it. Now, when foo has finished, there will already be ten further calls to +it waiting for execution.

+ +

Dealing with possible blocking code

+ +

The easiest as well as most controllable solution, is to use setTimeout within +the function itself.

+ +
function foo(){
+    // something that blocks for 1 second
+    setTimeout(foo, 100);
+}
+foo();
+
+ +

Not only does this encapsulate the setTimeout call, but it also prevents the +stacking of calls and it gives additional control.foo itself can now decide +whether it wants to run again or not.

+ +

Manually clearing timeouts

+ +

Clearing timeouts and intervals works by passing the respective ID to +clearTimeout or clearInterval, depending which set function was used in +the first place.

+ +
var id = setTimeout(foo, 1000);
+clearTimeout(id);
+
+ +

Clearing all timeouts

+ +

As there is no built-in method for clearing all timeouts and/or intervals, +it is necessary to use brute force in order to achieve this functionality.

+ +
// clear "all" timeouts
+for(var i = 1; i < 1000; i++) {
+    clearTimeout(i);
+}
+
+ +

There might still be timeouts that are unaffected by this arbitrary number; +therefore, is is instead recommended to keep track of all the timeout IDs, so +they can be cleared specifically.

+ +

Hidden use of eval

+ +

setTimeout and setInterval can also take a string as their first parameter. +This feature should never be used, since it internally makes use of eval.

+ + + +
function foo() {
+    // will get called
+}
+
+function bar() {
+    function foo() {
+        // never gets called
+    }
+    setTimeout('foo()', 1000);
+}
+bar();
+
+ +

Since eval is not getting called directly in this case, the string +passed to setTimeout will get executed in the global scope; thus, it will +not use the local variable foo from the scope of bar.

+ +

It is further recommended to not use a string for passing arguments to the +function that will get called by either of the timeout functions.

+ +
function foo(a, b, c) {}
+
+// NEVER use this
+setTimeout('foo(1,2, 3)', 1000)
+
+// Instead use an anonymous function
+setTimeout(function() {
+    foo(a, b, c);
+}, 1000)
+
+ + + +

In conclusion

+ +

Never should a string be used as the parameter of setTimeout or +setInterval. It is a clear sign of really bad code, when arguments need +to be supplied to the function that gets called. An anonymous function should +be passed that then takes care of the actual call.

+ +

Further, the use of setInterval should be avoided since its scheduler is not +blocked by executing JavaScript.

Copyright © 2011. Built with +Node.jsusing a +jadetemplate. +

\ No newline at end of file diff --git a/site/zh/index.html b/site/zh/index.html index 0671f9d2..855430d7 100644 --- a/site/zh/index.html +++ b/site/zh/index.html @@ -1,23 +1,23 @@ JavaScript Garden -

Intro

JavaScript 秘密花园是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 +

简介

JavaScript 秘密花园是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, 初学者可以籍此深入了解 JavaScript 的语言特性。

JavaScript 秘密花园不是用来教你 JavaScript。为了更好的理解这篇文章的内容, -你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习向导

 关于作者(The Authors)

这篇文章的作者是两位 Stack Overflow 的用户, Ivo Wetzel (写作) 和 Zhang Yi Jiang (设计)。

 贡献者(Contributors)

    +你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习向导

 关于作者

这篇文章的作者是两位 Stack Overflow 的用户, Ivo Wetzel (写作) 和 Zhang Yi Jiang (设计)。

 许可(License)

JavaScript 秘密花园在 MIT license 许可协议下发布,并存放在开源社区 GitHub。 +

 许可

JavaScript 秘密花园在 MIT license 许可协议下发布,并存放在开源社区 GitHub。 如果你发现错误或者打字错误,请 file an issue 或者 pull request。 -你也可以在 Stack Overflow 的聊天室 JavaScript room 找到我们。

Objects

 对象使用和属性(Object Usage and Properties)

JavaScript 中所有变量都是对象,除了两个例外 nullundefined

+你也可以在 Stack Overflow 的聊天室 JavaScript room 找到我们。

对象

 对象使用和属性

JavaScript 中所有变量都是对象,除了两个例外 nullundefined

false.toString() // 'false'
 [1, 2, 3].toString(); // '1,2,3'
@@ -108,7 +108,7 @@ 

中文翻译(Chinese Translation)

上面的第二种声明方式在 ECMAScript 5 之前会抛出 SyntaxError 的错误。

这个错误的原因是 delete 是 JavaScript 语言的一个关键词;因此为了在更低版本的 JavaScript 引擎下也能正常运行, -必须使用字符串字面值声明方式。

 原型(The prototype)

JavaScript 不包含传统的类继承模型,而是使用 prototypical 原型模型。

+必须使用字符串字面值声明方式。

 原型

JavaScript 不包含传统的类继承模型,而是使用 prototypical 原型模型。

虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。 实现传统的类继承模型是很简单,但是实现 JavaScript 中的原型继承则要困难的多。 @@ -205,7 +205,7 @@

中文翻译(Chinese Translation)

在写复杂的 JavaScript 应用之前,充分理解原型链继承的工作方式是每个 JavaScript 程序员必修的功课。 要提防原型链过长带来的性能问题,并知道如何通过缩短原型链来提高性能。 -更进一步,绝对不要扩展内置类型的原型,除非是为了和新的 JavaScript 引擎兼容。

 hasOwnProperty

为了判断一个对象是否包含自定义属性而不是原型链上的属性, +更进一步,绝对不要扩展内置类型的原型,除非是为了和新的 JavaScript 引擎兼容。

 hasOwnProperty 函数

为了判断一个对象是否包含自定义属性而不是原型链上的属性, 我们需要使用继承自 Object.prototypehasOwnProperty 方法。

 for in 循环(The for in Loop)

in 操作符一样,for in 循环同样在查找对象属性时遍历原型链上的所有属性。

+这将会避免原型对象扩展带来的干扰。

 for in 循环

in 操作符一样,for in 循环同样在查找对象属性时遍历原型链上的所有属性。

最佳实践(Best practices)

-

推荐总是使用 hasOwnProperty。不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。

Functions

 函数声明与表达式(Function Declarations and Expressions)

函数是JavaScript中的一等对象,这意味着可以把函数像其它值一样传递。 +

推荐总是使用 hasOwnProperty。不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。

函数

 函数声明与表达式

函数是JavaScript中的一等对象,这意味着可以把函数像其它值一样传递。 一个常见的用法是把匿名函数作为回调函数传递对异步函数中。

函数声明(The function declaration)

@@ -334,7 +334,7 @@

中文翻译(Chinese Translation)

bar 函数声明外是不可见的,这是因为我们已经把函数赋值给了 foo; 然而在 bar 内部依然可见。这是由于 JavaScript 的 命名处理 所致, -函数名在函数内总是可见的。

 this 的工作原理(How this works)

JavaScript 有一套完全不同于其它语言的对 this 的处理机制。 +函数名在函数内总是可见的。

 this 的工作原理

JavaScript 有一套完全不同于其它语言的对 this 的处理机制。 在种不同的情况下 ,this 指向的各不相同。

全局范围内(The global scope)

@@ -442,7 +442,7 @@

中文翻译(Chinese Translation)

new Bar().method();
-

method 被调用时,this 将会指向 Bar 的实例对象。

 闭包和引用(Closures and references)

闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 +

method 被调用时,this 将会指向 Bar 的实例对象。

 闭包和引用

闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 因为 函数 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。

模拟私有变量(Emulating private variables)

@@ -526,7 +526,7 @@

中文翻译(Chinese Translation)

} })(i), 1000) } -

 arguments 对象(The arguments object)

JavaScript 中每个函数内都能访问一个特别变量 arguments。这个变量维护着所有传递到这个函数中的参数列表。

+

 arguments 对象

JavaScript 中每个函数内都能访问一个特别变量 arguments。这个变量维护着所有传递到这个函数中的参数列表。

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.

+

 构造函数

JavaScript 中的构造函数和其它语言中的构造函数是不同的。 +通过 new 关键字方式调用的函数都被认为是构造函数。

-

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.

+

在构造函数内部 - 也就是被调用的函数内 - this 指向新创建的对象 Object。 +这个新创建的对象的 prototype 被设置为作为构造函数的 prototype

-

If the function that was called has no explicit return statement, then it -implicitly returns the value of this - the new object.

+

如果被调用的函数没有显式的 return 表达式,则隐式的会返回 this 对象 - 也就是新创建的对象。

function Foo() {
     this.bla = 1;
@@ -674,16 +671,23 @@ 

中文翻译(Chinese Translation)

var test = new Foo();
-

The above calls Foo as constructor and sets the prototype of the newly -created object to Foo.prototype.

+

上面代码把 Foo 作为构造函数调用,并设置新创建对象的 prototypeFoo.prototype

-

In case of an explicit return statement the function returns the value -specified that statement, but only if the return value is an Object.

+

显式的 return 表达式将会影响返回结果,但仅限于返回的是一个对象。

function Bar() {
     return 2;
 }
-new Bar(); // a new object
+new Bar(); // 返回新创建的对象
+
+// 译者注:new Bar() 返回的是新创建的对象,而不是数字 2。
+// 有 new Bar().constructor === Bar
+// 但是如果返回的是数字对象,结果就不同了
+// function Bar() {
+//    return new Number(2);
+// }
+// new Bar().constructor === Number
+
 
 function Test() {
     this.value = 2;
@@ -692,25 +696,26 @@ 

中文翻译(Chinese Translation)

foo: 1 }; } -new Test(); // the returned object +new Test(); // 返回的对象 +// 译者注:这里得到的是函数返回的对象,而不是通过 new 关键字新创建的对象 +// 所有 (new Test()).value 为 undefined,但是 (new Test()).foo === 1。
-

When the new keyword is omitted, the function will not return a new object.

+

如果 new 被遗漏了,则函数不会返回新创建的对象。 +When the new keyword is omitted, the function will not return a new object.

function Foo() {
-    this.bla = 1; // gets set on the global object
+    this.bla = 1; // 获取设置全局参数
 }
 Foo(); // undefined
 
-

While the above example might still appear to work in some cases, due to the -workings of this in JavaScript, it will use the -global object as the value of this.

+

虽然上例在有些情况下也能正常运行,但是由于 JavaScript 中 this 的工作原理, +这里的 this 指向全局对象

-

Factories

+

工厂模式(Factories)

-

In order to be able to omit the new keyword, the constructor function has to -explicitly return a value.

+

为了不使用 new 关键字,构造函数必须显式的返回一个值。

function Bar() {
     var value = 1;
@@ -728,24 +733,29 @@ 

中文翻译(Chinese Translation)

Bar();
-

Both calls to Bar return the exact same thing, a newly create object which -has a property called method on it, that is a -Closure.

+

上面两个对 Bar 函数的调用返回的值完全相同,一个新创建的拥有一个叫 method 属性的对象被返回, +其实这里创建了一个闭包

-

It is also to note that the call new Bar() does not affect the prototype -of the returned object. While the prototype will be set on the newly created -object, Bar never returns that new object.

+

还需要注意,new Bar()不会改变返回对象的原型(译者注:也就是返回对象的原型不会指向 Bar.prototype)。 +因为构造函数的原型会被指向到刚刚创建的新对象,而这里的 Bar 没有把这个新对象返回(译者注:而是返回了一个包含 method 属性的自定义对象)。

-

In the above example, there is no functional difference between using and -not using the new keyword.

+

在上面的例子中,使用或者不使用 new 关键字没有功能性的区别。

-

Creating new objects via factories

+
// 译者注:上面两种方式创建的对象不能访问 Bar 原型链上的属性
+var bar1 = new Bar();
+typeof(bar1.method); // "function"
+typeof(bar1.foo); // "undefined"
+
+var bar2 = Bar();
+typeof(bar2.method); // "function"
+typeof(bar2.foo); // "undefined"
+
-

An often made recommendation is to not use new since forgetting its use -may lead to bugs.

+

通过工厂模式创建新对象(Creating new objects via factories)

-

In order to create new object, one should rather use a factory and construct a -new object inside of that factory.

+

我们常听到的一条忠告是不要使用 new 关键字来调用函数,因为如果忘记使用它就会导致错误。

+ +

为了创建新对象,我们可以创建一个工厂方法,并且在方法内构造一个新对象。

function Foo() {
     var obj = {};
@@ -763,26 +773,19 @@ 

中文翻译(Chinese Translation)

}
-

While the above is robust against a missing new keyword and certainly makes -the use of private variables easier, it comes with some -downsides.

+

虽然上面的方式比起 new 的调用方式不容易出错,并且可以充分利用私有变量带来的便利, +但是随之而来的是一些不好的地方。

    -
  1. It uses more memory since the created objects do not share the methods -on a prototype.
  2. -
  3. In order to inherit the factory needs to copy all the methods from another -object or put that object on the prototype of the new object.
  4. -
  5. Dropping the prototype chain just because of a left out new keyword -somehow goes against the spirit of the language.
  6. +
  7. 会占用更多的内存,因为新创建的对象不能共享原型上的方法。
  8. +
  9. 为了实现继承,工厂方法需要从另外一个对象拷贝所有属性,或者把一个对象作为新创建对象的原型。
  10. +
  11. 放弃原型链仅仅是因为防止遗漏 new 带来的问题,这似乎和语言本身的思想相违背。
-

In conclusion

+

总结(In conclusion)

-

While omitting the new keyword might lead to bugs, it is certainly not a -reason to drop the use of prototypes altogether. In the end it comes down to -which solution is better suited for the needs of the application, it is -especially important to choose a specific style of object creation and stick -with it.

 作用域与命名空间(Scopes and namespaces)

尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; +

虽然遗漏 new 关键字可能会导致问题,但这并不是放弃使用原型链的借口。 +最终使用哪种方式取决于应用程序的需求,选择一种代码书写风格并坚持下去才是最重要的。

 作用域与命名空间

尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; 而仅仅支持 函数作用域

function test() { // 一个作用域
@@ -1014,7 +1017,7 @@ 

中文翻译(Chinese Translation)

推荐使用匿名包装器(译者注:也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, 而且有利于程序的模块化。

-

另外,使用全局变量被认为是不好的习惯。这样的代码倾向于产生错误和带来高的维护成本。

Arrays

Array Iteration and Properties

Although arrays in JavaScript are objects, there are no good reasons to use +

另外,使用全局变量被认为是不好的习惯。这样的代码倾向于产生错误和带来高的维护成本。

数组

Array Iteration and Properties

Although arrays in JavaScript are objects, there are no good reasons to use the for in loop in for iteration on them. In fact there are a number of good reasons against the use of for in on arrays.

@@ -1106,7 +1109,7 @@

中文翻译(Chinese Translation)

The use of the Array constructor should be avoided as much as possible. Literals are definitely preferred. They are shorter and have a clearer syntax; -therefore, they also increase the readability of the code.

Types

Equality and comparisons

JavaScript has two different ways of comparing the values of objects for equality.

+therefore, they also increase the readability of the code.

类型

Equality and comparisons

JavaScript has two different ways of comparing the values of objects for equality.

The equals operator

@@ -1373,7 +1376,7 @@

中文翻译(Chinese Translation)

!!'-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.

+

核心

Why not to use eval

The eval function will execute a string of JavaScript code in the local scope.

var foo = 1;
 function test() {
@@ -1609,7 +1612,7 @@ 

中文翻译(Chinese Translation)

keep braces on the same line with their corresponding statements and to never omit them for one single-line if / else statements. Both of these measures will not only improve the consistency of the code, they will also prevent the -JavaScript parser from changing its behavior.

Other

setTimeout and setInterval

Since JavaScript is asynchronous, it is possible to schedule the execution of a +JavaScript parser from changing its behavior.

其它

setTimeout and setInterval

Since JavaScript is asynchronous, it is possible to schedule the execution of a function by using the setTimeout and setInterval functions.

 许可

JavaScript 秘密花园在 MIT license 许可协议下发布,并存放在开源社区 GitHub。 如果你发现错误或者打字错误,请 file an issue 或者 pull request。 你也可以在 Stack Overflow 的聊天室 JavaScript room 找到我们。

对象

 对象使用和属性

JavaScript 中所有变量都是对象,除了两个例外 nullundefined

@@ -70,8 +72,9 @@

中文翻译

两种语法是等价的,但是中括号操作符在下面两种情况下依然有效 - 动态设置属性 - - 属性名不是一个有效的变量名(译者注:比如属性名中包含空格,或者属性名是JS的关键词) -(译者注:在 JSLint 语法检测工具中,点操作符是推荐做法)

+ - 属性名不是一个有效的变量名(译者注:比如属性名中包含空格,或者属性名是JS的关键词)

+ +

译者注:在 JSLint 语法检测工具中,点操作符是推荐做法。

删除属性(Deleting properties)

@@ -159,9 +162,9 @@

中文翻译

Foo instance, since that property gets defined in the constructor of Foo. But this constructor has to be called explicitly.)

-

(译者注:我认为这个描述是错误的,test.value 是可以访问的。 +

译者注:我认为这个描述是错误的,test.value 是可以访问的。 因为在设置 Bar.prototype = new Foo(); 时,value 也就成为 Bar.prototype 上的一个属性。 -如果你有不同观点,可以到我的博客评论。)

+如果你有不同观点,可以到我的博客评论。

总结(In conclusion)

@@ -342,8 +346,9 @@

中文翻译

this;
 
-

当在全部范围内使用 this,它将会指向全局对象。 -(译者注:浏览器中运行的JavaScript脚本,这个全局对象是 window)

+

当在全部范围内使用 this,它将会指向全局对象。

+ +

译者注:浏览器中运行的JavaScript脚本,这个全局对象是 window。

函数调用(Calling a function)

@@ -354,7 +359,7 @@

中文翻译

方法调用(Calling a method)

@@ -389,17 +394,17 @@

中文翻译

常见误解(Common pitfalls)

-

尽管大部分的情况都说的过去,不过第一个规则(译者注:这里指的应该是第二个规则,也就是直接调用函数时,this 指向全局对象) +

尽管大部分的情况都说的过去,不过第一个规则(译者注:这里指的应该是第二个规则,也就是直接调用函数时,this 指向全局对象) 被认为是JavaScript语言另一个错误设计的地方,因为它从来就没有实际的用途。

Foo.method = function() {
     function test() {
-        // this 将会被设置为全局对象(译者注:浏览器环境中也就是 window 对象)
+        // this 将会被设置为全局对象([译者注][30]:浏览器环境中也就是 window 对象)
     }
     test();
 }
@@ -502,7 +507,7 @@ 

中文翻译

避免引用错误(Avoiding the reference problem)

-

为了正确的获得循环序号,最好使用 匿名包裹器(译者注:其实就是我们通常说的自执行匿名函数)。

+

为了正确的获得循环序号,最好使用 匿名包裹器译者注:其实就是我们通常说的自执行匿名函数)。

for(var i = 0; i < 10; i++) {
     (function(e) {
@@ -578,7 +583,7 @@ 

中文翻译

};
-

译者注:上面的 Foo.method 函数和下面代码的效果是一样的。

+

译者注:上面的 Foo.method 函数和下面代码的效果是一样的:

Foo.method = function() {
     var args = Array.prototype.slice.call(arguments);
@@ -617,9 +622,9 @@ 

中文翻译

-

译者注:在 MDC 中对 strict mode 模式下 arguments 的描述有助于我们的理解。

+

译者注:在 MDC 中对 strict mode 模式下 arguments 的描述有助于我们的理解,请看下面代码:

-
// 译者注:来自 [MDC][2] 的代码,说明在 ES5 的严格模式下 `arguments` 的特性。
+
// 阐述在 ES5 的严格模式下 `arguments` 的特性
 function f(a)
 {
   "use strict";
@@ -645,7 +650,7 @@ 

中文翻译

}
-

上面代码中,foo 不再是一个单纯的内联函数 inlining(译者注:这里指的是解析器可以做内联处理), +

上面代码中,foo 不再是一个单纯的内联函数 inlining译者注:这里指的是解析器可以做内联处理), 因为它需要知道它自己和它的调用者。 这不仅抵消了内联函数带来的性能提升,而且破坏了封装,因此现在函数可能要依赖于特定的上下文。

@@ -656,7 +661,7 @@

中文翻译

通过 new 关键字方式调用的函数都被认为是构造函数。

在构造函数内部 - 也就是被调用的函数内 - this 指向新创建的对象 Object。 -这个新创建的对象的 prototype 被设置为作为构造函数的 prototype

+这个新创建的对象的 prototype 被指向到构造函数的 prototype

如果被调用的函数没有显式的 return 表达式,则隐式的会返回 this 对象 - 也就是新创建的对象。

@@ -680,15 +685,6 @@

中文翻译

} new Bar(); // 返回新创建的对象 -// 译者注:new Bar() 返回的是新创建的对象,而不是数字 2。 -// 有 new Bar().constructor === Bar -// 但是如果返回的是数字对象,结果就不同了 -// function Bar() { -// return new Number(2); -// } -// new Bar().constructor === Number - - function Test() { this.value = 2; @@ -697,12 +693,22 @@

中文翻译

}; } new Test(); // 返回的对象 -// 译者注:这里得到的是函数返回的对象,而不是通过 new 关键字新创建的对象 -// 所有 (new Test()).value 为 undefined,但是 (new Test()).foo === 1。
-

如果 new 被遗漏了,则函数不会返回新创建的对象。 -When the new keyword is omitted, the function will not return a new object.

+

译者注: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; // 获取设置全局参数
@@ -733,16 +739,17 @@ 

中文翻译

Bar();
-

上面两个对 Bar 函数的调用返回的值完全相同,一个新创建的拥有一个叫 method 属性的对象被返回, +

上面两种对 Bar 函数的调用返回的值完全相同,一个新创建的拥有 method 属性的对象被返回, 其实这里创建了一个闭包

-

还需要注意,new Bar()不会改变返回对象的原型(译者注:也就是返回对象的原型不会指向 Bar.prototype)。 -因为构造函数的原型会被指向到刚刚创建的新对象,而这里的 Bar 没有把这个新对象返回(译者注:而是返回了一个包含 method 属性的自定义对象)。

+

还需要注意,new Bar()不会改变返回对象的原型(译者注:也就是返回对象的原型不会指向 Bar.prototype)。 +因为构造函数的原型会被指向到刚刚创建的新对象,而这里的 Bar 没有把这个新对象返回(译者注:而是返回了一个包含 method 属性的自定义对象)。

在上面的例子中,使用或者不使用 new 关键字没有功能性的区别。

-
// 译者注:上面两种方式创建的对象不能访问 Bar 原型链上的属性
-var bar1 = new Bar();
+

译者注:上面两种方式创建的对象不能访问 Bar 原型链上的属性,如下所示:

+ +
var bar1 = new Bar();
 typeof(bar1.method); // "function"
 typeof(bar1.foo); // "undefined"
 
@@ -801,7 +808,7 @@ 

中文翻译

而不是作为对象的字面语法解析。如果考虑到 自动分号插入,这可能会导致一些不易察觉的错误。

-

译者注:如果 return 对象的左括号和 return 不在一行上就会出错。

+

译者注:如果 return 对象的左括号和 return 不在一行上就会出错。

// 译者注:下面输出 undefined
 function add(a, b) {
@@ -953,7 +960,7 @@ 

中文翻译

}
-

译者注:在 Nettuts+ 网站有一篇介绍 hoisting 的文章,其中的代码很有启发性。

+

译者注:在 Nettuts+ 网站有一篇介绍 hoisting 的文章,其中的代码很有启发性。

// 译者注:来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则
 var myvar = 'my value';  
@@ -988,7 +995,7 @@ 

中文翻译

只有一个全局作用域导致的常见错误是命名冲突。在 JavaScript中,这可以通过 匿名包装器 轻松解决。

(function() {
-    // 函数创建一个命名空间(译者注:也就是作用域)
+    // 函数创建一个命名空间
 
     window.foo = function() {
         // 对外公开的函数,创建了闭包
@@ -1014,28 +1021,23 @@ 

中文翻译

结论(In conclusion)

-

推荐使用匿名包装器(译者注:也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, +

推荐使用匿名包装器译者注:也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, 而且有利于程序的模块化。

-

另外,使用全局变量被认为是不好的习惯。这样的代码倾向于产生错误和带来高的维护成本。

数组

Array Iteration and Properties

Although arrays in JavaScript are objects, there are no good reasons to use -the for in loop in for iteration on them. In fact there -are a number of good reasons against the use of for in on arrays.

+

另外,使用全局变量被认为是不好的习惯。这样的代码倾向于产生错误和带来高的维护成本。

数组

 数组遍历与属性

虽然在 JavaScript 中数组是是对象,但是没有好的理由去使用 for in 循环 遍历数组。 +相反,有一些好的理由不去使用 for in 遍历数组。

-

Since the for in loop enumerates all the properties that are on the prototype -chain and the only way to exclude those properties is to use -hasOwnProperty, it is already up to twenty times -slower than a normal for loop.

+

由于 for in 循环会枚举原型链上的所有属性,唯一过滤这些属性的方式是使用 hasOwnProperty 函数, +因此会比普通的 for 循环慢上好多倍。

-

Iteration

+

遍历(Iteration)

-

In order to achieve the best performance when iterating over arrays, it is best -to use the classic for loop.

+

为了达到遍历数组的最佳性能,推荐使用经典的 for 循环。

var list = [1, 2, 3, 4, 5, ...... 100000000];
 for(var i = 0, l = list.length; i < l; i++) {
@@ -1043,22 +1045,16 @@ 

中文翻译

}
-

There is one extra catch in the above example, that is the caching of the -length of the array via l = list.length.

+

上面代码有一个处理,就是通过 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.

+

虽然 length 是数组的一个属性,但是在每次循环中访问它还是有性能开销。 +可能最新的 JavaScript 引擎在这点上做了优化,但是我们没法保证自己的代码是否运行在这些最近的引擎之上。

-

In fact, leaving out the caching may result in the loop being only half as -fast as with the cached length.

+

实际上,不使用缓存数组长度的方式比缓存版本要慢很多。

-

The length property

+

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.

+

length 属性的 getter 方式会简单的返回数组的长度,而 setter 方式会截断数组。

var foo = [1, 2, 3, 4, 5, 6];
 foo.length = 3;
@@ -1068,55 +1064,65 @@ 

中文翻译

foo; // [1, 2, 3]
-

Assigning a smaller length does truncate the array, but increasing the length -does not have any effect on the array.

+

译者注: +在 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 属性值不会对数组产生影响。

+ +

结论(In conclusion)

+ +

为了更好的性能,推荐使用普通的 for 循环并缓存数组的 length 属性。 +使用 for in 遍历数组被认为是不好的代码习惯并倾向于产生错误和导致性能问题。

 Array 构造函数

由于 Array 的构造函数在如何处理参数时有点模棱两可,因此总是推荐使用数组的字面语法 - [] - 来创建数组。

-

In conclusion

+
[1, 2, 3]; // 结果: [1, 2, 3]
+new Array(1, 2, 3); // 结果: [1, 2, 3]
 
-

For the best performance it is recommended to always use the plain for loop -and cache the length property. The use of for in on an array is a sign of -badly written code that is prone to bugs and bad performance.

The Array Constructor

Since the Array constructor is ambiguous in how it deals with its parameters, -it is highly recommended to always use the array literals - [] notation - -when creating new arrays.

+[3]; // 结果: [3] +new Array(3); // 结果: [] +new Array('3') // 结果: ['3'] + -
[1, 2, 3]; // Result: [1, 2, 3]
-new Array(1, 2, 3); // Result: [1, 2, 3]
+

译者注:这里的模棱两可指的是数组的两种构造函数语法 +var arr1 = new Array(arrayLength); +var arr2 = new Array(element0, element1, ..., elementN);

-[3]; // Result: [3] -new Array(3); // Result: [] -new Array('3') // Result: ['3'] +
// 译者注:因此下面的代码将会使人很迷惑
+new Array(3, 4, 5); // 结果: [3, 4, 5] 
+new Array(3) // 结果: [],此数组长度为 3
 
-

In cases when there is only one argument passed to the Array constructor, -and that argument is a Number, the constructor will return a new sparse -array with the length property set to the value of the argument. It should be -noted that only the length property of the new array will be set this way, -the actual indexes of the array will not be initialized.

+

由于只有一个参数传递到构造函数中(译者注:指的是 new Array(3); 这种调用方式),并且这个参数是数字,构造函数会返回一个 length 属性被设置为此参数的空数组。 +需要特别注意的是,此时只有 length 属性被设置,真正的数组并没有生成。 +译者注:在 Firebug 中,你会看到 [undefined, undefined, undefined],这其实是不对的。在上一节有详细的分析。

var arr = new Array(3);
 arr[1]; // undefined
-1 in arr; // false, the index was not set
+1 in arr; // false, 数组还没有生成
 
-

The behavior of being able to set the length of the array upfront only comes in -handy in a few cases, like repeating a string, in which it avoids the use of a -for loop code.

+

这种优先于设置数组长度属性的做法只在少数几种情况下有用,比如需要循环字符串,可以避免 for 循环的麻烦。

new Array(count + 1).join(stringToRepeat);
+// 译者注:new Array(3).join('#') 将会返回 "##"
 
-

In conclusion

+

结论(In conclusion)

-

The use of the Array constructor should be avoided as much as possible. -Literals are definitely preferred. They are shorter and have a clearer syntax; -therefore, they also increase the readability of the code.

类型

Equality and comparisons

JavaScript has two different ways of comparing the values of objects for equality.

+

应该尽量避免使用数组构造函数创建新数组。推荐使用数组的字面语法。它们更加短小和简洁,因此增加了代码的可读性。

类型

 相等与比较

JavaScript 有两种方式判断两个值是否相等。

-

The equals operator

+

等于操作符(The equals operator)

-

The equals operator consists of two equal signs: ==

+

等于操作符由两个等号组成:==

-

JavaScript features weak typing, that means, that the equals operator -coerces types in order to compare them.

+

JavaScript 是弱类型语言,这就意味着,等于操作符会为了比较两个值而进行强制类型转换

""           ==   "0"           // false
 0            ==   ""            // true
@@ -1129,20 +1135,16 @@ 

中文翻译

" \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 equals operator

+

严格的等于操作符(The strict equals operator)

-

The strict equals operator consists of three equal signs: ===

+

严格的等于操作符由个等号组成:===

-

Other than the normal equals operator, the strict equals operator does not -perform type coercion between its operands.

+

不想普通的等于操作符,严格的等于操作符不会进行强制类型转换。

""           ===   "0"           // false
 0            ===   ""            // false
@@ -1155,14 +1157,11 @@ 

中文翻译

" \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

+

比较对象(Comparing objects)

-

While both == and === are stated as equality operators, they behave -different when at least one of their operands happens to be an Object.

+

虽然 ===== 操作符都是等于操作符,但是当其中有一个操作数为对象时,行为就不同了。

{} === {};                   // false
 new String('foo') === 'foo'; // false
@@ -1171,30 +1170,25 @@ 

中文翻译

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 a pointer comparison in C do.

+

这里等于操作符比较的不是值是否相等,而是是否属于同一个身份;也就是说,只有对象的同一个实例才被认为是相等的。 +这有点像 Python 中的 is 和 C 中的指针比较。

-

In conclusion

+

结论(In conclusion)

-

It is highly recommended to only use the strict equals operator. In cases -where types need to be coerced, it should be done explicitly -and not left to the language's complicated coercion rules.

The typeof operator

The typeof operator (together with -instanceof) is probably the biggest -design flaw of JavaScript, as it is near of being completely broken.

+

强烈推荐使用严格的等于操作符。如果类型需要转换,应该在比较之前显式的转换, +而不是使用语言本身复杂的强制转换规则。

 typeof 操作符

typeof 操作符(和 instanceof 一起)或许是 JavaScript 中最大的设计缺陷, +因为几乎不可能从它们那里得到想要的结果。

-

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.

+

尽管 instanceof 还有一些极少数的应用场景,typeof 只有一个实际的应用(译者注:这个实际应用是用来检测一个对象是否已经定义或者是否已经赋值), +而这个应用却不是用来检查对象的类型。

-

The JavaScript type table

+

JavaScript 类型表格(The JavaScript type table)

Value               Class      Type
 -------------------------------------
@@ -1215,24 +1209,21 @@ 

中文翻译

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.

+

上面表格中,Type 一列表示 typeof 操作符的运算结果。可以看到,这个值在大多数情况下都返回 "object"。

-

The Class refers to the value of the internal [[Class]] property of an object.

+

Class 一列表示对象的内部属性 [[Class]] 的值。

-

In order to retrieve the value of [[Class]] one has to make use of the -toString method of Object.prototype.

+

为了获取对象的 [[Class]],我们需要使用定义在 Object.prototype 上的方法 toString

-

The Class of an object

+

对象的类定义(The Class of an object)

-

The specification gives exactly one way of accessing the [[Class]] value, -with the use of Object.prototype.toString.

+

JavaScript 标准文档只给出了一种获取 [[Class]] 值的方法,那就是使用 Object.prototype.toString

function is(type, obj) {
     var clas = Object.prototype.toString.call(obj).slice(8, -1);
@@ -1243,37 +1234,48 @@ 

中文翻译

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.

+

上面例子中,Object.prototype.toString 方法被调用,this 被设置为了需要获取 [[Class]] 值的对象。

-

Testing for undefined variables

+

译者注:这种变化可以从 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]"
+
+ +

测试为定义变量(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.

+

上面代码会检测 foo 是否已经定义;如果没有定义而直接使用会导致 ReferenceError 的异常。 +这是 typeof 唯一有用的地方。

-

In conclusion

+

结论(In conclusion)

-

In order to check the type of an object, it is highly recommended to use -Object.prototype.toString; as this is the only reliable way of doing so. -As shown in the above type table, some return values of typeof are not defined -in the specification; thus, they can differ across various implementations.

+

为了检测一个对象的类型,强烈推荐使用 Object.prototype.toString 方法; +因为这是唯一一个可依赖的方式。正如上面表格所示,typeof 的一些返回值在标准文档中并未定义, +因此不同的引擎实现可能不同。

-

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.

+

除非为了检测一个变量是否已经定义,我们应尽量避免使用 typeof 操作符。

 instanceof 操作符

instanceof 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 +如果用来比较内置类型,将会和 typeof 操作符 一样用处不大。

-

Comparing custom objects

+

比较自定义对象(Comparing custom objects)

function Foo() {}
 function Bar() {}
@@ -1282,13 +1284,12 @@ 

中文翻译

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 本省,而不是 Foo 构造函数的一个实例 Bar.prototype = Foo; new Bar() instanceof Foo; // false
-

Using instanceof with native types

+

instanceof 比较内置类型(Using instanceof with native types)

new String('foo') instanceof String; // true
 new String('foo') instanceof Object; // true
@@ -1297,77 +1298,79 @@ 

中文翻译

'foo' instanceof Object; // false
-

One important thing to note here is, that instanceof does not work on objects -that origin from different JavaScript contexts (e.g. different documents -in a web browser), since their constructors will not be the exact same object.

+

有一点需要注意,instanceof 用来比较属于不同 JavaScript 上下文的对象(比如,浏览器中不同的文档结构)时将会出错, +因为它们的构造函数不会是同一个对象。

-

In conclusion

+

结论(In conclusion)

-

The instanceof operator should only be used when dealing with custom made -objects that origin from the same JavaScript context. Just like the -typeof operator, every other use of it should be avoided.

Type casting

JavaScript is a weakly typed language, so it will apply type coercion -wherever possible.

+

instanceof 操作符应该仅仅用来比较来自同一个 JavaScript 上下文的自定义对象。 +正如 typeof 操作符一样,任何其它的用法都应该是避免的。

 类型转换

JavaScript 是弱类型语言,所以会在任何可能的情况下应用强制类型转换

-
// These are true
-new Number(10) == 10; // Number.toString() is converted
-                      // back to a number
+
// 下面的比较结果是:true
+new Number(10) == 10; // Number.toString() 返回的字符串被再次转换为数字
 
-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 == '10';           // 字符串被转换为数字
+10 == '+10 ';         // 同上
+10 == '010';          // 同上 
+isNaN(null) == false; // null 被转换为数字 0
+                      // 0 当然不是一个 NaN(译者注:否定之否定)
 
-// These are false
+// 下面的比较结果是:false
 10 == 010;
 10 == '-10';
 
-

Constructors of built-in types

+

内置类型的构造函数(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.

+

内置类型(比如 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
+
new Number(10) === 10;     // False, 对象与数字的比较
+Number(10) === 10;         // True, 数字与数字的比较
+new Number(10) + 0 === 10; // True, 由于隐式的类型转换
 
-

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.

+

使用内置类型 Number 作为构造函数将会创建一个新的 Number 对象, +而在不使用 new 关键字的 Number 函数更像是一个数字转换器。

-

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

+

转换为字符串(Casting to a string)

'' + 10 === '10'; // true
 
-

By prepending a empty string a value can easily be casted to a string.

+

将一个值加上空字符串可以轻松转换为字符串类型。

-

Casting to a number

+

转换为数字(Casting to a number)

+'10' === 10; // true
 
-

Using the unary plus operator it is possible to cast to a number.

+

使用一元的加号操作符,可以把字符串转换为数字。

+ +

译者注:字符串转换为数字的常用方法:

+ +
+'010' === 10
+Number('010') === 10
+parseInt('010', 10) === 10  // 用来转换为整数
 
-

Casting to a boolean

++'010.2' === 10.2 +Number('010.2') === 10.2 +parseInt('010.2', 10) === 10 + + +

转换为布尔型(Casting to a boolean)

-

By using the not operator twice, a value can be converted a boolean.

+

通过使用 操作符两次,可以把一个值转换为布尔型。

!!'foo';   // true
 !!'';      // false
@@ -1376,7 +1379,7 @@ 

中文翻译

!!'-1' // true !!{}; // true !!true; // true -

核心

Why not to use eval

The eval function will execute a string of JavaScript code in the local scope.

+

核心

 为什么不要使用 eval

eval 函数会在当前作用域中执行一段 JavaScript 代码字符串。

var foo = 1;
 function test() {
@@ -1388,8 +1391,7 @@ 

中文翻译

foo; // 1
-

But eval only executes in local scope when it is being called directly and -the name of the called function is actually eval.

+

但是 eval 只在被直接调用并且调用函数就是 eval 本身时,才在当前作用域中执行。

var foo = 1;
 function test() {
@@ -1402,77 +1404,87 @@ 

中文翻译

foo; // 3
-

The use of eval should be avoided at all costs. 99.9% of its "uses" can be -achieved without it.

+

译者注:上面的代码等价于在全局作用域中调用 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 in disguise

+

伪装的 evaleval 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.

+

定时函数 setTimeoutsetInterval 都可以接受字符串作为它们的第一个参数。 +这个字符串总是在全局作用域中执行,因此 eval 在这种情况下没有被直接调用。

-

Security issues

+

安全问题(Security issues)

-

eval also is a security problem as it executes any code given to it, -it should never be used with strings of unknown or untrusted origins.

+

eval 也存在安全问题,因为它会执行任意传给它的代码, +在代码字符串未知或者是来自一个不信任的源时,绝对不要使用 eval 函数。

-

In conclusion

+

结论(In conclusion)

-

eval should never be used, any code that makes use of it is to be questioned in -its workings, performance and security. In case something requires eval in -order to work, its design is to be questioned and should not be used in the -first place, a better design should be used, that does not require the use of -eval.

undefined and null

JavaScript has two distinct values for nothing, the more useful of these two -being undefined.

+

绝对不要使用 eval,任何使用它的代码都会在它的工作方式,性能和安全性方面受到质疑。 +如果一些情况必须使用到 eval 才能正常工作,首先它的设计会受到质疑,这不应该是首选的解决方案, +一个更好的不使用 eval 的解决方案应该得到充分考虑并优先采用。

 undefinednull

JavaScript 有两个表示 的值,其中比较有用的是 undefined

-

The value undefined

+

undefined的值(The value undefined

-

undefined is a type with exactly one value: undefined.

+

undefined 是一个值为 undefined 的类型。

-

The language also defines a global variable that has the value of undefined, -this variable is also called undefined. But this variable is not a constant, -nor is it a keyword of the language. This means that its value can be easily -overwritten.

+

这个语言也定义了一个全局变量,它的值是 undefined,这个变量也被称为 undefined。 +但是这个变量不是一个常量,也不是一个关键字。这意味着它的可以轻易被覆盖。

-

Handling changes to the value of undefined

+

处理 undefined 值的改变(Handling changes to the value of undefined

-

Since the global variable undefined only holds a copy of the actual value of -undefined, assigning a new value to it does not change the value of the -type undefined.

+

由于全局变量 undefined 只是保存了 undefined 类型实际的副本, +因此对它赋新值不会改变类型 undefined 的值。

-

Still, in order to compare something against the value of undefined it is -necessary to retrieve the value of undefined first.

+

然而,为了方便其它变量和 undefined 做比较,我们需要事先获取类型 undefined 的值。

-

In order to protect code against a possible overwritten undefined variable, a -common technique used is to add an additional parameter to an -anonymous wrapper, that gets no argument passed to it.

+

为了避免可能对 undefined 值的改变,一个常用的技巧是使用一个传递到匿名包装器的额外参数。 +在调用时,这个参数不会获取任何值。

var undefined = 123;
 (function(something, foo, undefined) {
-    // undefined in the local scope does 
-    // now again refer to the value
+    // 局部作用域里的 undefined 变量重新获得了 `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) {
@@ -1482,45 +1494,37 @@ 

中文翻译

})('Hello World', 42);
-

The only difference being here, that this version results in 4 more bytes being -used in case it is minified and there is no other var statement inside the -anonymous wrapper.

+

这里唯一的区别是,在压缩后并且函数内没有其它需要使用 var 声明变量的情况下,这个版本的代码会多出 4 个字节的代码。

-

Uses of null

+

译者注:这里有点绕口,其实很简单。 +如果此函数内没有其它需要声明的变量,那么 var 总共 4 个字符(包含一个空白字符)就是专门为 undefined 变量准备的,相比上个例子多出了 4 个字节。

-

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.

+

使用 null(Uses of null

-

It is used in some JavaScript internals (like declaring the end of the -prototype chain by setting Foo.prototype = null), but in almost all cases it -can be replaced by undefined.

Automatic semicolon insertion

Although JavaScript has C style syntax, it does not enforce the use of -semicolons in the source code, it is possible to omit them.

+

JavaScript 中的 undefined 的使用场景类似于其它语言中的 null,实际上 JavaScript 中的 null 是另外一种数据类型。

-

But JavaScript is not a semicolon-less language, it in fact needs the -semicolons in order to understand the source code. Therefore the JavaScript -parser automatically inserts them whenever it encounters a parse -error due to a missing semicolon.

+

它在 JavaScript 内部有一些使用场景(比如声明原型链的终结 Foo.prototype = null),但是大多数情况下都可以使用 undefined 来代替。

 自动分号插入

尽管 JavaScript 有 C 的代码风格,但是它强制要求在代码中使用分号,实际上可以省略它们。

+ +

JavaScript 不是一个没有分号的语言,恰恰相反上它需要分号来就解析源代码。 +因此 JavaScript 解析器在遇到由于缺少分号导致的解析错误时,会自动在源代码中插入分号。

var foo = function() {
-} // parse error, semicolon expected
+} // 解析错误,分号丢失
 test()
 
-

Insertion happens, and the parser tries again.

+

自动插入分号,解析器重新解析。

var foo = function() {
-}; // no error, parser continues
+}; // 没有错误,解析继续
 test()
 
-

The automatic insertion of semicolon is considered to be one of biggest -design flaws in the language, as it can change the behavior of code.

+

自动的分号插入被认为是 JavaScript 语言最大的设计缺陷之一,因为它改变代码的行为。

-

How it works

+

工作原理(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) {
@@ -1550,7 +1554,7 @@ 

中文翻译

})(window)
-

Below is the result of the parser's "guessing" game.

+

下面是解析器"猜测"的结果。

(function(window, undefined) {
     function test(options) {
@@ -1558,90 +1562,78 @@ 

中文翻译

// 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 + return; // <- 插入分号, 改变了 return 表达式的行为 + { // 作为一个代码段处理 // a label and a single expression statement foo: function() {} - }; // <- inserted + }; // <- 插入分号 } - window.test = test; // <- inserted + window.test = test; // <- 插入分号 // The lines got merged again })(window)(function(window) { - window.someLibrary = {}; // <- inserted + window.someLibrary = {}; // <- 插入分号 -})(window); //<- inserted +})(window); //<- 插入分号
-

The parser drastically changed the behavior of the code above, in certain cases -it does the wrong thing.

+

解析器显著改变了上面代码的行为,在另外一些情况下也会做出错误的处理

-

Leading parenthesis

+

前置括号(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.

+

log 函数的执行结果极大可能不是函数;这种情况下就会出现 TypeError 的错误,详细错误信息可能是 undefined is not a function

-

In conclusion

+

结论(In conclusion)

-

It is highly recommended to never omit semicolons, it is also advocated to -keep braces on the same line with their corresponding statements and to never omit -them for one single-line if / else statements. Both of these measures will -not only improve the consistency of the code, they will also prevent the -JavaScript parser from changing its behavior.

其它

setTimeout and setInterval

Since JavaScript is asynchronous, it is possible to schedule the execution of a -function by using the setTimeout and setInterval functions.

+

建议绝对不要省略分号,同时也提倡将花括号和相应的表达式放在一行, +对于只有一行代码的 if 或者 else 表达式,也不应该省略花括号。 +这些良好的编程习惯不仅可以提到代码的一致性,而且可以防止解析器改变代码行为的错误处理。

其它

 setTimeoutsetInterval

由于 JavaScript 是异步的,可以使用 setTimeoutsetInterval 来计划执行函数。

function foo() {}
-var id = setTimeout(foo, 1000); // returns a Number > 0
+var id = setTimeout(foo, 1000); // 返回一个大于零的数字
 
-

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.

+

setTimeout 被调用时,它会返回一个 ID 标识并且计划在将来大约 1000 毫秒后调用 foo 函数。 +foo 函数只会被执行一次

-

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.

+

基于 JavaScript 引擎的计时策略,以及本质上的单线程运行方式,所以其它代码的运行可能会阻塞此线程。 +因此没法确保函数会在 setTimeout 指定的时刻被调用。

-

The function that was passed as the first parameter will get called by the -global object, that means, that this inside the called function -refers to that very object.

+

作为第一个参数的函数将会在全局作用域中执行,因此函数内的 this 将会指向这个全局对象。

function Foo() {
     this.value = 42;
     this.method = function() {
-        // this refers to the global object
-        console.log(this.value); // will log undefined
+        // this 指向全局对象
+        console.log(this.value); // 输出:undefined
     };
     setTimeout(this.method, 500);
 }
@@ -1649,134 +1641,120 @@ 

中文翻译

-

Stacking calls with setInterval

+

setInterval 的堆调用(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.

+

setTimeout 只会执行回调函数一次,不过 setInterval - 正如名字建议的 - 会每隔 X 毫秒执行函数一次。 +但是却不鼓励使用这个函数。

-

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.

+

当回调函数的执行被阻塞时,setInterval 仍然会发布更多的毁掉指令。在很小的定时间隔情况下,这会导致回调函数被堆积起来。

function foo(){
-    // something that blocks for 1 second
+    // 阻塞执行 1 秒
 }
 setInterval(foo, 100);
 
-

In the above code foo will get called once and will then block for one second.

+

上面代码中,foo 会执行一次随后被阻塞了一分钟。

-

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.

+

foo 被阻塞的时候,setInterval 仍然在组织将来对回调函数的调用。 +因此,当第一次 foo 函数调用结束时,已经有 10 次函数调用在等待执行。

-

Dealing with possible blocking code

+

处理可能的阻塞调用(Dealing with possible blocking code)

-

The easiest as well as most controllable solution, is to use setTimeout within -the function itself.

+

最简单也是最容易控制的方案,是在回调函数内部使用 setTimeout 函数。

function foo(){
-    // something that blocks for 1 second
+    // 阻塞执行 1 秒
     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.

+

这样不仅封装了 setTimeout 回调函数,而且阻止了调用指令的堆积,可以有更多的控制。 +foo 函数现在可以控制是否继续执行还是终止执行。

-

Manually clearing timeouts

+

手工清空定时器(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.

+

可以通过将定时时产生的 ID 标识传递给 clearTimeout 或者 clearInterval 函数来清除定时, +至于使用哪个函数取决于调用的时候使用的是 setTimeout 还是 setInterval

var id = setTimeout(foo, 1000);
 clearTimeout(id);
 
-

Clearing all timeouts

+

清除所有定时器(Clearing all timeouts)

-

As there is no built-in method for clearing all timeouts and/or intervals, -it is necessary to use brute force in order to achieve this functionality.

+

由于没有内置的清除所有定时器的方法,可以采用一种暴力的方式来达到这一目的。

-
// clear "all" timeouts
+
// 清空"所有"的定时器
 for(var i = 1; i < 1000; i++) {
     clearTimeout(i);
 }
 
-

There might still be timeouts that are unaffected by this arbitrary number; -therefore, is is instead recommended to keep track of all the timeout IDs, so -they can be cleared specifically.

+

可能还有些定时器不会在上面代码中被清除(译者注:如果定时器调用时返回的 ID 值大于 1000), +因此我们可以事先保存所有的定时器 ID,然后一把清除。

-

Hidden use of eval

+

隐藏使用 eval(Hidden use of eval

-

setTimeout and setInterval can also take a string as their first parameter. -This feature should never be used, since it internally makes use of eval.

+

setTimeoutsetInterval 也接受第一个参数为字符串的情况。 +这个特性绝对不要使用,因为它在内部使用了 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.

+

由于 eval 在这种情况下不是被直接调用,因此传递到 setTimeout 的字符串会自全局作用域中执行; +因此,上面的回调函数使用的不是定义在 bar 作用域中的局部变量 foo

-

It is further recommended to not use a string for passing arguments to the -function that will get called by either of the timeout functions.

+

建议不要在调用定时器函数时,为了向回调函数传递参数而使用字符串的形式。

function foo(a, b, c) {}
 
-// NEVER use this
+// 不要这样做
 setTimeout('foo(1,2, 3)', 1000)
 
-// Instead use an anonymous function
+// 可以使用匿名函数完成相同功能
 setTimeout(function() {
     foo(a, b, c);
 }, 1000)
 
-

In conclusion

+

结论(In conclusion)

+ +

绝对不要使用字符串作为 setTimeout 或者 setInterval 的第一个参数, +这么写的代码明显质量很差。当需要向回调函数传递参数时,可以创建一个匿名函数,在函数内执行真实的回调函数。

+ +

另外,应该避免使用 setInterval,因为它的定时执行不会被 JavaScript 阻塞。

\ No newline at end of file +template. +

\ No newline at end of file From e387a6a1f63063012071a23c486c477b8080bc46 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Thu, 24 Mar 2011 22:52:49 +0800 Subject: [PATCH 109/641] Cleaned up Chinese translation --- README.md | 49 ++--- doc/zh/array/constructor.md | 16 +- doc/zh/array/general.md | 26 ++- doc/zh/core/eval.md | 13 +- doc/zh/core/semicolon.md | 23 +-- doc/zh/core/undefined.md | 17 +- doc/zh/function/arguments.md | 21 +- doc/zh/function/closures.md | 17 +- doc/zh/function/constructors.md | 22 +- doc/zh/function/general.md | 9 +- doc/zh/function/scopes.md | 27 ++- doc/zh/function/this.md | 28 ++- doc/zh/index.json | 6 +- doc/zh/intro/authors.md | 7 +- doc/zh/intro/contributors.md | 4 +- doc/zh/intro/index.md | 2 +- doc/zh/intro/license.md | 7 +- doc/zh/object/forinloop.md | 10 +- doc/zh/object/general.md | 14 +- doc/zh/object/hasownproperty.md | 9 +- doc/zh/object/prototype.md | 31 ++- doc/zh/other/timeouts.md | 25 +-- doc/zh/types/casting.md | 19 +- doc/zh/types/equality.md | 16 +- doc/zh/types/instanceof.md | 12 +- doc/zh/types/typeof.md | 21 +- site/style/garden.css | 4 + site/zh/index.html | 347 ++++++++++++++++---------------- 28 files changed, 379 insertions(+), 423 deletions(-) diff --git a/README.md b/README.md index 8ab9841c..6371eb25 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,33 @@ -JavaScript 秘密花园 +JavaScript Garden ================= -**JavaScript 秘密花园**是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 -对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, -初学者可以籍此深入了解 JavaScript 的语言特性。 +**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 秘密花园**不是**用来教你 JavaScript。为了更好的理解这篇文章的内容, -你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习[向导][1]。 +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 -这篇文章的作者是两位 Stack Overflow 的用户, [Ivo Wetzel][6] (写作) 和 [Zhang Yi Jiang][5] (设计)。 +This guide is the work of two lovely Stack Overflow users, [Ivo Wetzel][6] +(Writing) and [Zhang Yi Jiang][5] (Design). +### Contributors -### 贡献者 + - [Caio Romão][8] (Spelling corrections) + - [Andreas Blixt][9] (Language corrections) - - [Caio Romão][8] (拼写检查) - - [Andreas Blixt][9] (语言修正) - -### 中文翻译 - - - [三生石上][29] - -此中文翻译由[三生石上][29]独立完成,[博客园][30]首发,转载请注明出处。 - - -### 许可 - -JavaScript 秘密花园在 [MIT license][2] 许可协议下发布,并存放在开源社区 [GitHub][4]。 -如果你发现错误或者打字错误,请 [file an issue][3] 或者 pull request。 -你也可以在 Stack Overflow 的聊天室 [JavaScript room][10] 找到我们。 +### 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 @@ -41,7 +38,3 @@ JavaScript 秘密花园在 [MIT license][2] 许可协议下发布,并存放在 [8]: https://github.com/caio [9]: https://github.com/blixt [10]: http://chat.stackoverflow.com/rooms/17/javascript - -[29]: http://sanshi.me/ -[30]: http://cnblogs.com/sanshi/ - diff --git a/doc/zh/array/constructor.md b/doc/zh/array/constructor.md index 93f2c891..225c9f80 100755 --- a/doc/zh/array/constructor.md +++ b/doc/zh/array/constructor.md @@ -1,4 +1,4 @@ -## `Array` 构造函数 +##`Array` 构造函数 由于 `Array` 的构造函数在如何处理参数时有点模棱两可,因此总是推荐使用数组的字面语法 - `[]` - 来创建数组。 @@ -8,18 +8,17 @@ [3]; // 结果: [3] new Array(3); // 结果: [] new Array('3') // 结果: ['3'] - -译者注:这里的模棱两可指的是数组的[两种构造函数语法][1] -var arr1 = new Array(arrayLength); -var arr2 = new Array(element0, element1, ..., elementN); // 译者注:因此下面的代码将会使人很迷惑 new Array(3, 4, 5); // 结果: [3, 4, 5] new Array(3) // 结果: [],此数组长度为 3 + +> **译者注:**这里的模棱两可指的是数组的[两种构造函数语法][1] 由于只有一个参数传递到构造函数中(译者注:指的是 `new Array(3);` 这种调用方式),并且这个参数是数字,构造函数会返回一个 `length` 属性被设置为此参数的空数组。 需要特别注意的是,此时只有 `length` 属性被设置,真正的数组并没有生成。 -译者注:在 Firebug 中,你会看到 [undefined, undefined, undefined],这其实是不对的。在上一节有详细的分析。 + +> **译者注:**在 Firebug 中,你会看到 `[undefined, undefined, undefined]`,这其实是不对的。在上一节有详细的分析。 var arr = new Array(3); arr[1]; // undefined @@ -28,9 +27,10 @@ var arr2 = new Array(element0, element1, ..., elementN); 这种优先于设置数组长度属性的做法只在少数几种情况下有用,比如需要循环字符串,可以避免 `for` 循环的麻烦。 new Array(count + 1).join(stringToRepeat); - // 译者注:new Array(3).join('#') 将会返回 "##" -### 结论(In conclusion) +> **译者注:** `new Array(3).join('#')` 将会返回 `##` + +###结论 应该尽量避免使用数组构造函数创建新数组。推荐使用数组的字面语法。它们更加短小和简洁,因此增加了代码的可读性。 diff --git a/doc/zh/array/general.md b/doc/zh/array/general.md index 07bc2228..e7140e8f 100755 --- a/doc/zh/array/general.md +++ b/doc/zh/array/general.md @@ -1,4 +1,4 @@ -## 数组遍历与属性 +##数组遍历与属性 虽然在 JavaScript 中数组是是对象,但是没有好的理由去使用 [`for in` 循环](#object.forinloop) 遍历数组。 相反,有一些好的理由**不去**使用 `for in` 遍历数组。 @@ -9,7 +9,7 @@ 由于 `for in` 循环会枚举原型链上的所有属性,唯一过滤这些属性的方式是使用 [`hasOwnProperty`](#object.hasownproperty) 函数, 因此会比普通的 `for` 循环慢上好多倍。 -### 遍历(Iteration) +###遍历 为了达到遍历数组的最佳性能,推荐使用经典的 `for` 循环。 @@ -25,8 +25,7 @@ 实际上,不使用缓存数组长度的方式比缓存版本要慢很多。 - -### `length` 属性(The `length` property) +###`length` 属性 `length` 属性的 *getter* 方式会简单的返回数组的长度,而 *setter* 方式会**截断**数组。 @@ -37,20 +36,19 @@ 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 +**译者注:** +在 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` 属性值不会对数组产生影响。 -### 结论(In conclusion) +###结论 为了更好的性能,推荐使用普通的 `for` 循环并缓存数组的 `length` 属性。 使用 `for in` 遍历数组被认为是不好的代码习惯并倾向于产生错误和导致性能问题。 diff --git a/doc/zh/core/eval.md b/doc/zh/core/eval.md index 03126ddb..577ba04f 100755 --- a/doc/zh/core/eval.md +++ b/doc/zh/core/eval.md @@ -1,4 +1,4 @@ -## 为什么不要使用 `eval` +##为什么不要使用 `eval` `eval` 函数会在当前作用域中执行一段 JavaScript 代码字符串。 @@ -23,7 +23,7 @@ test(); // 2 foo; // 3 -[译者注][30]:上面的代码等价于在全局作用域中调用 `eval`,和下面两种写法效果一样: +**[译者注][30]:**上面的代码等价于在全局作用域中调用 `eval`,和下面两种写法效果一样: // 写法一:直接调用全局作用域下的 foo 变量 var foo = 1; @@ -35,7 +35,7 @@ test(); // 2 foo; // 3 - // 写法二:使用 call 函数修改 `eval` 执行的上下文为全局作用域 + // 写法二:使用 call 函数修改 eval 执行的上下文为全局作用域 var foo = 1; function test() { var foo = 2; @@ -47,22 +47,21 @@ 在**任何情况下**我们都应该避免使用 `eval` 函数。99.9% 使用 `eval` 的场景都有**不使用** `eval` 的解决方案。 -### 伪装的 `eval`(`eval` in disguise) +###伪装的 `eval` [定时函数](#other.timeouts) `setTimeout` 和 `setInterval` 都可以接受字符串作为它们的第一个参数。 这个字符串**总是**在全局作用域中执行,因此 `eval` 在这种情况下没有被直接调用。 -### 安全问题(Security issues) +###安全问题 `eval` 也存在安全问题,因为它会执行**任意**传给它的代码, 在代码字符串未知或者是来自一个不信任的源时,绝对不要使用 `eval` 函数。 -### 结论(In conclusion) +###结论 绝对不要使用 `eval`,任何使用它的代码都会在它的工作方式,性能和安全性方面受到质疑。 如果一些情况必须使用到 `eval` 才能正常工作,首先它的设计会受到质疑,这**不应该**是首选的解决方案, 一个更好的不使用 `eval` 的解决方案应该得到充分考虑并优先采用。 - [30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/core/semicolon.md b/doc/zh/core/semicolon.md index 1df424c5..f4e52041 100755 --- a/doc/zh/core/semicolon.md +++ b/doc/zh/core/semicolon.md @@ -1,4 +1,4 @@ -## 自动分号插入 +##自动分号插入 尽管 JavaScript 有 C 的代码风格,但是它**不**强制要求在代码中使用分号,实际上可以省略它们。 @@ -17,12 +17,10 @@ JavaScript 不是一个没有分号的语言,恰恰相反上它需要分号来 自动的分号插入被认为是 JavaScript 语言**最大**的设计缺陷之一,因为它*能*改变代码的行为。 - -### 工作原理(How it works) +### 工作原理 下面的代码没有分号,因此解析器需要自己判断需要在哪些地方插入分号。 - (function(window, undefined) { function test(options) { log('testing!') @@ -47,7 +45,6 @@ JavaScript 不是一个没有分号的语言,恰恰相反上它需要分号来 (function(window) { window.someLibrary = {} - })(window) 下面是解析器"猜测"的结果。 @@ -55,7 +52,7 @@ JavaScript 不是一个没有分号的语言,恰恰相反上它需要分号来 (function(window, undefined) { function test(options) { - // Not inserted, lines got merged + // 没有插入分号,两行被合并为一行 log('testing!')(options.list || []).forEach(function(i) { }); // <- 插入分号 @@ -67,27 +64,22 @@ JavaScript 不是一个没有分号的语言,恰恰相反上它需要分号来 return; // <- 插入分号, 改变了 return 表达式的行为 { // 作为一个代码段处理 - - // a label and a single expression statement foo: function() {} }; // <- 插入分号 } window.test = test; // <- 插入分号 - // The lines got merged again + // 两行又被合并了 })(window)(function(window) { window.someLibrary = {}; // <- 插入分号 - })(window); //<- 插入分号 -> **注意:** JavaScript 不能正确的处理 return 表达式紧跟换行符的情况, +> **注意:** JavaScript 不能正确的处理 `return` 表达式紧跟换行符的情况, > 虽然这不能算是自动分号插入的错误,但这确实是一种不希望的副作用。 - 解析器显著改变了上面代码的行为,在另外一些情况下也会做出**错误的处理**。 - -### 前置括号(Leading parenthesis) +###前置括号 在前置括号的情况下,解析器**不会**自动插入分号。 @@ -100,8 +92,7 @@ JavaScript 不是一个没有分号的语言,恰恰相反上它需要分号来 `log` 函数的执行结果**极大**可能**不是**函数;这种情况下就会出现 `TypeError` 的错误,详细错误信息可能是 `undefined is not a function`。 - -### 结论(In conclusion) +###结论 建议**绝对**不要省略分号,同时也提倡将花括号和相应的表达式放在一行, 对于只有一行代码的 `if` 或者 `else` 表达式,也不应该省略花括号。 diff --git a/doc/zh/core/undefined.md b/doc/zh/core/undefined.md index ba79e052..9b779b6b 100755 --- a/doc/zh/core/undefined.md +++ b/doc/zh/core/undefined.md @@ -1,9 +1,8 @@ -## `undefined` 和 `null` +##`undefined` 和 `null` -JavaScript 有两个表示 `空` 的值,其中比较有用的是 `undefined`。 +JavaScript 有两个表示‘空’的值,其中比较有用的是 `undefined`。 - -### `undefined`的值(The value `undefined`) +###`undefined` 的值 `undefined` 是一个值为 `undefined` 的类型。 @@ -22,8 +21,7 @@ JavaScript 有两个表示 `空` 的值,其中比较有用的是 `undefined` - 函数参数没有被显式的传递值。 - 任何被设置为 `undefined` 值的变量。 - -### 处理 `undefined` 值的改变(Handling changes to the value of `undefined`) +###处理 `undefined` 值的改变 由于全局变量 `undefined` 只是保存了 `undefined` 类型实际*值*的副本, 因此对它赋新值**不会**改变类型 `undefined` 的值。 @@ -50,11 +48,10 @@ JavaScript 有两个表示 `空` 的值,其中比较有用的是 `undefined` 这里唯一的区别是,在压缩后并且函数内没有其它需要使用 `var` 声明变量的情况下,这个版本的代码会多出 4 个字节的代码。 -[译者注][30]:这里有点绕口,其实很简单。 -如果此函数内没有其它需要声明的变量,那么 `var ` 总共 4 个字符(包含一个空白字符)就是专门为 undefined 变量准备的,相比上个例子多出了 4 个字节。 - +> **[译者注][30]:**这里有点绕口,其实很简单。如果此函数内没有其它需要声明的变量,那么 `var` 总共 4 个字符(包含一个空白字符) +就是专门为 `undefined` 变量准备的,相比上个例子多出了 4 个字节。 -### 使用 `null`(Uses of `null`) +###`null` 的用处 JavaScript 中的 `undefined` 的使用场景类似于其它语言中的 *null*,实际上 JavaScript 中的 `null` 是另外一种数据类型。 diff --git a/doc/zh/function/arguments.md b/doc/zh/function/arguments.md index 8921e2d5..b86e72f6 100755 --- a/doc/zh/function/arguments.md +++ b/doc/zh/function/arguments.md @@ -1,4 +1,4 @@ -## `arguments` 对象 +##`arguments` 对象 JavaScript 中每个函数内都能访问一个特别变量 `arguments`。这个变量维护着所有传递到这个函数中的参数列表。 @@ -12,7 +12,7 @@ JavaScript 中每个函数内都能访问一个特别变量 `arguments`。这个 因此,无法对 `arguments` 变量使用标准的数组方法,比如 `push`, `pop` 或者 `slice`。 虽然使用 `for` 循环遍历也是可以的,但是为了更好的使用数组方法,最好把它转化为一个真正的数组。 -### 转化为数组(Converting to an array) +###转化为数组 下面的代码将会创建一个新的数组,包含所有 `arguments` 对象中的元素。 @@ -20,7 +20,7 @@ JavaScript 中每个函数内都能访问一个特别变量 `arguments`。这个 这个转化比较**慢**,在性能不好的代码中**不推荐**这种做法。 -### 传递参数(Passing arguments) +###传递参数 下面将参数从一个函数传递到另一个函数,是推荐的做法。 @@ -48,7 +48,7 @@ JavaScript 中每个函数内都能访问一个特别变量 `arguments`。这个 }; -[译者注][30]:上面的 `Foo.method` 函数和下面代码的效果是一样的: +**[译者注][30]**:上面的 `Foo.method` 函数和下面代码的效果是一样的: Foo.method = function() { var args = Array.prototype.slice.call(arguments); @@ -56,7 +56,7 @@ JavaScript 中每个函数内都能访问一个特别变量 `arguments`。这个 }; -### 自动更新(Modification "magic") +###自动更新 `arguments` 对象为其内部属性以及函数形式参数创建 *getter* 和 *setter* 方法。 @@ -75,7 +75,7 @@ JavaScript 中每个函数内都能访问一个特别变量 `arguments`。这个 } foo(1, 2, 3); -### 性能真相(Performance myths and truths) +### 性能真相 `arguments` 对象总会被创建,除了两个特殊情况 - 作为局部变量声明和作为形式参数。 而不管它是否有被使用。 @@ -85,12 +85,10 @@ JavaScript 中每个函数内都能访问一个特别变量 `arguments`。这个 > **ES5 提示:** 这些 *getters* 和 *setters* 在严格模式下(strict mode)不会被创建。 - -[译者注][30]:在 [MDC][2] 中对 `strict mode` 模式下 `arguments` 的描述有助于我们的理解,请看下面代码: +**[译者注][30]:**在 [MDC][2] 中对 `strict mode` 模式下 `arguments` 的描述有助于我们的理解,请看下面代码: // 阐述在 ES5 的严格模式下 `arguments` 的特性 - function f(a) - { + function f(a) { "use strict"; a = 42; return [a, arguments[0]]; @@ -112,13 +110,12 @@ JavaScript 中每个函数内都能访问一个特别变量 `arguments`。这个 } } -上面代码中,`foo` 不再是一个单纯的内联函数 [inlining][1]([译者注][30]:这里指的是解析器可以做内联处理), +上面代码中,`foo` 不再是一个单纯的内联函数 [inlining][1](**[译者注][30]**:这里指的是解析器可以做内联处理), 因为它需要知道它自己和它的调用者。 这不仅抵消了内联函数带来的性能提升,而且破坏了封装,因此现在函数可能要依赖于特定的上下文。 因此**强烈**建议大家**不要**使用 `arguments.callee` 和它的属性。 - > **ES5 提示:** 在严格模式下,`arguments.callee` 会报错 `TypeError`,因为它已经被废除了。 [1]: http://en.wikipedia.org/wiki/Inlining diff --git a/doc/zh/function/closures.md b/doc/zh/function/closures.md index 76a841fc..f3db29d1 100755 --- a/doc/zh/function/closures.md +++ b/doc/zh/function/closures.md @@ -1,9 +1,9 @@ -## 闭包和引用 +##闭包和引用 闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域**总是**能够访问外部作用域中的变量。 因为 [函数](#scopes) 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。 -### 模拟私有变量(Emulating private variables) +###模拟私有变量 function Counter(start) { var count = start; @@ -25,7 +25,7 @@ 这里,`Counter` 函数返回两个闭包,函数 `increment` 和函数 `get`。 这两个函数都维持着 对外部作用域 `Counter` 的引用,因此总可以访问此作用域内定义的变量 `count`. -### 为什么不可以在外部访问私有变量(Why private variables work) +###为什么不可以在外部访问私有变量 因为 JavaScript 中不可以对作用域进行引用或赋值,因此没有办法在外部访问 `count` 变量。 唯一的途径就是通过那两个闭包。 @@ -38,10 +38,9 @@ 上面的代码**不会**改变定义在 `Counter` 作用域中的 `count` 变量的值,因为 `foo.hack` 没有 定义在那个**作用域**内。它将会创建或者覆盖*全局*变量 `count`。 -### 循环中的闭包(Closures inside loops) +###循环中的闭包 -一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号(as if they were -copying the value of the loops index variable.)。 +一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号 for(var i = 0; i < 10; i++) { setTimeout(function() { @@ -51,13 +50,13 @@ copying the value of the loops index variable.)。 上面的代码不会输出数字 `0` 到 `9`,而是会输出数字 `10` 十次。 -当 `console.log` 被调用的时候,*匿名*函数保持对外部变量 `i` 的引用,此时 `for 循环`已经结束, `i` 的值被修改成了 `10`. +当 `console.log` 被调用的时候,*匿名*函数保持对外部变量 `i` 的引用,此时 `for`循环已经结束, `i` 的值被修改成了 `10`. 为了得到想要的结果,需要在每次循环中创建变量 `i` 的**拷贝**。 -### 避免引用错误(Avoiding the reference problem) +###避免引用错误 -为了正确的获得循环序号,最好使用 [匿名包裹器](#scopes)([译者注][30]:其实就是我们通常说的自执行匿名函数)。 +为了正确的获得循环序号,最好使用 [匿名包裹器](#scopes)(**[译者注][30]:**其实就是我们通常说的自执行匿名函数)。 for(var i = 0; i < 10; i++) { (function(e) { diff --git a/doc/zh/function/constructors.md b/doc/zh/function/constructors.md index d4de4203..137dc38c 100755 --- a/doc/zh/function/constructors.md +++ b/doc/zh/function/constructors.md @@ -1,4 +1,4 @@ -## 构造函数 +##构造函数 JavaScript 中的构造函数和其它语言中的构造函数是不同的。 通过 `new` 关键字方式调用的函数都被认为是构造函数。 @@ -37,8 +37,8 @@ JavaScript 中的构造函数和其它语言中的构造函数是不同的。 new Test(); // 返回的对象 -[译者注][30]:new Bar() 返回的是新创建的对象,而不是数字的字面值 2。 -因此 new Bar().constructor === Bar,但是如果返回的是数字对象,结果就不同了,如下所示 +**[译者注][30]:**`new Bar()` 返回的是新创建的对象,而不是数字的字面值 2。 +因此 `new Bar().constructor === Bar`,但是如果返回的是数字对象,结果就不同了,如下所示 function Bar() { return new Number(2); @@ -46,7 +46,8 @@ JavaScript 中的构造函数和其它语言中的构造函数是不同的。 new Bar().constructor === Number -[译者注][30]:这里得到的(new Test())是函数返回的对象,而不是通过 new 关键字新创建的对象,因此: +**[译者注][30]:**这里得到的 `new Test()`是函数返回的对象,而不是通过`new`关键字新创建的对象,因此: + (new Test()).value === undefined (new Test()).foo === 1 @@ -61,7 +62,7 @@ JavaScript 中的构造函数和其它语言中的构造函数是不同的。 虽然上例在有些情况下也能正常运行,但是由于 JavaScript 中 [`this`](#function.this) 的工作原理, 这里的 `this` 指向*全局对象*。 -### 工厂模式(Factories) +### 工厂模式 为了不使用 `new` 关键字,构造函数必须显式的返回一个值。 @@ -83,13 +84,12 @@ JavaScript 中的构造函数和其它语言中的构造函数是不同的。 上面两种对 `Bar` 函数的调用返回的值完全相同,一个新创建的拥有 `method` 属性的对象被返回, 其实这里创建了一个[闭包](#function.closures)。 - -还需要注意,`new Bar()` 并**不会**改变返回对象的原型([译者注][30]:也就是返回对象的原型不会指向 Bar.prototype)。 +还需要注意, `new Bar()` 并**不会**改变返回对象的原型(**[译者注][30]:**也就是返回对象的原型不会指向 `Bar.prototype`)。 因为构造函数的原型会被指向到刚刚创建的新对象,而这里的 `Bar` 没有把这个新对象返回([译者注][30]:而是返回了一个包含 `method` 属性的自定义对象)。 在上面的例子中,使用或者不使用 `new` 关键字没有功能性的区别。 -[译者注][30]:上面两种方式创建的对象不能访问 Bar 原型链上的属性,如下所示: +**[译者注][30]:**上面两种方式创建的对象不能访问 `Bar` 原型链上的属性,如下所示: var bar1 = new Bar(); typeof(bar1.method); // "function" @@ -99,8 +99,7 @@ JavaScript 中的构造函数和其它语言中的构造函数是不同的。 typeof(bar2.method); // "function" typeof(bar2.foo); // "undefined" - -### 通过工厂模式创建新对象(Creating new objects via factories) +###通过工厂模式创建新对象 我们常听到的一条忠告是**不要**使用 `new` 关键字来调用函数,因为如果忘记使用它就会导致错误。 @@ -129,10 +128,9 @@ JavaScript 中的构造函数和其它语言中的构造函数是不同的。 2. 为了实现继承,工厂方法需要从另外一个对象拷贝所有属性,或者把一个对象作为新创建对象的原型。 3. 放弃原型链仅仅是因为防止遗漏 `new` 带来的问题,这似乎和语言本身的思想相违背。 -### 总结(In conclusion) +###总结 虽然遗漏 `new` 关键字可能会导致问题,但这并**不是**放弃使用原型链的借口。 最终使用哪种方式取决于应用程序的需求,选择一种代码书写风格并**坚持**下去才是最重要的。 - [30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/function/general.md b/doc/zh/function/general.md index c009acec..6e759c89 100755 --- a/doc/zh/function/general.md +++ b/doc/zh/function/general.md @@ -1,9 +1,9 @@ -## 函数声明与表达式 +##函数声明与表达式 函数是JavaScript中的一等对象,这意味着可以把函数像其它值一样传递。 一个常见的用法是把*匿名函数*作为回调函数传递对异步函数中。 -### 函数声明(The `function` declaration) +###函数声明 function foo() {} @@ -13,7 +13,7 @@ foo(); // 正常运行,因为foo在代码运行前已经被创建 function foo() {} -### 函数赋值表达式(The `function` expression) +### 函数赋值表达式 var foo = function() {}; @@ -27,7 +27,7 @@ 但是由于赋值语句只在运行时执行,因此在相应代码执行之前, `foo` 的值缺省为 [undefined](#undefined)。 -### 命名函数的赋值表达式(Named function expression) +###命名函数的赋值表达式 另外一个特殊的情况是将命名函数赋值给一个变量。 @@ -40,5 +40,4 @@ 然而在 `bar` 内部依然可见。这是由于 JavaScript 的 [命名处理](#scopes) 所致, 函数名在函数内*总是*可见的。 - [30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/function/scopes.md b/doc/zh/function/scopes.md index c48a9cfe..b644b677 100755 --- a/doc/zh/function/scopes.md +++ b/doc/zh/function/scopes.md @@ -1,4 +1,4 @@ -## 作用域与命名空间 +##作用域与命名空间 尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; 而仅仅支持 *函数作用域*。 @@ -13,7 +13,7 @@ > **注意:** 如果不是在赋值语句中,而是在 return 表达式或者函数参数中,`{...}` 将会作为代码段解析, > 而不是作为对象的字面语法解析。如果考虑到 [自动分号插入](#semicolon),这可能会导致一些不易察觉的错误。 -[译者注][30]:如果 return 对象的左括号和 return 不在一行上就会出错。 +**[译者注][30]:**如果 `return` 对象的左括号和 `return` 不在一行上就会出错。 // 译者注:下面输出 undefined function add(a, b) { @@ -27,12 +27,12 @@ JavaScript 中没有显式的命名空间定义,这就意味着所有对象都 每次引用一个变量,JavaScript 会向上遍历整个作用域直到找到这个变量为止。 如果到达全局作用域但是这个变量仍未找到,则会抛出 `ReferenceError` 异常。 -### 隐式的全局变量(The bane of global variables) +###隐式的全局变量 - // script A + // 脚本 A foo = '42'; - // script B + // 脚本 B var foo = '42' 上面两段脚本效果**不同**。脚本 A 在*全局*作用域内定义了变量 `foo`,而脚本 B 在*当前*作用域内定义变量 `foo`。 @@ -68,7 +68,7 @@ JavaScript 中没有显式的命名空间定义,这就意味着所有对象都 在第二个 `for` 循环中使用 `var` 声明变量可以避免这种错误。 声明变量时**绝对不要**遗漏 `var` 关键字,除非这就是*期望*的影响外部作用域的行为。 -### 局部变量(Local variables) +###局部变量 JavaScript 中局部变量只可能通过两种方式声明,一个是作为[函数](#functions)参数,另一个是通过 `var` 关键字声明。 @@ -136,7 +136,7 @@ JavaScript 会**提升**变量声明。这意味着 `var` 表达式和 `function 没有块级作用域不仅导致 `var` 表达式被从循环内移到外部,而且使一些 `if` 表达式更难看懂。 -在原来代码中,`if` 表达式看起来修改了*全部变量* `goo`,实际上在提升规则(hoisting)被应用后,却是在修改*局部变量*。 +在原来代码中,`if` 表达式看起来修改了*全部变量* `goo`,实际上在提升规则被应用后,却是在修改*局部变量*。 如果没有提升规则(hoisting)的知识,下面的代码看起来会抛出异常 `ReferenceError`。 @@ -157,7 +157,7 @@ JavaScript 会**提升**变量声明。这意味着 `var` 表达式和 `function } -[译者注][30]:在 Nettuts+ 网站有一篇介绍 hoisting 的[文章][1],其中的代码很有启发性。 +**[译者注][30]:**在 Nettuts+ 网站有一篇介绍 hoisting 的[文章][1],其中的代码很有启发性。 // 译者注:来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则 var myvar = 'my value'; @@ -168,7 +168,7 @@ JavaScript 会**提升**变量声明。这意味着 `var` 表达式和 `function })(); -### 名称解析顺序(Name resolution order) +###名称解析顺序 JavaScript 中的所有作用域,包括*全局作用域*,都有一个特别的名称 [`this`](#this) 指向当前对象。 @@ -183,7 +183,7 @@ JavaScript 中的所有作用域,包括*全局作用域*,都有一个特别 > **注意:** 自定义 `arguments` 参数将会阻止原生的 `arguments` 对象的创建。 -### 命名空间(Namespaces) +###命名空间 只有一个全局作用域导致的常见错误是命名冲突。在 JavaScript中,这可以通过 *匿名包装器* 轻松解决。 @@ -196,7 +196,7 @@ JavaScript 中的所有作用域,包括*全局作用域*,都有一个特别 })(); // 立即执行此匿名函数 -匿名函数被认为是 [表达式](#functions);因此为了可调用性,它们首先会被执行(evaluated)。 +匿名函数被认为是 [表达式](#functions);因此为了可调用性,它们首先会被执行。 ( // 小括号内的函数首先被执行 function() {} @@ -209,13 +209,12 @@ JavaScript 中的所有作用域,包括*全局作用域*,都有一个特别 +function(){}(); (function(){}()); -### 结论(In conclusion) +###结论 -推荐使用*匿名包装器*([译者注][30]:也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, +推荐使用*匿名包装器*(**[译者注][30]:**也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, 而且有利于程序的模块化。 另外,使用全局变量被认为是**不好的习惯**。这样的代码倾向于产生错误和带来高的维护成本。 - [1]: http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/ [30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/function/this.md b/doc/zh/function/this.md index 4b9550f8..494ed98c 100755 --- a/doc/zh/function/this.md +++ b/doc/zh/function/this.md @@ -1,18 +1,17 @@ -## this 的工作原理 +##`this` 的工作原理 JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 在**五**种不同的情况下 ,`this` 指向的各不相同。 -### 全局范围内(The global scope) +###全局范围内 this; 当在全部范围内使用 `this`,它将会指向*全局*对象。 -[译者注][30]:浏览器中运行的JavaScript脚本,这个全局对象是 window。 +> **[译者注][30]:**浏览器中运行的 JavaScript 脚本,这个全局对象是 `window`。 - -### 函数调用(Calling a function) +###函数调用 foo(); @@ -22,20 +21,20 @@ JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 > 这种情况下 `this` 将会是 `undefined`。 > [译者注][30]:ES5指的是ECMAScript 5,是 2009-12 发布的最新的 JavaScript 版本。 -### 方法调用(Calling a method) +###方法调用 test.foo(); 这个例子中,`this` 指向 `test` 对象。 -### 调用构造函数(Calling a constructor) +###调用构造函数 new foo(); 如果函数倾向于和 `new` 关键词一块使用,则我们称这个函数是 [构造函数](#constructors)。 在函数内部,`this` 指向*新创建*的对象。 -### 显式的设置 `this`(Explicit setting of `this`) +###显式的设置 `this` function foo(a, b, c) {} @@ -50,16 +49,16 @@ JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 > **注意:** 在对象的字面声明语法中,`this` **不能**用来指向对象本身。 > 因此 `var obj = {me: this}` 中的 `me` 不会指向 `obj`,因为 `this` 只可能出现在上述的五种情况中。 -> [译者注][30]:这个例子中,如果是在浏览器中运行,obj.me等于window对象。 +> **[译者注][30]:**这个例子中,如果是在浏览器中运行,`obj.me` 等于 `window` 对象。 -### 常见误解(Common pitfalls) +###常见误解 -尽管大部分的情况都说的过去,不过第一个规则([译者注][30]:这里指的应该是第二个规则,也就是直接调用函数时,`this` 指向全局对象) +尽管大部分的情况都说的过去,不过第一个规则(**[译者注][30]:**这里指的应该是第二个规则,也就是直接调用函数时,`this` 指向全局对象) 被认为是JavaScript语言另一个错误设计的地方,因为它**从来**就没有实际的用途。 Foo.method = function() { function test() { - // this 将会被设置为全局对象([译者注][30]:浏览器环境中也就是 window 对象) + // this 将会被设置为全局对象(译者注:浏览器环境中也就是 window 对象) } test(); } @@ -77,9 +76,9 @@ JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 } `that` 只是我们随意起的名字,不过这个名字被广泛的用来指向外部的 `this` 对象。 -在 [closures](#closures) 一节,我们可以看到 `that` 可以作为参数传递。 +在 [闭包](#closures) 一节,我们可以看到 `that` 可以作为参数传递。 -### 方法的赋值表达式(Assigning methods) +###方法的赋值表达式 另一个看起来奇怪的地方是函数别名,也就是将一个方法**赋值**给一个变量。 @@ -100,5 +99,4 @@ JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 当 `method` 被调用时,`this` 将会指向 `Bar` 的实例对象。 - [30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/index.json b/doc/zh/index.json index 7ad3654c..be3d5528 100755 --- a/doc/zh/index.json +++ b/doc/zh/index.json @@ -1,7 +1,7 @@ { - "title": "JavaScript Garden", - "langTitle": "JavaScript Garden in English", - "description": "A Guide to JavaScript's Quirks and Flaws.", + "title": "JavaScript 秘密花园", + "langTitle": "JavaScript Garden 中文翻译", + "description": "JavaScript 语言最古怪用法的文档集合", "sections": [ { "title": "简介", diff --git a/doc/zh/intro/authors.md b/doc/zh/intro/authors.md index 022a8259..427310b3 100755 --- a/doc/zh/intro/authors.md +++ b/doc/zh/intro/authors.md @@ -1,6 +1,7 @@ ## 关于作者 -这篇文章的作者是两位 Stack Overflow 的用户, [Ivo Wetzel][1] (写作) 和 [Zhang Yi Jiang][2] (设计)。 +这篇文章的作者是两位 [Stack Overflow][1] 用户, [伊沃·韦特泽尔 Ivo Wetzel][2](写作) 和 [张易江 Zhang Yi Jiang][3](设计)。 -[1]: http://stackoverflow.com/users/170224/ivo-wetzel -[2]: http://stackoverflow.com/users/313758/yi-jiang +[1]: http://stackoverflow.com/ +[2]: http://stackoverflow.com/users/170224/ivo-wetzel +[3]: http://stackoverflow.com/users/313758/yi-jiang diff --git a/doc/zh/intro/contributors.md b/doc/zh/intro/contributors.md index 9f458d05..339b1c57 100755 --- a/doc/zh/intro/contributors.md +++ b/doc/zh/intro/contributors.md @@ -7,10 +7,8 @@ - [三生石上][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/doc/zh/intro/index.md b/doc/zh/intro/index.md index 4503e0a7..497110ce 100755 --- a/doc/zh/intro/index.md +++ b/doc/zh/intro/index.md @@ -1,6 +1,6 @@ ## 简介 -**JavaScript 秘密花园**是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 +**JavaScript 秘密花园**是一个不断更新,主要关心 JavaScript 一些古怪用法的文档。 对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, 初学者可以籍此深入了解 JavaScript 的语言特性。 diff --git a/doc/zh/intro/license.md b/doc/zh/intro/license.md index c74e07dd..917a09c6 100755 --- a/doc/zh/intro/license.md +++ b/doc/zh/intro/license.md @@ -1,9 +1,8 @@ ## 许可 -JavaScript 秘密花园在 [MIT license][1] 许可协议下发布,并存放在开源社区 [GitHub][2]。 -如果你发现错误或者打字错误,请 [file an issue][3] 或者 pull request。 -你也可以在 Stack Overflow 的聊天室 [JavaScript room][4] 找到我们。 - +JavaScript 秘密花园在 [MIT license][1] 许可协议下发布,并存放在 [GitHub][2] 开源社区。 +如果你发现错误或者打字错误,请[新建一个任务单][3]或者发一个抓取请求。 +你也可以在 Stack Overflow 的 [JavaScript 聊天室][4] 找到我们。 [1]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE [2]: https://github.com/BonsaiDen/JavaScript-Garden diff --git a/doc/zh/object/forinloop.md b/doc/zh/object/forinloop.md index 6f03cd9b..ea7d389d 100755 --- a/doc/zh/object/forinloop.md +++ b/doc/zh/object/forinloop.md @@ -1,9 +1,9 @@ -## `for in` 循环 +##`for in` 循环 和 `in` 操作符一样,`for in` 循环同样在查找对象属性时遍历原型链上的所有属性。 > **注意:** `for in` 循环**不会**遍历那些 `enumerable` 设置为 `false` 的属性;比如数组的 `length` 属性。 - + // 修改 Object.prototype Object.prototype.bar = 1; @@ -17,7 +17,7 @@ > **注意:** 由于 `for in` 总是要遍历整个原型链,因此如果一个对象的继承层次太深的话会影响性能。 -### 使用 `hasOwnProperty` 过滤(Using `hasOwnProperty` for filtering) +###使用 `hasOwnProperty` 过滤 // foo 变量是上例中的 for(var i in foo) { @@ -32,12 +32,10 @@ 一个广泛使用的类库 [Prototype][1] 就扩展了原生的 JavaScript 对象。 因此,但这个类库被包含在页面中时,不使用 `hasOwnProperty` 过滤的 `for in` 循环难免会出问题。 - -### 最佳实践(Best practices) +###总结 推荐**总是**使用 `hasOwnProperty`。不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。 - [1]: http://www.prototypejs.org/ [30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/object/general.md b/doc/zh/object/general.md index 7f826a42..f57ff1f1 100755 --- a/doc/zh/object/general.md +++ b/doc/zh/object/general.md @@ -1,4 +1,4 @@ -## 对象使用和属性 +##对象使用和属性 JavaScript 中所有变量都是对象,除了两个例外 [`null`](#undefined) 和 [`undefined`](#undefined)。 @@ -20,7 +20,7 @@ JavaScript 中所有变量都是对象,除了两个例外 [`null`](#undefined) 2 .toString(); // 注意点号前面的空格 (2).toString(); // 2先被计算 -### 对象作为数据类型(Objects as a data type) +###对象作为数据类型 JavaScript 的对象可以作为[*哈希表*][1]使用,主要用来保存命名的键与值的对应关系。 @@ -32,7 +32,7 @@ JavaScript 的对象可以作为[*哈希表*][1]使用,主要用来保存命 // 一个新对象,拥有一个值为12的自定义属性'test' var bar = {test: 12}; -### 访问属性(Accessing properties) +### 访问属性 有两种方式来访问对象的属性,点操作符或者中括号操作符。 @@ -48,11 +48,11 @@ JavaScript 的对象可以作为[*哈希表*][1]使用,主要用来保存命 两种语法是等价的,但是中括号操作符在下面两种情况下依然有效 - 动态设置属性 - - 属性名不是一个有效的变量名([译者注][30]:比如属性名中包含空格,或者属性名是JS的关键词) + - 属性名不是一个有效的变量名(**[译者注][30]:**比如属性名中包含空格,或者属性名是 JS 的关键词) -[译者注][30]:在 [JSLint][2] 语法检测工具中,点操作符是推荐做法。 +> **[译者注][30]:**在 [JSLint][2] 语法检测工具中,点操作符是推荐做法。 -### 删除属性(Deleting properties) +###删除属性 删除属性的唯一方法是使用 `delete` 操作符;设置属性为 `undefined` 或者 `null` 并不能真正的删除属性, 而**仅仅**是移除了属性和值的关联。 @@ -74,7 +74,7 @@ JavaScript 的对象可以作为[*哈希表*][1]使用,主要用来保存命 上面的输出结果有 `bar undefined` 和 `foo null` - 只有 `baz` 被真正的删除了,所以从输出结果中消失。 -### 属性名的语法(Notation of keys) +###属性名的语法 var test = { 'case': 'I am a keyword so I must be notated as a string', diff --git a/doc/zh/object/hasownproperty.md b/doc/zh/object/hasownproperty.md index b918e45d..8abefd14 100755 --- a/doc/zh/object/hasownproperty.md +++ b/doc/zh/object/hasownproperty.md @@ -1,13 +1,12 @@ -## `hasOwnProperty` 函数 +##`hasOwnProperty` 函数 为了判断一个对象是否包含*自定义*属性而*不是*[原型链](#prototype)上的属性, 我们需要使用继承自 `Object.prototype` 的 `hasOwnProperty` 方法。 - > **注意:** 通过判断一个属性是否 `undefined` 是**不够**的。 > 因为一个属性可能确实存在,只不过它的值被设置为 `undefined`。 -`hasOwnProperty` 是JavaScript中唯一一个处理属性但是**不需要**查找原型链的方法。 +`hasOwnProperty` 是 JavaScript 中唯一一个处理属性但是**不**查找原型链的函数。 // 修改Object.prototype Object.prototype.bar = 1; @@ -22,7 +21,7 @@ 只有 `hasOwnProperty` 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 **没有**其它方法可以用来排除原型链上的属性,而不是定义在对象*自身*上的属性。 -### `hasOwnProperty` 作为属性(`hasOwnProperty` as a property) +###`hasOwnProperty` 作为属性 JavaScript **不会**保护 `hasOwnProperty` 被非法占用,因此如果一个对象碰巧存在这个属性, 就需要使用*外部*的 `hasOwnProperty` 函数来获取正确的结果。 @@ -39,7 +38,7 @@ JavaScript **不会**保护 `hasOwnProperty` 被非法占用,因此如果一 // 使用其它对象的 hasOwnProperty,并将其上下为设置为foo {}.hasOwnProperty.call(foo, 'bar'); // true -### 结论(In conclusion) +###结论 当检查对象上某个属性是否存在时,`hasOwnProperty` 是**唯一**可用的方法。 同时在使用 [`for in` loop](#forinloop) 遍历对象时,推荐**总是**使用 `hasOwnProperty` 方法, diff --git a/doc/zh/object/prototype.md b/doc/zh/object/prototype.md index 00d8271f..5a0484a2 100755 --- a/doc/zh/object/prototype.md +++ b/doc/zh/object/prototype.md @@ -1,16 +1,16 @@ -## 原型 +##原型 -JavaScript 不包含传统的类继承模型,而是使用 *prototypical* 原型模型。 +JavaScript 不包含传统的类继承模型,而是使用 *prototypal* 原型模型。 虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。 实现传统的类继承模型是很简单,但是实现 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.) +(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` 将会导致两个对象共享**相同**的原型。 > 因此,改变任意一个对象的原型都会影响到另一个对象的原型,在大多数情况下这不是希望的结果。 @@ -44,24 +44,24 @@ other way around is a far more difficult task.) 上面的例子中,`test` 对象从 `Bar.prototype` 和 `Foo.prototype` 继承下来;因此, 它能否访问 `Foo` 的原型方法 `method`。但是它不能访问 `Foo` 的实例属性 `value`, 因为这个属性在`Foo`的[构造函数](#constructor)中定义。 -(But it will not have access to the property `value` of a +(But it will not have access to the property `value` of a `Foo` instance, since that property gets defined in the [constructor](#constructor) -of `Foo`. But this constructor has to be called explicitly.) +of `Foo`. But this constructor has to be called explicitly.) -[译者注][30]:我认为这个描述是错误的,test.value 是可以访问的。 -因为在设置 Bar.prototype = new Foo(); 时,`value` 也就成为 Bar.prototype 上的一个属性。 +> **[译者注][30]:**我认为这个描述是错误的,`test.value` 是可以访问的。 +因为在设置 `Bar.prototype = new Foo();` 时,`value` 也就成为 `Bar.prototype` 上的一个属性。 如果你有不同观点,可以到[我的博客][30]评论。 > **注意:** **不要**使用 `Bar.prototype = Foo`,因为这不会执行 `Foo` 的原型,而是指向函数 `Foo`。 > 因此原型链将会回溯到 `Function.prototype` 而不是 `Foo.prototype`,因此 `method` 将不会在 Bar 的原型链上。 -### 属性查找(Property lookup) +###属性查找 当查找一个对象的属性时,JavaScript 会**向上**遍历原型链,直到找到给定名称的属性为止。 到查找到达原型链的顶部 - 也就是 `Object.prototype` - 但是仍然没有找到指定的属性,就会返回 [undefined](#undefined)。 -### 原型属性(The prototype property) +###原型属性 当原型属性用来创建原型链时,可以把**任何**类型的值赋给它(prototype)。 然而将原子类型赋给 prototype 的操作将会被忽略。 @@ -71,13 +71,13 @@ of `Foo`. But this constructor has to be called explicitly.) 而将对象赋值给 prototype,正如上面的例子所示,将会动态的创建原型链。 -### 性能(Performance) +###性能 如果一个属性在原型链的上端,则对于查找时间将带来不利影响。特别的,试图获取一个不存在的属性将会遍历整个原型链。 并且,当使用 [for-in](#the-for-in-loop) 循环遍历对象的属性时,原型链上的**所有**属性都将被访问。 -### 扩展内置类型的原型(Extension of native prototypes) +###扩展内置类型的原型 一个错误特性被经常使用,那就是扩展 `Object.prototype` 或者其他内置类型的原型对象。 @@ -86,10 +86,9 @@ of `Foo`. But this constructor has to be called explicitly.) 扩展内置类型的**唯一**理由是为了和新的 JavaScript 保持一致,比如 [`Array.forEach`][3]。 -[译者注][30]:这是编程领域常用的一种方式,称之为 [Backport][5],也就是将新的补丁添加到老版本中。 - +> **[译者注][30]:**这是编程领域常用的一种方式,称之为 [Backport][5],也就是将新的补丁添加到老版本中。 -### 总结(In conclusion) +###总结 在写复杂的 JavaScript 应用之前,充分理解原型链继承的工作方式是每个 JavaScript 程序员**必修**的功课。 要提防原型链过长带来的性能问题,并知道如何通过缩短原型链来提高性能。 diff --git a/doc/zh/other/timeouts.md b/doc/zh/other/timeouts.md index 1e57cc49..5993e362 100755 --- a/doc/zh/other/timeouts.md +++ b/doc/zh/other/timeouts.md @@ -1,8 +1,8 @@ -### `setTimeout` 和 `setInterval` +###`setTimeout` 和 `setInterval` 由于 JavaScript 是异步的,可以使用 `setTimeout` 和 `setInterval` 来计划执行函数。 -> **注意:** 定时处理**不是** ECMAScript 的标准,它们在 [DOM][1] 被实现。 +> **注意:** 定时处理**不是** ECMAScript 的标准,它们在 [DOM (文档对象模型)][1] 被实现。 function foo() {} var id = setTimeout(foo, 1000); // 返回一个大于零的数字 @@ -30,8 +30,7 @@ > 这里回调函数是 `foo` 的**返回值**,而**不是**`foo`本身。 > 大部分情况下,这是一个潜在的错误,因为如果函数返回 `undefined`,`setTimeout` 也**不会**报错。 - -### `setInterval` 的堆调用(Stacking calls with `setInterval`) +###`setInterval` 的堆调用 `setTimeout` 只会执行回调函数一次,不过 `setInterval` - 正如名字建议的 - 会每隔 `X` 毫秒执行函数一次。 但是却不鼓励使用这个函数。 @@ -48,7 +47,7 @@ 在 `foo` 被阻塞的时候,`setInterval` 仍然在组织将来对回调函数的调用。 因此,当第一次 `foo` 函数调用结束时,已经有 **10** 次函数调用在等待执行。 -### 处理可能的阻塞调用(Dealing with possible blocking code) +###处理可能的阻塞调用 最简单也是最容易控制的方案,是在回调函数内部使用 `setTimeout` 函数。 @@ -62,7 +61,7 @@ `foo` 函数现在可以控制是否继续执行还是终止执行。 -### 手工清空定时器(Manually clearing timeouts) +###手工清空定时器 可以通过将定时时产生的 ID 标识传递给 `clearTimeout` 或者 `clearInterval` 函数来清除定时, 至于使用哪个函数取决于调用的时候使用的是 `setTimeout` 还是 `setInterval`。 @@ -70,7 +69,7 @@ var id = setTimeout(foo, 1000); clearTimeout(id); -### 清除所有定时器(Clearing all timeouts) +###清除所有定时器 由于没有内置的清除所有定时器的方法,可以采用一种暴力的方式来达到这一目的。 @@ -79,15 +78,14 @@ clearTimeout(i); } -可能还有些定时器不会在上面代码中被清除([译者注][30]:如果定时器调用时返回的 ID 值大于 1000), +可能还有些定时器不会在上面代码中被清除(**[译者注][30]:**如果定时器调用时返回的 ID 值大于 1000), 因此我们可以事先保存所有的定时器 ID,然后一把清除。 -### 隐藏使用 `eval`(Hidden use of `eval`) +###隐藏使用 `eval` `setTimeout` 和 `setInterval` 也接受第一个参数为字符串的情况。 这个特性**绝对**不要使用,因为它在内部使用了 `eval`。 - > **注意:** 由于定时器函数不是 ECMAScript 的标准,如何解析字符串参数在不同的 JavaScript 引擎实现中可能不同。 > 事实上,微软的 JScript 会使用 `Function` 构造函数来代替 `eval` 的使用。 @@ -120,18 +118,15 @@ > **注意:** 虽然也可以使用这样的语法 `setTimeout(foo, 1000, a, b, c)`, > 但是不推荐这么做,因为在使用对象的[属性方法](#function.this)时可能会出错。 ->(译者注:这里说的是属性方法内,this 的指向错误) - +>(**译者注:**这里说的是属性方法内,`this` 的指向错误) -### 结论(In conclusion) +###结论 **绝对不要**使用字符串作为 `setTimeout` 或者 `setInterval` 的第一个参数, 这么写的代码明显质量很差。当需要向回调函数传递参数时,可以创建一个*匿名函数*,在函数内执行真实的回调函数。 另外,应该避免使用 `setInterval`,因为它的定时执行不会被 JavaScript 阻塞。 - [1]: http://en.wikipedia.org/wiki/Document_Object_Model - [30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/types/casting.md b/doc/zh/types/casting.md index 70f956da..df7383b1 100755 --- a/doc/zh/types/casting.md +++ b/doc/zh/types/casting.md @@ -1,4 +1,4 @@ -## 类型转换 +##类型转换 JavaScript 是*弱类型*语言,所以会在**任何**可能的情况下应用*强制类型转换*。 @@ -21,8 +21,7 @@ JavaScript 是*弱类型*语言,所以会在**任何**可能的情况下应用 为了避免上面复杂的强制类型转换,**强烈**推荐使用[严格的等于操作符](#types.equality)。 虽然这可以避免大部分的问题,但 JavaScript 的弱类型系统仍然会导致一些其它问题。 - -### 内置类型的构造函数(Constructors of built-in types) +###内置类型的构造函数 内置类型(比如 `Number` 和 `String`)的构造函数在被调用时,使用或者不使用 `new` 的结果完全不同。 @@ -37,21 +36,19 @@ JavaScript 是*弱类型*语言,所以会在**任何**可能的情况下应用 最好的选择是把要比较的值**显式**的转换为三种可能的类型之一。 - -### 转换为字符串(Casting to a string) +###转换为字符串 '' + 10 === '10'; // true 将一个值加上空字符串可以轻松转换为字符串类型。 - -### 转换为数字(Casting to a number) +###转换为数字 +'10' === 10; // true 使用**一元**的加号操作符,可以把字符串转换为数字。 -[译者注][30]:字符串转换为数字的常用方法: +**[译者注][30]:**字符串转换为数字的常用方法: +'010' === 10 Number('010') === 10 @@ -62,7 +59,7 @@ JavaScript 是*弱类型*语言,所以会在**任何**可能的情况下应用 parseInt('010.2', 10) === 10 -### 转换为布尔型(Casting to a boolean) +###转换为布尔型 通过使用 **否** 操作符两次,可以把一个值转换为布尔型。 @@ -74,6 +71,4 @@ JavaScript 是*弱类型*语言,所以会在**任何**可能的情况下应用 !!{}; // true !!true; // true - - -[30]: http://cnblogs.com/sanshi/ \ No newline at end of file +[30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/types/equality.md b/doc/zh/types/equality.md index 22b12469..637d7bd6 100755 --- a/doc/zh/types/equality.md +++ b/doc/zh/types/equality.md @@ -1,8 +1,8 @@ -## 相等与比较 +##相等与比较 JavaScript 有两种方式判断两个值是否相等。 -### 等于操作符(The equals operator) +###等于操作符 等于操作符由两个等号组成:`==` @@ -23,10 +23,9 @@ JavaScript 是*弱类型*语言,这就意味着,等于操作符会为了比 此外,强制类型转换也会带来性能消耗,比如一个字符串为了和一个数组进行比较,必须事先被强制转换为数字。 +###严格等于操作符 -### 严格的等于操作符(The strict equals operator) - -严格的等于操作符由**三**个等号组成:`===` +严格等于操作符由**三**个等号组成:`===` 不想普通的等于操作符,严格的等于操作符**不会**进行强制类型转换。 @@ -42,8 +41,7 @@ JavaScript 是*弱类型*语言,这就意味着,等于操作符会为了比 上面的结果更加清晰并有利于代码的分析。如果两个操作数类型不同就肯定不相等也有助于性能的提升。 - -### 比较对象(Comparing objects) +###比较对象 虽然 `==` 和 `===` 操作符都是等于操作符,但是当其中有一个操作数为对象时,行为就不同了。 @@ -56,11 +54,11 @@ JavaScript 是*弱类型*语言,这就意味着,等于操作符会为了比 这里等于操作符比较的**不是**值是否相等,而是是否属于同一个**身份**;也就是说,只有对象的同一个实例才被认为是相等的。 这有点像 Python 中的 `is` 和 C 中的指针比较。 -### 结论(In conclusion) +###结论 强烈推荐使用**严格的等于操作符**。如果类型需要转换,应该在比较之前[显式](#types.casting)的转换, 而不是使用语言本身复杂的强制转换规则。 -[30]: http://cnblogs.com/sanshi/ \ No newline at end of file +[30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/types/instanceof.md b/doc/zh/types/instanceof.md index 8aec4d15..b7aae51e 100755 --- a/doc/zh/types/instanceof.md +++ b/doc/zh/types/instanceof.md @@ -1,9 +1,9 @@ -## `instanceof` 操作符 +##`instanceof` 操作符 `instanceof` 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 如果用来比较内置类型,将会和 [typeof 操作符](#types.typeof) 一样用处不大。 -### 比较自定义对象(Comparing custom objects) +###比较自定义对象 function Foo() {} function Bar() {} @@ -16,7 +16,7 @@ Bar.prototype = Foo; new Bar() instanceof Foo; // false -### `instanceof` 比较内置类型(Using `instanceof` with native types) +###`instanceof` 比较内置类型 new String('foo') instanceof String; // true new String('foo') instanceof Object; // true @@ -27,11 +27,9 @@ 有一点需要注意,`instanceof` 用来比较属于不同 JavaScript 上下文的对象(比如,浏览器中不同的文档结构)时将会出错, 因为它们的构造函数不会是同一个对象。 - -### 结论(In conclusion) +### 结论 `instanceof` 操作符应该**仅仅**用来比较来自同一个 JavaScript 上下文的自定义对象。 正如 [`typeof`](#types.typeof) 操作符一样,任何其它的用法都应该是避免的。 - -[30]: http://cnblogs.com/sanshi/ \ No newline at end of file +[30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/types/typeof.md b/doc/zh/types/typeof.md index b14ea3cd..8a322b2f 100755 --- a/doc/zh/types/typeof.md +++ b/doc/zh/types/typeof.md @@ -1,18 +1,16 @@ -## `typeof` 操作符 +##`typeof` 操作符 `typeof` 操作符(和 [`instanceof`](#types.instanceof) 一起)或许是 JavaScript 中最大的设计缺陷, 因为几乎不可能从它们那里得到想要的结果。 -尽管 `instanceof` 还有一些极少数的应用场景,`typeof` 只有一个实际的应用([译者注][30]:这个实际应用是用来检测一个对象是否已经定义或者是否已经赋值), +尽管 `instanceof` 还有一些极少数的应用场景,`typeof` 只有一个实际的应用(**[译者注][30]:**这个实际应用是用来检测一个对象是否已经定义或者是否已经赋值), 而这个应用却**不是**用来检查对象的类型。 - > **注意:** 由于 `typeof` 也可以像函数的语法被调用,比如 `typeof(obj)`,但这并是一个函数调用。 > 那两个小括号只是用来计算一个表达式的值,这个返回值会作为 `typeof` 操作符的一个操作数。 > 实际上**不存在**名为 `typeof` 的函数。 - -### JavaScript 类型表格(The JavaScript type table) +###JavaScript 类型表格 Value Class Type ------------------------------------- @@ -42,8 +40,7 @@ 为了获取对象的 `[[Class]]`,我们需要使用定义在 `Object.prototype` 上的方法 `toString`。 - -### 对象的类定义(The Class of an object) +###对象的类定义 JavaScript 标准文档只给出了一种获取 `[[Class]]` 值的方法,那就是使用 `Object.prototype.toString`。 @@ -57,19 +54,17 @@ JavaScript 标准文档只给出了一种获取 `[[Class]]` 值的方法,那 上面例子中,`Object.prototype.toString` 方法被调用,[this](#function.this) 被设置为了需要获取 `[[Class]]` 值的对象。 - -[译者注][30]:`Object.prototype.toString` 返回一种标准格式字符串,所以上例可以通过 slice 截取指定位置的字符串,如下所示: +**[译者注][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 中看出区别,如下所示: +**[译者注][30]:**这种变化可以从 IE8 和 Firefox 4 中看出区别,如下所示: // IE8 Object.prototype.toString.call(null) // "[object Object]" @@ -80,7 +75,7 @@ JavaScript 标准文档只给出了一种获取 `[[Class]]` 值的方法,那 Object.prototype.toString.call(undefined) // "[object Undefined]" -### 测试为定义变量(Testing for undefined variables) +###测试为定义变量 typeof foo !== 'undefined' @@ -88,7 +83,7 @@ JavaScript 标准文档只给出了一种获取 `[[Class]]` 值的方法,那 这是 `typeof` 唯一有用的地方。 -### 结论(In conclusion) +###结论 为了检测一个对象的类型,强烈推荐使用 `Object.prototype.toString` 方法; 因为这是唯一一个可依赖的方式。正如上面表格所示,`typeof` 的一些返回值在标准文档中并未定义, diff --git a/site/style/garden.css b/site/style/garden.css index 407cf37d..d0ef21ab 100644 --- a/site/style/garden.css +++ b/site/style/garden.css @@ -346,6 +346,10 @@ aside p { line-height: 1.3em; } +aside p + p { + margin-top: 1.5em; +} + aside.es5:after { content: 'ES5'; font-family: Georgia, serif; diff --git a/site/zh/index.html b/site/zh/index.html index 48d57135..8c55454b 100644 --- a/site/zh/index.html +++ b/site/zh/index.html @@ -1,12 +1,12 @@ -JavaScript Garden -

简介

JavaScript 秘密花园是一个不断更新的文档,主要关心 JavaScript 一些古怪用法。 +

简介

JavaScript 秘密花园是一个不断更新,主要关心 JavaScript 一些古怪用法的文档。 对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, 初学者可以籍此深入了解 JavaScript 的语言特性。

JavaScript 秘密花园不是用来教你 JavaScript。为了更好的理解这篇文章的内容, -你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习向导

 关于作者

这篇文章的作者是两位 Stack Overflow 的用户, Ivo Wetzel (写作) 和 Zhang Yi Jiang (设计)。

 贡献者

    +你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习向导

 许可

JavaScript 秘密花园在 MIT license 许可协议下发布,并存放在开源社区 GitHub。 -如果你发现错误或者打字错误,请 file an issue 或者 pull request。 -你也可以在 Stack Overflow 的聊天室 JavaScript room 找到我们。

对象

 对象使用和属性

JavaScript 中所有变量都是对象,除了两个例外 nullundefined

+

此中文翻译由三生石上独立完成,博客园首发,转载请注明出处。

对象

对象使用和属性

JavaScript 中所有变量都是对象,除了两个例外 nullundefined

false.toString() // 'false'
 [1, 2, 3].toString(); // '1,2,3'
@@ -42,7 +42,7 @@ 

中文翻译

(2).toString(); // 2先被计算
-

对象作为数据类型(Objects as a data type)

+

对象作为数据类型

JavaScript 的对象可以作为哈希表使用,主要用来保存命名的键与值的对应关系。

@@ -55,7 +55,7 @@

中文翻译

var bar = {test: 12};
-

访问属性(Accessing properties)

+

访问属性

有两种方式来访问对象的属性,点操作符或者中括号操作符。

@@ -72,11 +72,13 @@

中文翻译

两种语法是等价的,但是中括号操作符在下面两种情况下依然有效 - 动态设置属性 - - 属性名不是一个有效的变量名(译者注:比如属性名中包含空格,或者属性名是JS的关键词)

+ - 属性名不是一个有效的变量名(译者注比如属性名中包含空格,或者属性名是 JS 的关键词)

-

译者注:在 JSLint 语法检测工具中,点操作符是推荐做法。

+ -

删除属性(Deleting properties)

+

删除属性

删除属性的唯一方法是使用 delete 操作符;设置属性为 undefined 或者 null 并不能真正的删除属性, 而仅仅是移除了属性和值的关联。

@@ -99,7 +101,7 @@

中文翻译

上面的输出结果有 bar undefinedfoo null - 只有 baz 被真正的删除了,所以从输出结果中消失。

-

属性名的语法(Notation of keys)

+

属性名的语法

var test = {
     'case': 'I am a keyword so I must be notated as a string',
@@ -111,12 +113,12 @@ 

中文翻译

上面的第二种声明方式在 ECMAScript 5 之前会抛出 SyntaxError 的错误。

这个错误的原因是 delete 是 JavaScript 语言的一个关键词;因此为了在更低版本的 JavaScript 引擎下也能正常运行, -必须使用字符串字面值声明方式。

 原型

JavaScript 不包含传统的类继承模型,而是使用 prototypical 原型模型。

+必须使用字符串字面值声明方式。

原型

JavaScript 不包含传统的类继承模型,而是使用 prototypal 原型模型。

虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。 实现传统的类继承模型是很简单,但是实现 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.)

+(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 是唯一一个被广泛使用的基于原型继承的语言,所以理解两种继承模式的差异是需要一定时间的。

@@ -158,26 +160,26 @@

中文翻译

上面的例子中,test 对象从 Bar.prototypeFoo.prototype 继承下来;因此, 它能否访问 Foo 的原型方法 method。但是它不能访问 Foo 的实例属性 value, 因为这个属性在Foo构造函数中定义。 -(But it will not have access to the property value of a +(But it will not have access to the property value of a Foo instance, since that property gets defined in the constructor -of Foo. But this constructor has to be called explicitly.)

- -

译者注:我认为这个描述是错误的,test.value 是可以访问的。 -因为在设置 Bar.prototype = new Foo(); 时,value 也就成为 Bar.prototype 上的一个属性。 -如果你有不同观点,可以到我的博客评论。

+of Foo. But this constructor has to be called explicitly.)

-

属性查找(Property lookup)

+

属性查找

当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。

到查找到达原型链的顶部 - 也就是 Object.prototype - 但是仍然没有找到指定的属性,就会返回 undefined

-

原型属性(The prototype property)

+

原型属性

当原型属性用来创建原型链时,可以把任何类型的值赋给它(prototype)。 然而将原子类型赋给 prototype 的操作将会被忽略。

@@ -188,13 +190,13 @@

中文翻译

而将对象赋值给 prototype,正如上面的例子所示,将会动态的创建原型链。

-

性能(Performance)

+

性能

如果一个属性在原型链的上端,则对于查找时间将带来不利影响。特别的,试图获取一个不存在的属性将会遍历整个原型链。

并且,当使用 for-in 循环遍历对象的属性时,原型链上的所有属性都将被访问。

-

扩展内置类型的原型(Extension of native prototypes)

+

扩展内置类型的原型

一个错误特性被经常使用,那就是扩展 Object.prototype 或者其他内置类型的原型对象。

@@ -203,13 +205,15 @@

中文翻译

扩展内置类型的唯一理由是为了和新的 JavaScript 保持一致,比如 Array.forEach

-

译者注:这是编程领域常用的一种方式,称之为 Backport,也就是将新的补丁添加到老版本中。

+ -

总结(In conclusion)

+

总结

在写复杂的 JavaScript 应用之前,充分理解原型链继承的工作方式是每个 JavaScript 程序员必修的功课。 要提防原型链过长带来的性能问题,并知道如何通过缩短原型链来提高性能。 -更进一步,绝对不要扩展内置类型的原型,除非是为了和新的 JavaScript 引擎兼容。

 hasOwnProperty 函数

为了判断一个对象是否包含自定义属性而不是原型链上的属性, +更进一步,绝对不要扩展内置类型的原型,除非是为了和新的 JavaScript 引擎兼容。

hasOwnProperty 函数

为了判断一个对象是否包含自定义属性而不是原型链上的属性, 我们需要使用继承自 Object.prototypehasOwnProperty 方法。

-

hasOwnProperty 是JavaScript中唯一一个处理属性但是不需要查找原型链的方法。

+

hasOwnProperty 是 JavaScript 中唯一一个处理属性但是查找原型链的函数。

// 修改Object.prototype
 Object.prototype.bar = 1; 
@@ -233,7 +237,7 @@ 

中文翻译

只有 hasOwnProperty 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 没有其它方法可以用来排除原型链上的属性,而不是定义在对象自身上的属性。

-

hasOwnProperty 作为属性(hasOwnProperty as a property)

+

hasOwnProperty 作为属性

JavaScript 不会保护 hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性, 就需要使用外部hasOwnProperty 函数来获取正确的结果。

@@ -251,11 +255,11 @@

中文翻译

{}.hasOwnProperty.call(foo, 'bar'); // true
-

结论(In conclusion)

+

结论

当检查对象上某个属性是否存在时,hasOwnProperty唯一可用的方法。 同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法, -这将会避免原型对象扩展带来的干扰。

 for in 循环

in 操作符一样,for in 循环同样在查找对象属性时遍历原型链上的所有属性。

+这将会避免原型对象扩展带来的干扰。

for in 循环

in 操作符一样,for in 循环同样在查找对象属性时遍历原型链上的所有属性。

-

使用 hasOwnProperty 过滤(Using hasOwnProperty for filtering)

+

使用 hasOwnProperty 过滤

// foo 变量是上例中的
 for(var i in foo) {
@@ -293,12 +297,12 @@ 

中文翻译

一个广泛使用的类库 Prototype 就扩展了原生的 JavaScript 对象。 因此,但这个类库被包含在页面中时,不使用 hasOwnProperty 过滤的 for in 循环难免会出问题。

-

最佳实践(Best practices)

+

总结

-

推荐总是使用 hasOwnProperty。不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。

函数

 函数声明与表达式

函数是JavaScript中的一等对象,这意味着可以把函数像其它值一样传递。 +

推荐总是使用 hasOwnProperty。不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。

函数

函数声明与表达式

函数是JavaScript中的一等对象,这意味着可以把函数像其它值一样传递。 一个常见的用法是把匿名函数作为回调函数传递对异步函数中。

-

函数声明(The function declaration)

+

函数声明

function foo() {}
 
@@ -310,7 +314,7 @@

中文翻译

function foo() {}
-

函数赋值表达式(The function expression)

+

函数赋值表达式

var foo = function() {};
 
@@ -326,7 +330,7 @@

中文翻译

但是由于赋值语句只在运行时执行,因此在相应代码执行之前, foo 的值缺省为 undefined

-

命名函数的赋值表达式(Named function expression)

+

命名函数的赋值表达式

另外一个特殊的情况是将命名函数赋值给一个变量。

@@ -338,19 +342,21 @@

中文翻译

bar 函数声明外是不可见的,这是因为我们已经把函数赋值给了 foo; 然而在 bar 内部依然可见。这是由于 JavaScript 的 命名处理 所致, -函数名在函数内总是可见的。

 this 的工作原理

JavaScript 有一套完全不同于其它语言的对 this 的处理机制。 +函数名在函数内总是可见的。

this 的工作原理

JavaScript 有一套完全不同于其它语言的对 this 的处理机制。 在种不同的情况下 ,this 指向的各不相同。

-

全局范围内(The global scope)

+

全局范围内

this;
 

当在全部范围内使用 this,它将会指向全局对象。

-

译者注:浏览器中运行的JavaScript脚本,这个全局对象是 window。

+ -

函数调用(Calling a function)

+

函数调用

foo();
 
@@ -362,14 +368,14 @@

中文翻译

译者注:ES5指的是ECMAScript 5,是 2009-12 发布的最新的 JavaScript 版本。

-

方法调用(Calling a method)

+

方法调用

test.foo(); 
 

这个例子中,this 指向 test 对象。

-

调用构造函数(Calling a constructor)

+

调用构造函数

new foo(); 
 
@@ -377,7 +383,7 @@

中文翻译

如果函数倾向于和 new 关键词一块使用,则我们称这个函数是 构造函数。 在函数内部,this 指向新创建的对象。

-

显式的设置 this(Explicit setting of this

+

显式的设置 this

function foo(a, b, c) {}
 
@@ -394,17 +400,17 @@ 

中文翻译

-

常见误解(Common pitfalls)

+

常见误解

-

尽管大部分的情况都说的过去,不过第一个规则(译者注:这里指的应该是第二个规则,也就是直接调用函数时,this 指向全局对象) +

尽管大部分的情况都说的过去,不过第一个规则(译者注这里指的应该是第二个规则,也就是直接调用函数时,this 指向全局对象) 被认为是JavaScript语言另一个错误设计的地方,因为它从来就没有实际的用途。

Foo.method = function() {
     function test() {
-        // this 将会被设置为全局对象([译者注][30]:浏览器环境中也就是 window 对象)
+        // this 将会被设置为全局对象(译者注:浏览器环境中也就是 window 对象)
     }
     test();
 }
@@ -424,9 +430,9 @@ 

中文翻译

that 只是我们随意起的名字,不过这个名字被广泛的用来指向外部的 this 对象。 -在 closures 一节,我们可以看到 that 可以作为参数传递。

+在 闭包 一节,我们可以看到 that 可以作为参数传递。

-

方法的赋值表达式(Assigning methods)

+

方法的赋值表达式

另一个看起来奇怪的地方是函数别名,也就是将一个方法赋值给一个变量。

@@ -447,10 +453,10 @@

中文翻译

new Bar().method();
-

method 被调用时,this 将会指向 Bar 的实例对象。

 闭包和引用

闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 +

method 被调用时,this 将会指向 Bar 的实例对象。

闭包和引用

闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 因为 函数 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。

-

模拟私有变量(Emulating private variables)

+

模拟私有变量

function Counter(start) {
     var count = start;
@@ -473,7 +479,7 @@ 

中文翻译

这里,Counter 函数返回两个闭包,函数 increment 和函数 get。 这两个函数都维持着 对外部作用域 Counter 的引用,因此总可以访问此作用域内定义的变量 count.

-

为什么不可以在外部访问私有变量(Why private variables work)

+

为什么不可以在外部访问私有变量

因为 JavaScript 中不可以对作用域进行引用或赋值,因此没有办法在外部访问 count 变量。 唯一的途径就是通过那两个闭包。

@@ -487,10 +493,9 @@

中文翻译

上面的代码不会改变定义在 Counter 作用域中的 count 变量的值,因为 foo.hack 没有 定义在那个作用域内。它将会创建或者覆盖全局变量 count

-

循环中的闭包(Closures inside loops)

+

循环中的闭包

-

一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号(as if they were -copying the value of the loops index variable.)。

+

一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号

for(var i = 0; i < 10; i++) {
     setTimeout(function() {
@@ -501,13 +506,13 @@ 

中文翻译

上面的代码不会输出数字 09,而是会输出数字 10 十次。

-

console.log 被调用的时候,匿名函数保持对外部变量 i 的引用,此时 for 循环已经结束, i 的值被修改成了 10.

+

console.log 被调用的时候,匿名函数保持对外部变量 i 的引用,此时 for循环已经结束, i 的值被修改成了 10.

为了得到想要的结果,需要在每次循环中创建变量 i拷贝

-

避免引用错误(Avoiding the reference problem)

+

避免引用错误

-

为了正确的获得循环序号,最好使用 匿名包裹器译者注:其实就是我们通常说的自执行匿名函数)。

+

为了正确的获得循环序号,最好使用 匿名包裹器译者注其实就是我们通常说的自执行匿名函数)。

for(var i = 0; i < 10; i++) {
     (function(e) {
@@ -531,7 +536,7 @@ 

中文翻译

} })(i), 1000) } -

 arguments 对象

JavaScript 中每个函数内都能访问一个特别变量 arguments。这个变量维护着所有传递到这个函数中的参数列表。

+

arguments 对象

JavaScript 中每个函数内都能访问一个特别变量 arguments。这个变量维护着所有传递到这个函数中的参数列表。

转化为数组(Converting to an array)

+

转化为数组

下面的代码将会创建一个新的数组,包含所有 arguments 对象中的元素。

@@ -554,7 +559,7 @@

中文翻译

这个转化比较,在性能不好的代码中不推荐这种做法。

-

传递参数(Passing arguments)

+

传递参数

下面将参数从一个函数传递到另一个函数,是推荐的做法。

@@ -583,7 +588,7 @@

中文翻译

};
-

译者注:上面的 Foo.method 函数和下面代码的效果是一样的:

+

译者注:上面的 Foo.method 函数和下面代码的效果是一样的:

Foo.method = function() {
     var args = Array.prototype.slice.call(arguments);
@@ -591,7 +596,7 @@ 

中文翻译

};
-

自动更新(Modification "magic")

+

自动更新

arguments 对象为其内部属性以及函数形式参数创建 gettersetter 方法。

@@ -611,7 +616,7 @@

中文翻译

foo(1, 2, 3); -

性能真相(Performance myths and truths)

+

性能真相

arguments 对象总会被创建,除了两个特殊情况 - 作为局部变量声明和作为形式参数。 而不管它是否有被使用。

@@ -622,11 +627,10 @@

中文翻译

-

译者注:在 MDC 中对 strict mode 模式下 arguments 的描述有助于我们的理解,请看下面代码:

+

译者注MDC 中对 strict mode 模式下 arguments 的描述有助于我们的理解,请看下面代码:

// 阐述在 ES5 的严格模式下 `arguments` 的特性
-function f(a)
-{
+function f(a) {
   "use strict";
   a = 42;
   return [a, arguments[0]];
@@ -650,14 +654,14 @@ 

中文翻译

}
-

上面代码中,foo 不再是一个单纯的内联函数 inlining译者注:这里指的是解析器可以做内联处理), +

上面代码中,foo 不再是一个单纯的内联函数 inlining译者注:这里指的是解析器可以做内联处理), 因为它需要知道它自己和它的调用者。 这不仅抵消了内联函数带来的性能提升,而且破坏了封装,因此现在函数可能要依赖于特定的上下文。

因此强烈建议大家不要使用 arguments.callee 和它的属性。

 构造函数

JavaScript 中的构造函数和其它语言中的构造函数是不同的。 +

构造函数

JavaScript 中的构造函数和其它语言中的构造函数是不同的。 通过 new 关键字方式调用的函数都被认为是构造函数。

在构造函数内部 - 也就是被调用的函数内 - this 指向新创建的对象 Object。 @@ -695,8 +699,8 @@

中文翻译

new Test(); // 返回的对象 -

译者注:new Bar() 返回的是新创建的对象,而不是数字的字面值 2。 -因此 new Bar().constructor === Bar,但是如果返回的是数字对象,结果就不同了,如下所示

+

译者注new Bar() 返回的是新创建的对象,而不是数字的字面值 2。 +因此 new Bar().constructor === Bar,但是如果返回的是数字对象,结果就不同了,如下所示

function Bar() {
     return new Number(2);
@@ -704,9 +708,11 @@ 

中文翻译

new Bar().constructor === Number
-

译者注:这里得到的(new Test())是函数返回的对象,而不是通过 new 关键字新创建的对象,因此: - (new Test()).value === undefined - (new Test()).foo === 1

+

译者注这里得到的 new Test()是函数返回的对象,而不是通过new关键字新创建的对象,因此:

+ +
(new Test()).value === undefined
+(new Test()).foo === 1
+

如果 new 被遗漏了,则函数不会返回新创建的对象。

@@ -719,7 +725,7 @@

中文翻译

虽然上例在有些情况下也能正常运行,但是由于 JavaScript 中 this 的工作原理, 这里的 this 指向全局对象

-

工厂模式(Factories)

+

工厂模式

为了不使用 new 关键字,构造函数必须显式的返回一个值。

@@ -742,12 +748,12 @@

中文翻译

上面两种对 Bar 函数的调用返回的值完全相同,一个新创建的拥有 method 属性的对象被返回, 其实这里创建了一个闭包

-

还需要注意,new Bar()不会改变返回对象的原型(译者注:也就是返回对象的原型不会指向 Bar.prototype)。 +

还需要注意, new Bar()不会改变返回对象的原型(译者注也就是返回对象的原型不会指向 Bar.prototype)。 因为构造函数的原型会被指向到刚刚创建的新对象,而这里的 Bar 没有把这个新对象返回(译者注:而是返回了一个包含 method 属性的自定义对象)。

在上面的例子中,使用或者不使用 new 关键字没有功能性的区别。

-

译者注:上面两种方式创建的对象不能访问 Bar 原型链上的属性,如下所示:

+

译者注上面两种方式创建的对象不能访问 Bar 原型链上的属性,如下所示:

var bar1 = new Bar();
 typeof(bar1.method); // "function"
@@ -758,7 +764,7 @@ 

中文翻译

typeof(bar2.foo); // "undefined"
-

通过工厂模式创建新对象(Creating new objects via factories)

+

通过工厂模式创建新对象

我们常听到的一条忠告是不要使用 new 关键字来调用函数,因为如果忘记使用它就会导致错误。

@@ -789,10 +795,10 @@

中文翻译

  • 放弃原型链仅仅是因为防止遗漏 new 带来的问题,这似乎和语言本身的思想相违背。
  • -

    总结(In conclusion)

    +

    总结

    虽然遗漏 new 关键字可能会导致问题,但这并不是放弃使用原型链的借口。 -最终使用哪种方式取决于应用程序的需求,选择一种代码书写风格并坚持下去才是最重要的。

     作用域与命名空间

    尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; +最终使用哪种方式取决于应用程序的需求,选择一种代码书写风格并坚持下去才是最重要的。

    作用域与命名空间

    尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; 而仅仅支持 函数作用域

    function test() { // 一个作用域
    @@ -808,7 +814,7 @@ 

    中文翻译

    而不是作为对象的字面语法解析。如果考虑到 自动分号插入,这可能会导致一些不易察觉的错误。

    -

    译者注:如果 return 对象的左括号和 return 不在一行上就会出错。

    +

    译者注如果 return 对象的左括号和 return 不在一行上就会出错。

    // 译者注:下面输出 undefined
     function add(a, b) {
    @@ -823,12 +829,12 @@ 

    中文翻译

    每次引用一个变量,JavaScript 会向上遍历整个作用域直到找到这个变量为止。 如果到达全局作用域但是这个变量仍未找到,则会抛出 ReferenceError 异常。

    -

    隐式的全局变量(The bane of global variables)

    +

    隐式的全局变量

    -
    // script A
    +
    // 脚本 A
     foo = '42';
     
    -// script B
    +// 脚本 B
     var foo = '42'
     
    @@ -867,7 +873,7 @@

    中文翻译

    在第二个 for 循环中使用 var 声明变量可以避免这种错误。 声明变量时绝对不要遗漏 var 关键字,除非这就是期望的影响外部作用域的行为。

    -

    局部变量(Local variables)

    +

    局部变量

    JavaScript 中局部变量只可能通过两种方式声明,一个是作为函数参数,另一个是通过 var 关键字声明。

    @@ -938,7 +944,7 @@

    中文翻译

    没有块级作用域不仅导致 var 表达式被从循环内移到外部,而且使一些 if 表达式更难看懂。

    -

    在原来代码中,if 表达式看起来修改了全部变量 goo,实际上在提升规则(hoisting)被应用后,却是在修改局部变量

    +

    在原来代码中,if 表达式看起来修改了全部变量 goo,实际上在提升规则被应用后,却是在修改局部变量

    如果没有提升规则(hoisting)的知识,下面的代码看起来会抛出异常 ReferenceError

    @@ -960,7 +966,7 @@

    中文翻译

    }
    -

    译者注:在 Nettuts+ 网站有一篇介绍 hoisting 的文章,其中的代码很有启发性。

    +

    译者注在 Nettuts+ 网站有一篇介绍 hoisting 的文章,其中的代码很有启发性。

    // 译者注:来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则
     var myvar = 'my value';  
    @@ -971,7 +977,7 @@ 

    中文翻译

    })();
    -

    名称解析顺序(Name resolution order)

    +

    名称解析顺序

    JavaScript 中的所有作用域,包括全局作用域,都有一个特别的名称 this 指向当前对象。

    @@ -990,7 +996,7 @@

    中文翻译

    注意: 自定义 arguments 参数将会阻止原生的 arguments 对象的创建。

    -

    命名空间(Namespaces)

    +

    命名空间

    只有一个全局作用域导致的常见错误是命名冲突。在 JavaScript中,这可以通过 匿名包装器 轻松解决。

    @@ -1004,7 +1010,7 @@

    中文翻译

    })(); // 立即执行此匿名函数
    -

    匿名函数被认为是 表达式;因此为了可调用性,它们首先会被执行(evaluated)。

    +

    匿名函数被认为是 表达式;因此为了可调用性,它们首先会被执行。

    ( // 小括号内的函数首先被执行
     function() {}
    @@ -1019,12 +1025,12 @@ 

    中文翻译

    (function(){}());
    -

    结论(In conclusion)

    +

    结论

    -

    推荐使用匿名包装器译者注:也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, +

    推荐使用匿名包装器译者注也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, 而且有利于程序的模块化。

    -

    另外,使用全局变量被认为是不好的习惯。这样的代码倾向于产生错误和带来高的维护成本。

    数组

     数组遍历与属性

    虽然在 JavaScript 中数组是是对象,但是没有好的理由去使用 for in 循环 遍历数组。 +

    另外,使用全局变量被认为是不好的习惯。这样的代码倾向于产生错误和带来高的维护成本。

    数组

    数组遍历与属性

    虽然在 JavaScript 中数组是是对象,但是没有好的理由去使用 for in 循环 遍历数组。 相反,有一些好的理由不去使用 for in 遍历数组。

    遍历(Iteration)

    +

    遍历

    为了达到遍历数组的最佳性能,推荐使用经典的 for 循环。

    @@ -1052,7 +1058,7 @@

    中文翻译

    实际上,不使用缓存数组长度的方式比缓存版本要慢很多。

    -

    length 属性(The length property)

    +

    length 属性

    length 属性的 getter 方式会简单的返回数组的长度,而 setter 方式会截断数组。

    @@ -1064,10 +1070,10 @@

    中文翻译

    foo; // [1, 2, 3] -

    译者注: -在 Firebug 中查看此时 foo 的值是: [1, 2, 3, undefined, undefined, undefined] -但是这个结果并不准确,如果你在 Chrome 的控制台查看 foo 的结果,你会发现是这样的: [1, 2, 3] -因为在 JavaScript 中 undefined 是一个变量,注意是变量不是关键字,因此上面两个结果的意义是完全不相同的。

    +

    译者注: +在 Firebug 中查看此时 foo 的值是: [1, 2, 3, undefined, undefined, undefined] +但是这个结果并不准确,如果你在 Chrome 的控制台查看 foo 的结果,你会发现是这样的: [1, 2, 3] +因为在 JavaScript 中 undefined 是一个变量,注意是变量不是关键字,因此上面两个结果的意义是完全不相同的。

    // 译者注:为了验证,我们来执行下面代码,看序号 5 是否存在于 foo 中。
     5 in foo; // 不管在 Firebug 或者 Chrome 都返回 false
    @@ -1077,10 +1083,10 @@ 

    中文翻译

    length 设置一个更小的值会截断数组,但是增大 length 属性值不会对数组产生影响。

    -

    结论(In conclusion)

    +

    结论

    为了更好的性能,推荐使用普通的 for 循环并缓存数组的 length 属性。 -使用 for in 遍历数组被认为是不好的代码习惯并倾向于产生错误和导致性能问题。

     Array 构造函数

    由于 Array 的构造函数在如何处理参数时有点模棱两可,因此总是推荐使用数组的字面语法 - [] - 来创建数组。

    +使用 for in 遍历数组被认为是不好的代码习惯并倾向于产生错误和导致性能问题。

    Array 构造函数

    由于 Array 的构造函数在如何处理参数时有点模棱两可,因此总是推荐使用数组的字面语法 - [] - 来创建数组。

    [1, 2, 3]; // 结果: [1, 2, 3]
     new Array(1, 2, 3); // 结果: [1, 2, 3]
    @@ -1088,20 +1094,22 @@ 

    中文翻译

    [3]; // 结果: [3] new Array(3); // 结果: [] new Array('3') // 结果: ['3'] -
    -

    译者注:这里的模棱两可指的是数组的两种构造函数语法 -var arr1 = new Array(arrayLength); -var arr2 = new Array(element0, element1, ..., elementN);

    - -
    // 译者注:因此下面的代码将会使人很迷惑
    +// 译者注:因此下面的代码将会使人很迷惑
     new Array(3, 4, 5); // 结果: [3, 4, 5] 
     new Array(3) // 结果: [],此数组长度为 3
     
    + +

    由于只有一个参数传递到构造函数中(译者注:指的是 new Array(3); 这种调用方式),并且这个参数是数字,构造函数会返回一个 length 属性被设置为此参数的空数组。 -需要特别注意的是,此时只有 length 属性被设置,真正的数组并没有生成。 -译者注:在 Firebug 中,你会看到 [undefined, undefined, undefined],这其实是不对的。在上一节有详细的分析。

    +需要特别注意的是,此时只有 length 属性被设置,真正的数组并没有生成。

    + +
    var arr = new Array(3);
     arr[1]; // undefined
    @@ -1111,14 +1119,17 @@ 

    中文翻译

    这种优先于设置数组长度属性的做法只在少数几种情况下有用,比如需要循环字符串,可以避免 for 循环的麻烦。

    new Array(count + 1).join(stringToRepeat);
    -// 译者注:new Array(3).join('#') 将会返回 "##"
     
    -

    结论(In conclusion)

    + + +

    结论

    -

    应该尽量避免使用数组构造函数创建新数组。推荐使用数组的字面语法。它们更加短小和简洁,因此增加了代码的可读性。

    类型

     相等与比较

    JavaScript 有两种方式判断两个值是否相等。

    +

    应该尽量避免使用数组构造函数创建新数组。推荐使用数组的字面语法。它们更加短小和简洁,因此增加了代码的可读性。

    类型

    相等与比较

    JavaScript 有两种方式判断两个值是否相等。

    -

    等于操作符(The equals operator)

    +

    等于操作符

    等于操作符由两个等号组成:==

    @@ -1140,9 +1151,9 @@

    中文翻译

    此外,强制类型转换也会带来性能消耗,比如一个字符串为了和一个数组进行比较,必须事先被强制转换为数字。

    -

    严格的等于操作符(The strict equals operator)

    +

    严格等于操作符

    -

    严格的等于操作符由个等号组成:===

    +

    严格等于操作符由个等号组成:===

    不想普通的等于操作符,严格的等于操作符不会进行强制类型转换。

    @@ -1159,7 +1170,7 @@

    中文翻译

    上面的结果更加清晰并有利于代码的分析。如果两个操作数类型不同就肯定不相等也有助于性能的提升。

    -

    比较对象(Comparing objects)

    +

    比较对象

    虽然 ===== 操作符都是等于操作符,但是当其中有一个操作数为对象时,行为就不同了。

    @@ -1173,13 +1184,13 @@

    中文翻译

    这里等于操作符比较的不是值是否相等,而是是否属于同一个身份;也就是说,只有对象的同一个实例才被认为是相等的。 这有点像 Python 中的 is 和 C 中的指针比较。

    -

    结论(In conclusion)

    +

    结论

    强烈推荐使用严格的等于操作符。如果类型需要转换,应该在比较之前显式的转换, -而不是使用语言本身复杂的强制转换规则。

     typeof 操作符

    typeof 操作符(和 instanceof 一起)或许是 JavaScript 中最大的设计缺陷, +而不是使用语言本身复杂的强制转换规则。

    typeof 操作符

    typeof 操作符(和 instanceof 一起)或许是 JavaScript 中最大的设计缺陷, 因为几乎不可能从它们那里得到想要的结果。

    -

    尽管 instanceof 还有一些极少数的应用场景,typeof 只有一个实际的应用(译者注:这个实际应用是用来检测一个对象是否已经定义或者是否已经赋值), +

    尽管 instanceof 还有一些极少数的应用场景,typeof 只有一个实际的应用(译者注这个实际应用是用来检测一个对象是否已经定义或者是否已经赋值), 而这个应用却不是用来检查对象的类型。

    -

    JavaScript 类型表格(The JavaScript type table)

    +

    JavaScript 类型表格

    Value               Class      Type
     -------------------------------------
    @@ -1221,7 +1232,7 @@ 

    中文翻译

    为了获取对象的 [[Class]],我们需要使用定义在 Object.prototype 上的方法 toString

    -

    对象的类定义(The Class of an object)

    +

    对象的类定义

    JavaScript 标准文档只给出了一种获取 [[Class]] 值的方法,那就是使用 Object.prototype.toString

    @@ -1236,7 +1247,7 @@

    中文翻译

    上面例子中,Object.prototype.toString 方法被调用,this 被设置为了需要获取 [[Class]] 值的对象。

    -

    译者注Object.prototype.toString 返回一种标准格式字符串,所以上例可以通过 slice 截取指定位置的字符串,如下所示:

    +

    译者注Object.prototype.toString 返回一种标准格式字符串,所以上例可以通过 slice 截取指定位置的字符串,如下所示:

    Object.prototype.toString.call([])  // "[object Array]"
     Object.prototype.toString.call({})  // "[object Object]"
    @@ -1247,7 +1258,7 @@ 

    中文翻译

    其返回值由 Object 变成了 NullUndefined

    -

    译者注:这种变化可以从 IE8 和 Firefox 4 中看出区别,如下所示:

    +

    译者注这种变化可以从 IE8 和 Firefox 4 中看出区别,如下所示:

    // IE8
     Object.prototype.toString.call(null)    // "[object Object]"
    @@ -1258,7 +1269,7 @@ 

    中文翻译

    Object.prototype.toString.call(undefined) // "[object Undefined]"
    -

    测试为定义变量(Testing for undefined variables)

    +

    测试为定义变量

    typeof foo !== 'undefined'
     
    @@ -1266,16 +1277,16 @@

    中文翻译

    上面代码会检测 foo 是否已经定义;如果没有定义而直接使用会导致 ReferenceError 的异常。 这是 typeof 唯一有用的地方。

    -

    结论(In conclusion)

    +

    结论

    为了检测一个对象的类型,强烈推荐使用 Object.prototype.toString 方法; 因为这是唯一一个可依赖的方式。正如上面表格所示,typeof 的一些返回值在标准文档中并未定义, 因此不同的引擎实现可能不同。

    -

    除非为了检测一个变量是否已经定义,我们应尽量避免使用 typeof 操作符。

     instanceof 操作符

    instanceof 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 +

    除非为了检测一个变量是否已经定义,我们应尽量避免使用 typeof 操作符。

    instanceof 操作符

    instanceof 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 如果用来比较内置类型,将会和 typeof 操作符 一样用处不大。

    -

    比较自定义对象(Comparing custom objects)

    +

    比较自定义对象

    function Foo() {}
     function Bar() {}
    @@ -1289,7 +1300,7 @@ 

    中文翻译

    new Bar() instanceof Foo; // false
    -

    instanceof 比较内置类型(Using instanceof with native types)

    +

    instanceof 比较内置类型

    new String('foo') instanceof String; // true
     new String('foo') instanceof Object; // true
    @@ -1301,10 +1312,10 @@ 

    中文翻译

    有一点需要注意,instanceof 用来比较属于不同 JavaScript 上下文的对象(比如,浏览器中不同的文档结构)时将会出错, 因为它们的构造函数不会是同一个对象。

    -

    结论(In conclusion)

    +

    结论

    instanceof 操作符应该仅仅用来比较来自同一个 JavaScript 上下文的自定义对象。 -正如 typeof 操作符一样,任何其它的用法都应该是避免的。

     类型转换

    JavaScript 是弱类型语言,所以会在任何可能的情况下应用强制类型转换

    +正如 typeof 操作符一样,任何其它的用法都应该是避免的。

    类型转换

    JavaScript 是弱类型语言,所以会在任何可能的情况下应用强制类型转换

    // 下面的比较结果是:true
     new Number(10) == 10; // Number.toString() 返回的字符串被再次转换为数字
    @@ -1327,7 +1338,7 @@ 

    中文翻译

    为了避免上面复杂的强制类型转换,强烈推荐使用严格的等于操作符。 虽然这可以避免大部分的问题,但 JavaScript 的弱类型系统仍然会导致一些其它问题。

    -

    内置类型的构造函数(Constructors of built-in types)

    +

    内置类型的构造函数

    内置类型(比如 NumberString)的构造函数在被调用时,使用或者不使用 new 的结果完全不同。

    @@ -1343,21 +1354,21 @@

    中文翻译

    最好的选择是把要比较的值显式的转换为三种可能的类型之一。

    -

    转换为字符串(Casting to a string)

    +

    转换为字符串

    '' + 10 === '10'; // true
     

    将一个值加上空字符串可以轻松转换为字符串类型。

    -

    转换为数字(Casting to a number)

    +

    转换为数字

    +'10' === 10; // true
     

    使用一元的加号操作符,可以把字符串转换为数字。

    -

    译者注:字符串转换为数字的常用方法:

    +

    译者注字符串转换为数字的常用方法:

    +'010' === 10
     Number('010') === 10
    @@ -1368,7 +1379,7 @@ 

    中文翻译

    parseInt('010.2', 10) === 10
    -

    转换为布尔型(Casting to a boolean)

    +

    转换为布尔型

    通过使用 操作符两次,可以把一个值转换为布尔型。

    @@ -1379,7 +1390,7 @@

    中文翻译

    !!'-1' // true !!{}; // true !!true; // true -

    核心

     为什么不要使用 eval

    eval 函数会在当前作用域中执行一段 JavaScript 代码字符串。

    +

    核心

    为什么不要使用 eval

    eval 函数会在当前作用域中执行一段 JavaScript 代码字符串。

    var foo = 1;
     function test() {
    @@ -1404,7 +1415,7 @@ 

    中文翻译

    foo; // 3
    -

    译者注:上面的代码等价于在全局作用域中调用 eval,和下面两种写法效果一样:

    +

    译者注上面的代码等价于在全局作用域中调用 eval,和下面两种写法效果一样:

    // 写法一:直接调用全局作用域下的 foo 变量
     var foo = 1;
    @@ -1416,7 +1427,7 @@ 

    中文翻译

    test(); // 2 foo; // 3 -// 写法二:使用 call 函数修改 `eval` 执行的上下文为全局作用域 +// 写法二:使用 call 函数修改 eval 执行的上下文为全局作用域 var foo = 1; function test() { var foo = 2; @@ -1429,23 +1440,23 @@

    中文翻译

    任何情况下我们都应该避免使用 eval 函数。99.9% 使用 eval 的场景都有不使用 eval 的解决方案。

    -

    伪装的 evaleval in disguise)

    +

    伪装的 eval

    定时函数 setTimeoutsetInterval 都可以接受字符串作为它们的第一个参数。 这个字符串总是在全局作用域中执行,因此 eval 在这种情况下没有被直接调用。

    -

    安全问题(Security issues)

    +

    安全问题

    eval 也存在安全问题,因为它会执行任意传给它的代码, 在代码字符串未知或者是来自一个不信任的源时,绝对不要使用 eval 函数。

    -

    结论(In conclusion)

    +

    结论

    绝对不要使用 eval,任何使用它的代码都会在它的工作方式,性能和安全性方面受到质疑。 如果一些情况必须使用到 eval 才能正常工作,首先它的设计会受到质疑,这不应该是首选的解决方案, -一个更好的不使用 eval 的解决方案应该得到充分考虑并优先采用。

     undefinednull

    JavaScript 有两个表示 的值,其中比较有用的是 undefined

    +一个更好的不使用 eval 的解决方案应该得到充分考虑并优先采用。

    undefinednull

    JavaScript 有两个表示‘空’的值,其中比较有用的是 undefined

    -

    undefined的值(The value undefined

    +

    undefined 的值

    undefined 是一个值为 undefined 的类型。

    @@ -1467,7 +1478,7 @@

    中文翻译

  • 任何被设置为 undefined 值的变量。
  • -

    处理 undefined 值的改变(Handling changes to the value of undefined

    +

    处理 undefined 值的改变

    由于全局变量 undefined 只是保存了 undefined 类型实际的副本, 因此对它赋新值不会改变类型 undefined 的值。

    @@ -1496,14 +1507,16 @@

    中文翻译

    这里唯一的区别是,在压缩后并且函数内没有其它需要使用 var 声明变量的情况下,这个版本的代码会多出 4 个字节的代码。

    -

    译者注:这里有点绕口,其实很简单。 -如果此函数内没有其它需要声明的变量,那么 var 总共 4 个字符(包含一个空白字符)就是专门为 undefined 变量准备的,相比上个例子多出了 4 个字节。

    + -

    使用 null(Uses of null

    +

    null 的用处

    JavaScript 中的 undefined 的使用场景类似于其它语言中的 null,实际上 JavaScript 中的 null 是另外一种数据类型。

    -

    它在 JavaScript 内部有一些使用场景(比如声明原型链的终结 Foo.prototype = null),但是大多数情况下都可以使用 undefined 来代替。

     自动分号插入

    尽管 JavaScript 有 C 的代码风格,但是它强制要求在代码中使用分号,实际上可以省略它们。

    +

    它在 JavaScript 内部有一些使用场景(比如声明原型链的终结 Foo.prototype = null),但是大多数情况下都可以使用 undefined 来代替。

    自动分号插入

    尽管 JavaScript 有 C 的代码风格,但是它强制要求在代码中使用分号,实际上可以省略它们。

    JavaScript 不是一个没有分号的语言,恰恰相反上它需要分号来就解析源代码。 因此 JavaScript 解析器在遇到由于缺少分号导致的解析错误时,会自动在源代码中插入分号。

    @@ -1522,7 +1535,7 @@

    中文翻译

    自动的分号插入被认为是 JavaScript 语言最大的设计缺陷之一,因为它改变代码的行为。

    -

    工作原理(How it works)

    +

    工作原理

    下面的代码没有分号,因此解析器需要自己判断需要在哪些地方插入分号。

    @@ -1550,7 +1563,6 @@

    中文翻译

    (function(window) { window.someLibrary = {} - })(window)
    @@ -1559,7 +1571,7 @@

    中文翻译

    (function(window, undefined) {
         function test(options) {
     
    -        // Not inserted, lines got merged
    +        // 没有插入分号,两行被合并为一行
             log('testing!')(options.list || []).forEach(function(i) {
     
             }); // <- 插入分号
    @@ -1571,17 +1583,14 @@ 

    中文翻译

    return; // <- 插入分号, 改变了 return 表达式的行为 { // 作为一个代码段处理 - - // a label and a single expression statement foo: function() {} }; // <- 插入分号 } window.test = test; // <- 插入分号 -// The lines got merged again +// 两行又被合并了 })(window)(function(window) { window.someLibrary = {}; // <- 插入分号 - })(window); //<- 插入分号
    @@ -1592,7 +1601,7 @@

    中文翻译

    解析器显著改变了上面代码的行为,在另外一些情况下也会做出错误的处理

    -

    前置括号(Leading parenthesis)

    +

    前置括号

    在前置括号的情况下,解析器不会自动插入分号。

    @@ -1607,14 +1616,14 @@

    中文翻译

    log 函数的执行结果极大可能不是函数;这种情况下就会出现 TypeError 的错误,详细错误信息可能是 undefined is not a function

    -

    结论(In conclusion)

    +

    结论

    建议绝对不要省略分号,同时也提倡将花括号和相应的表达式放在一行, 对于只有一行代码的 if 或者 else 表达式,也不应该省略花括号。 -这些良好的编程习惯不仅可以提到代码的一致性,而且可以防止解析器改变代码行为的错误处理。

    其它

     setTimeoutsetInterval

    由于 JavaScript 是异步的,可以使用 setTimeoutsetInterval 来计划执行函数。

    +这些良好的编程习惯不仅可以提到代码的一致性,而且可以防止解析器改变代码行为的错误处理。

    其它

    setTimeoutsetInterval

    由于 JavaScript 是异步的,可以使用 setTimeoutsetInterval 来计划执行函数。

    function foo() {}
    @@ -1646,7 +1655,7 @@ 

    中文翻译

    大部分情况下,这是一个潜在的错误,因为如果函数返回 undefinedsetTimeout不会报错。

    -

    setInterval 的堆调用(Stacking calls with setInterval

    +

    setInterval 的堆调用

    setTimeout 只会执行回调函数一次,不过 setInterval - 正如名字建议的 - 会每隔 X 毫秒执行函数一次。 但是却不鼓励使用这个函数。

    @@ -1664,7 +1673,7 @@

    中文翻译

    foo 被阻塞的时候,setInterval 仍然在组织将来对回调函数的调用。 因此,当第一次 foo 函数调用结束时,已经有 10 次函数调用在等待执行。

    -

    处理可能的阻塞调用(Dealing with possible blocking code)

    +

    处理可能的阻塞调用

    最简单也是最容易控制的方案,是在回调函数内部使用 setTimeout 函数。

    @@ -1678,7 +1687,7 @@

    中文翻译

    这样不仅封装了 setTimeout 回调函数,而且阻止了调用指令的堆积,可以有更多的控制。 foo 函数现在可以控制是否继续执行还是终止执行。

    -

    手工清空定时器(Manually clearing timeouts)

    +

    手工清空定时器

    可以通过将定时时产生的 ID 标识传递给 clearTimeout 或者 clearInterval 函数来清除定时, 至于使用哪个函数取决于调用的时候使用的是 setTimeout 还是 setInterval

    @@ -1687,7 +1696,7 @@

    中文翻译

    clearTimeout(id);
    -

    清除所有定时器(Clearing all timeouts)

    +

    清除所有定时器

    由于没有内置的清除所有定时器的方法,可以采用一种暴力的方式来达到这一目的。

    @@ -1697,10 +1706,10 @@

    中文翻译

    } -

    可能还有些定时器不会在上面代码中被清除(译者注:如果定时器调用时返回的 ID 值大于 1000), +

    可能还有些定时器不会在上面代码中被清除(译者注如果定时器调用时返回的 ID 值大于 1000), 因此我们可以事先保存所有的定时器 ID,然后一把清除。

    -

    隐藏使用 eval(Hidden use of eval

    +

    隐藏使用 eval

    setTimeoutsetInterval 也接受第一个参数为字符串的情况。 这个特性绝对不要使用,因为它在内部使用了 eval

    @@ -1742,10 +1751,10 @@

    中文翻译

    -

    结论(In conclusion)

    +

    结论

    绝对不要使用字符串作为 setTimeout 或者 setInterval 的第一个参数, 这么写的代码明显质量很差。当需要向回调函数传递参数时,可以创建一个匿名函数,在函数内执行真实的回调函数。

    From c097ee8cd30b173a6e717f4ed60c714b9ea086f6 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Thu, 24 Mar 2011 22:56:26 +0800 Subject: [PATCH 110/641] Updated sidebar icons --- site/image/sidebar-icon.png | Bin 3024 -> 3351 bytes site/style/garden.css | 10 ++++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/site/image/sidebar-icon.png b/site/image/sidebar-icon.png index a2cdc5952d8a936931455680e160a108ec1096e3..4b0b4dd0eb52135f029f640d6d216fbd5acd3c4b 100644 GIT binary patch delta 2556 zcmVH#0+%Gy{HrK9^l-000R|Nkl{EI|@1V`3SG zBH9lGLD@94Novd{+HBLN4J@sp%^%D(>-9bOx%GWt&$;*9_k8Ie_rPcO-Fwgd+;i_c z=llJh&-uQ8^^3>jp>&mg7UY?PazZgXwwG|&lUQc#W{ZrseC=va5tuE-fp@lR2eP){z%9qGB(h(&d$L%tS*}iJ8d9 zvKEiGBz7fk4Xn}Xk3&8H+be=(31JjrpE$1i_TPl1;`k+Ovp9JVwmG8m3usNHI9?3$ z=fv@U9M0#EAHjB%sQe@0CqlM3uCD7PbO;;dT>pa{p&j@#ip7_wy5KK_=doWwI7rwb zdP~gITS72Gs%#YG4-obdR$zZRp_A|wt22N#ZkCQbk9DPbpCfb?uG{HmTWZ;0T8tLC zk>)T%7l)ZPl5^wb*z77z9W{7r2){8ySK?)V6+`Sx+sSTn1|zZw+dg(q*`$0OkJr1L z&kT|*O)$41zn(~z!GsYQ@@LVH`)-7TPOJA1$OH0#{8lAbQNa_03dHED{6T0z(-da>rmmO+dRhr7299N^<#D9yKr(kJ2sKM*NHebmmMo)byv#PJ126P zBhJ*2%a#^u$ctIT5aJ=U(}%x8D4a~`1d5$Gt3zIZ?THizqLWtk(pl`Ebb2jnqx=^CGVh-o-20Rq^3| z79Ec;k*j#69TgY*@cfx9uHR-NUm0Yj`bxT}-iazTn$9`E=H;bWan9PA>2nKFbO6QXP4JC2M_&PvRIe z0fS7oWi6{@dxI+0zh`+_Md$G@pt4Crj}VrzdC*$hD0dy97N3;f(hj-Xmww(NLg^SnyAo_0nBeBrxx%b8s>YkZTrd4RoS=9Ar++7Z+w#OW;Gv)UL9UH{ zUY#WQQrRi^$p*G;=64W{WcqozY4|s>X}0a~&tqM1otNP{j*7=2G`JDhApOL#M+0q4 z?lv}9)?pnZidk|Llj0_^WO=6_{d_TdkEU|WEe*xX&8dDHgPR+Qd8V3Ueh)XN5+E-H zp+!I*kO$-`kVmn9y~H%Jl=Slkm|tlfdl1>yB%_~~YqI@Xfp$&yUy&{O6YRo6aM;=_|GhOdne8;GLdI9|2fa6_unWLXi2_OC$L*03jtL^3QNeOAPdxM zk_aT~nC@kzpZh^X>gP_BTZZjk-B4gCguLzS^ItTmh3=fdzRpS|8H1d?dDACjESnVO1rr z3)vXTSPs~AU2%(MWEQ&p0>m&uUZx{2XRR;w*$r#i%p~HapPw>hZ-)#6?6NLzT~}HD z7$(~#=;xt-_8QwLcRpberhXUELk?mcNcE_((9bi`(-smw_2Y>~iI(ZgMzk9r3T=UcccnxPAQ4P^%56^zVDhC0rO&E_Gr zO^Wz2!h0AjXIxB$Dm{D<+02O+5-t*+cA3#>=R}7wyh|{&b>%Wd1=CtwHPR!Lspyl# z|3=M!L+k$c8FX6b{<<5qLsN-p%t3=+)xu40IRw4scq)2}{6)MnNaqCMX}pf=8}Sp) z5W49A%#u$8p+!I*kO$-`kms|tIhA`poX~B%Hs`gf*qh3#qRo2fdi*FU3TZp(_0Y7g zZo_cX$@B01H#?(da921!q>QY9^&r)p=}6V2^$D|2{#GiPA(I^ zB6JeszGsU&*C3CO&e+=(LKk6%jz>qljBp$$Un9Irs3v6NcrKxi(8DgWo3O-2E;^jS z3Ht~IhHoZOLO6x~gM^3t$otW;pWUeq;+q`Bc|9ibR&+Rv6XgB`VHC#_KnD8~=o7~y z6s> zGjtRutS96cNJUWlkVVFBwNK2DiMk0b_l>|c#@|0Wdb$otS?J=>RJKVAy^I@rDh>)VTc zZ>r=onPyq2Ay@A-p&_rtz9CioKaH)(jTb|XnQxF&pC`=YBQ(w`gs|;)PzfF80{`*M zQ0_QB4dN_z{;!<;2W-m-3qz7u5ON3y2v<1yEy9<$dYdi;p^^}-Ln&2cTZez`plNZcp(FB$JR*P4 z^m!4?%okvk6(axF1)nkdI+$%e-0!cW5{3BMhJg zHu6UaCt0$svgFSuC|l{F&+;ReO809Y`$@%O=An0+$QxNjk*_h;gr(h}5KEi8n3-rv z9l3mlZ3=;u(AzY3ox;UEOh05`s!buE%jPKNu#=M@O+obQ9?K}xFH?WKMcJW>WpjQa zFBKbbk&A=@_KgNu{|ngvu=raJqQ%&bE5l$jG~yVuwKv1@k7+naU{?syfYhx>niK=~{oLq?~Lyo?#dIAl0cH|x@&u>z&PIFCzco5j7{(SaVvYbqr4Pp%gC zF#KGO4o$zCTuOh-JC~+<;WX|%%Os(BO_Y1Leo*;Xu6h41Sre7T>X1RO#n0ld)6Mlb zqJjz1BGd5iW#!&NN8%zj!`0;%Tjpb?FoR`ow%Wgum4|5;*J2S{<|b>LHWa)`3^)-j zk0`FiY!`Cwx16*ji-VeS-dRFt!=x8Z(Q|CT4M9ThiX?w@L>`ex!MBVh`L76C0X<-wnq~ZMJcvz%5-Lry2x5bp2wt{$crl-mTyW4 z*JmS7GtKClpyP0^eG_y&TOV2wP+BCJv?$S$%W`Fn&+65L&jq?vUs5OMaze_vGWG^d zA+Kbsn>>GwInNi%w6ecd`*n)NZCl0d!OG@ta|$+KCoDhJ;`X4=LoGph5ze!4px<(_ zxNZ8Jw*hZ*QO6b+ea;K60>i{dMmN!<)T5&9SM|AAg0dY~U(4=BuGk*HU0(9}M(uw3 zyn>bGWdUEq6-*iD+AQv~d@?D3#oca;ihb<)Z?u0{+&#(!p_9t(@+SX5V;mOuF?PHP z7PnX@eHE|drXb0p%F*JEPsidegAMhRVsY z{HIfgmfGsjds;!?^V;9!S~M!I#p*Dw#a%A-TWl&i_@%07*qoM6N<$g1r$COaK4? diff --git a/site/style/garden.css b/site/style/garden.css index 407cf37d..9af0392d 100644 --- a/site/style/garden.css +++ b/site/style/garden.css @@ -142,9 +142,10 @@ 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 -90px; } -nav li.nav_core h1 a { background-position: 5px -122px; } -nav li.nav_other h1 a { background-position: 5px -153px; } +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; @@ -286,9 +287,10 @@ nav li.nav_intro h1 a:after { content: 'i'; font-style: italic; } #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: 100px; + font-size: 130px; } #function:after, nav li.nav_function h1 a:after { From e8dbe140db458558c8566b0896717b7b22b4cd4d Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Fri, 25 Mar 2011 18:43:44 +0800 Subject: [PATCH 111/641] More corrects --- doc/zh/object/prototype.md | 4 ++-- doc/zh/types/equality.md | 6 ++---- doc/zh/types/instanceof.md | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/doc/zh/object/prototype.md b/doc/zh/object/prototype.md index 5a0484a2..9e70fc52 100755 --- a/doc/zh/object/prototype.md +++ b/doc/zh/object/prototype.md @@ -67,7 +67,7 @@ of `Foo`. But this constructor has to be called explicitly.) 然而将原子类型赋给 prototype 的操作将会被忽略。 function Foo() {} - Foo.prototype = 1; // no effect + Foo.prototype = 1; // 无效 而将对象赋值给 prototype,正如上面的例子所示,将会动态的创建原型链。 @@ -75,7 +75,7 @@ of `Foo`. But this constructor has to be called explicitly.) 如果一个属性在原型链的上端,则对于查找时间将带来不利影响。特别的,试图获取一个不存在的属性将会遍历整个原型链。 -并且,当使用 [for-in](#the-for-in-loop) 循环遍历对象的属性时,原型链上的**所有**属性都将被访问。 +并且,当使用 [`for in`](#the-for-in-loop) 循环遍历对象的属性时,原型链上的**所有**属性都将被访问。 ###扩展内置类型的原型 diff --git a/doc/zh/types/equality.md b/doc/zh/types/equality.md index 637d7bd6..eef0e9da 100755 --- a/doc/zh/types/equality.md +++ b/doc/zh/types/equality.md @@ -27,7 +27,7 @@ JavaScript 是*弱类型*语言,这就意味着,等于操作符会为了比 严格等于操作符由**三**个等号组成:`===` -不想普通的等于操作符,严格的等于操作符**不会**进行强制类型转换。 +不想普通的等于操作符,严格等于操作符**不会**进行强制类型转换。 "" === "0" // false 0 === "" // false @@ -56,9 +56,7 @@ JavaScript 是*弱类型*语言,这就意味着,等于操作符会为了比 ###结论 -强烈推荐使用**严格的等于操作符**。如果类型需要转换,应该在比较之前[显式](#types.casting)的转换, +强烈推荐使用**严格等于操作符**。如果类型需要转换,应该在比较之前[显式](#types.casting)的转换, 而不是使用语言本身复杂的强制转换规则。 - - [30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/types/instanceof.md b/doc/zh/types/instanceof.md index b7aae51e..191c43c4 100755 --- a/doc/zh/types/instanceof.md +++ b/doc/zh/types/instanceof.md @@ -1,7 +1,7 @@ ##`instanceof` 操作符 `instanceof` 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 -如果用来比较内置类型,将会和 [typeof 操作符](#types.typeof) 一样用处不大。 +如果用来比较内置类型,将会和 [`typeof` 操作符](#types.typeof) 一样用处不大。 ###比较自定义对象 From b9cfa247b80f23f8d2ea1f94e45b14d75d5e85d5 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Fri, 25 Mar 2011 21:52:40 +0800 Subject: [PATCH 112/641] Fixed broken links, outdated translation --- doc/zh/function/closures.md | 4 +- doc/zh/function/general.md | 6 +-- doc/zh/function/scopes.md | 16 +++---- doc/zh/function/this.md | 7 ++- doc/zh/intro/index.md | 3 ++ doc/zh/intro/license.md | 2 +- doc/zh/object/general.md | 4 +- doc/zh/object/hasownproperty.md | 6 +-- doc/zh/object/prototype.md | 19 +++----- site/zh/index.html | 78 ++++++++++++++++----------------- 10 files changed, 69 insertions(+), 76 deletions(-) diff --git a/doc/zh/function/closures.md b/doc/zh/function/closures.md index f3db29d1..b547be0d 100755 --- a/doc/zh/function/closures.md +++ b/doc/zh/function/closures.md @@ -1,7 +1,7 @@ ##闭包和引用 闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域**总是**能够访问外部作用域中的变量。 -因为 [函数](#scopes) 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。 +因为 [函数](#function.scopes) 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。 ###模拟私有变量 @@ -56,7 +56,7 @@ ###避免引用错误 -为了正确的获得循环序号,最好使用 [匿名包裹器](#scopes)(**[译者注][30]:**其实就是我们通常说的自执行匿名函数)。 +为了正确的获得循环序号,最好使用 [匿名包裹器](#function.scopes)(**[译者注][30]:**其实就是我们通常说的自执行匿名函数)。 for(var i = 0; i < 10; i++) { (function(e) { diff --git a/doc/zh/function/general.md b/doc/zh/function/general.md index 6e759c89..41a63a3d 100755 --- a/doc/zh/function/general.md +++ b/doc/zh/function/general.md @@ -7,7 +7,7 @@ function foo() {} -上面的方法会在执行前被 [解析(hoisted)](#scopes),因此它存在于当前上下文的*任意*一个地方, +上面的方法会在执行前被 [解析(hoisted)](#function.scopes),因此它存在于当前上下文的*任意*一个地方, 即使在函数定义体的上面被调用也是对的。 foo(); // 正常运行,因为foo在代码运行前已经被创建 @@ -25,7 +25,7 @@ 由于 `var` 定义了一个声明语句,对变量 `foo` 的解析是在代码运行之前,因此 `foo` 变量在代码运行时已经被定义过了。 -但是由于赋值语句只在运行时执行,因此在相应代码执行之前, `foo` 的值缺省为 [undefined](#undefined)。 +但是由于赋值语句只在运行时执行,因此在相应代码执行之前, `foo` 的值缺省为 [undefined](#core.undefined)。 ###命名函数的赋值表达式 @@ -37,7 +37,7 @@ bar(); // 出错:ReferenceError `bar` 函数声明外是不可见的,这是因为我们已经把函数赋值给了 `foo`; -然而在 `bar` 内部依然可见。这是由于 JavaScript 的 [命名处理](#scopes) 所致, +然而在 `bar` 内部依然可见。这是由于 JavaScript 的 [命名处理](#function.scopes) 所致, 函数名在函数内*总是*可见的。 [30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/function/scopes.md b/doc/zh/function/scopes.md index b644b677..4a66188d 100755 --- a/doc/zh/function/scopes.md +++ b/doc/zh/function/scopes.md @@ -11,7 +11,7 @@ } > **注意:** 如果不是在赋值语句中,而是在 return 表达式或者函数参数中,`{...}` 将会作为代码段解析, -> 而不是作为对象的字面语法解析。如果考虑到 [自动分号插入](#semicolon),这可能会导致一些不易察觉的错误。 +> 而不是作为对象的字面语法解析。如果考虑到 [自动分号插入](#core.semicolon),这可能会导致一些不易察觉的错误。 **[译者注][30]:**如果 `return` 对象的左括号和 `return` 不在一行上就会出错。 @@ -52,7 +52,7 @@ JavaScript 中没有显式的命名空间定义,这就意味着所有对象都 起初这看起来并不是大问题,但是当有成千上万行代码时,不使用 `var` 声明变量将会带来难以跟踪的 BUG。 // 全局作用域 - var items = [/* some list */]; + var items = [/* 数组 */]; for(var i = 0; i < 10; i++) { subLoop(); } @@ -60,7 +60,7 @@ JavaScript 中没有显式的命名空间定义,这就意味着所有对象都 function subLoop() { // subLoop 函数作用域 for(i = 0; i < 10; i++) { // 没有使用 var 声明变量 - // do amazing stuff! + // 干活 } } @@ -70,7 +70,7 @@ JavaScript 中没有显式的命名空间定义,这就意味着所有对象都 ###局部变量 -JavaScript 中局部变量只可能通过两种方式声明,一个是作为[函数](#functions)参数,另一个是通过 `var` 关键字声明。 +JavaScript 中局部变量只可能通过两种方式声明,一个是作为[函数](#function)参数,另一个是通过 `var` 关键字声明。 // 全局变量 var foo = 1; @@ -88,7 +88,7 @@ JavaScript 中局部变量只可能通过两种方式声明,一个是作为[ `foo` 和 `i` 是函数 `test` 内的局部变量,而对 `bar` 的赋值将会覆盖全局作用域内的同名变量。 -### 变量声明提升(Hoisting) +###变量声明提升(Hoisting) JavaScript 会**提升**变量声明。这意味着 `var` 表达式和 `function` 声明都将会被提升到当前作用域的顶部。 @@ -170,9 +170,9 @@ JavaScript 会**提升**变量声明。这意味着 `var` 表达式和 `function ###名称解析顺序 -JavaScript 中的所有作用域,包括*全局作用域*,都有一个特别的名称 [`this`](#this) 指向当前对象。 +JavaScript 中的所有作用域,包括*全局作用域*,都有一个特别的名称 [`this`](#function.this) 指向当前对象。 -函数作用域内也有默认的变量 [`arguments`](#arguments),其中包含了传递到函数中的参数。 +函数作用域内也有默认的变量 [`arguments`](#function.arguments),其中包含了传递到函数中的参数。 比如,当访问函数内的 `foo` 变量时,JavaScript 会按照下面顺序查找: @@ -196,7 +196,7 @@ JavaScript 中的所有作用域,包括*全局作用域*,都有一个特别 })(); // 立即执行此匿名函数 -匿名函数被认为是 [表达式](#functions);因此为了可调用性,它们首先会被执行。 +匿名函数被认为是 [表达式](#function);因此为了可调用性,它们首先会被执行。 ( // 小括号内的函数首先被执行 function() {} diff --git a/doc/zh/function/this.md b/doc/zh/function/this.md index 494ed98c..dd700690 100755 --- a/doc/zh/function/this.md +++ b/doc/zh/function/this.md @@ -19,7 +19,6 @@ JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 > **ES5 注意:** 在严格模式下(strict mode),不存在全局变量。 > 这种情况下 `this` 将会是 `undefined`。 -> [译者注][30]:ES5指的是ECMAScript 5,是 2009-12 发布的最新的 JavaScript 版本。 ###方法调用 @@ -31,7 +30,7 @@ JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 new foo(); -如果函数倾向于和 `new` 关键词一块使用,则我们称这个函数是 [构造函数](#constructors)。 +如果函数倾向于和 `new` 关键词一块使用,则我们称这个函数是 [构造函数](#function.constructors)。 在函数内部,`this` 指向*新创建*的对象。 ###显式的设置 `this` @@ -76,7 +75,7 @@ JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 } `that` 只是我们随意起的名字,不过这个名字被广泛的用来指向外部的 `this` 对象。 -在 [闭包](#closures) 一节,我们可以看到 `that` 可以作为参数传递。 +在 [闭包](#function.closures) 一节,我们可以看到 `that` 可以作为参数传递。 ###方法的赋值表达式 @@ -87,7 +86,7 @@ JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 上例中,`test` 就像一个普通的函数被调用;因此,函数内的 `this` 将不再被指向到 `someObject` 对象。 -虽然 `this` 的晚绑定特性似乎并不友好,但是这确实[基于原型继承](#prototype)赖以生存的土壤。 +虽然 `this` 的晚绑定特性似乎并不友好,但是这确实[基于原型继承](#object.prototype)赖以生存的土壤。 function Foo() {} Foo.prototype.method = function() {}; diff --git a/doc/zh/intro/index.md b/doc/zh/intro/index.md index 497110ce..0a93d9ab 100755 --- a/doc/zh/intro/index.md +++ b/doc/zh/intro/index.md @@ -7,5 +7,8 @@ JavaScript 秘密花园**不是**用来教你 JavaScript。为了更好的理解这篇文章的内容, 你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习[向导][1]。 +> **ES5 注意:** **译者注:** ES5 指的是ECMAScript 5,是 ECMAScript 标准语言的下一版本,正在开发中。 +JavaScript 是此标准语言的一个方言。 + [1]: https://developer.mozilla.org/en/JavaScript/Guide diff --git a/doc/zh/intro/license.md b/doc/zh/intro/license.md index 917a09c6..43397fb8 100755 --- a/doc/zh/intro/license.md +++ b/doc/zh/intro/license.md @@ -2,7 +2,7 @@ JavaScript 秘密花园在 [MIT license][1] 许可协议下发布,并存放在 [GitHub][2] 开源社区。 如果你发现错误或者打字错误,请[新建一个任务单][3]或者发一个抓取请求。 -你也可以在 Stack Overflow 的 [JavaScript 聊天室][4] 找到我们。 +你也可以在 Stack Overflow 的 [JavaScript 聊天室][4]找到我们。 [1]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE [2]: https://github.com/BonsaiDen/JavaScript-Garden diff --git a/doc/zh/object/general.md b/doc/zh/object/general.md index f57ff1f1..b75b0fea 100755 --- a/doc/zh/object/general.md +++ b/doc/zh/object/general.md @@ -1,6 +1,6 @@ ##对象使用和属性 -JavaScript 中所有变量都是对象,除了两个例外 [`null`](#undefined) 和 [`undefined`](#undefined)。 +JavaScript 中所有变量都是对象,除了两个例外 [`null`](#core.undefined) 和 [`undefined`](#core.undefined)。 false.toString() // 'false' [1, 2, 3].toString(); // '1,2,3' @@ -25,7 +25,7 @@ JavaScript 中所有变量都是对象,除了两个例外 [`null`](#undefined) JavaScript 的对象可以作为[*哈希表*][1]使用,主要用来保存命名的键与值的对应关系。 使用对象的字面语法 - `{}` - 可以创建一个简单对象。这个新创建的对象从 `Object.prototype` -[继承](#prototype)下面,没有任何[自定义属性](#hasownproperty)。 +[继承](#object.prototype)下面,没有任何[自定义属性](#object.hasownproperty)。 var foo = {}; // 一个空对象 diff --git a/doc/zh/object/hasownproperty.md b/doc/zh/object/hasownproperty.md index 8abefd14..935d7aa0 100755 --- a/doc/zh/object/hasownproperty.md +++ b/doc/zh/object/hasownproperty.md @@ -1,6 +1,6 @@ ##`hasOwnProperty` 函数 -为了判断一个对象是否包含*自定义*属性而*不是*[原型链](#prototype)上的属性, +为了判断一个对象是否包含*自定义*属性而*不是*[原型链](#object.prototype)上的属性, 我们需要使用继承自 `Object.prototype` 的 `hasOwnProperty` 方法。 > **注意:** 通过判断一个属性是否 `undefined` 是**不够**的。 @@ -41,7 +41,7 @@ JavaScript **不会**保护 `hasOwnProperty` 被非法占用,因此如果一 ###结论 当检查对象上某个属性是否存在时,`hasOwnProperty` 是**唯一**可用的方法。 -同时在使用 [`for in` loop](#forinloop) 遍历对象时,推荐**总是**使用 `hasOwnProperty` 方法, -这将会避免[原型](#prototype)对象扩展带来的干扰。 +同时在使用 [`for in` loop](#object.forinloop) 遍历对象时,推荐**总是**使用 `hasOwnProperty` 方法, +这将会避免[原型](#object.prototype)对象扩展带来的干扰。 [30]: http://cnblogs.com/sanshi/ diff --git a/doc/zh/object/prototype.md b/doc/zh/object/prototype.md index 9e70fc52..a02c4442 100755 --- a/doc/zh/object/prototype.md +++ b/doc/zh/object/prototype.md @@ -40,17 +40,11 @@ other way around is a far more difficult task.) {method: ...}; Object.prototype {toString: ... /* etc. */}; - +* 上面的例子中,`test` 对象从 `Bar.prototype` 和 `Foo.prototype` 继承下来;因此, -它能否访问 `Foo` 的原型方法 `method`。但是它不能访问 `Foo` 的实例属性 `value`, -因为这个属性在`Foo`的[构造函数](#constructor)中定义。 -(But it will not have access to the property `value` of a -`Foo` instance, since that property gets defined in the [constructor](#constructor) -of `Foo`. But this constructor has to be called explicitly.) - -> **[译者注][30]:**我认为这个描述是错误的,`test.value` 是可以访问的。 -因为在设置 `Bar.prototype = new Foo();` 时,`value` 也就成为 `Bar.prototype` 上的一个属性。 -如果你有不同观点,可以到[我的博客][30]评论。 +它能访问 `Foo` 的原型方法 `method`。它也同时能够访问那**一个**作为它原型的 `Foo` 实例 +的属性 `value`。需要注意的是 `new Bar()` **不会**创造出一个新的 `Foo` 实例,而是 +重新使用设定为它的原型的实例;也就是说,所有的 `Bar` 实例都将会有**同样**的 `value` 属性。 > **注意:** **不要**使用 `Bar.prototype = Foo`,因为这不会执行 `Foo` 的原型,而是指向函数 `Foo`。 > 因此原型链将会回溯到 `Function.prototype` 而不是 `Foo.prototype`,因此 `method` 将不会在 Bar 的原型链上。 @@ -59,7 +53,7 @@ of `Foo`. But this constructor has to be called explicitly.) 当查找一个对象的属性时,JavaScript 会**向上**遍历原型链,直到找到给定名称的属性为止。 -到查找到达原型链的顶部 - 也就是 `Object.prototype` - 但是仍然没有找到指定的属性,就会返回 [undefined](#undefined)。 +到查找到达原型链的顶部 - 也就是 `Object.prototype` - 但是仍然没有找到指定的属性,就会返回 [undefined](#core.undefined)。 ###原型属性 @@ -75,7 +69,7 @@ of `Foo`. But this constructor has to be called explicitly.) 如果一个属性在原型链的上端,则对于查找时间将带来不利影响。特别的,试图获取一个不存在的属性将会遍历整个原型链。 -并且,当使用 [`for in`](#the-for-in-loop) 循环遍历对象的属性时,原型链上的**所有**属性都将被访问。 +并且,当使用 [`for in`](#object.forinloop) 循环遍历对象的属性时,原型链上的**所有**属性都将被访问。 ###扩展内置类型的原型 @@ -100,4 +94,3 @@ of `Foo`. But this constructor has to be called explicitly.) [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/site/zh/index.html b/site/zh/index.html index 8c55454b..e122e8a8 100644 --- a/site/zh/index.html +++ b/site/zh/index.html @@ -6,7 +6,11 @@ 初学者可以籍此深入了解 JavaScript 的语言特性。

    JavaScript 秘密花园不是用来教你 JavaScript。为了更好的理解这篇文章的内容, -你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习向导

     贡献者

      +你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习向导

      + +

    对象

    对象使用和属性

    JavaScript 中所有变量都是对象,除了两个例外 nullundefined

    +你也可以在 Stack Overflow 的 JavaScript 聊天室找到我们。

    对象

    对象使用和属性

    JavaScript 中所有变量都是对象,除了两个例外 nullundefined

    false.toString() // 'false'
     [1, 2, 3].toString(); // '1,2,3'
    @@ -47,7 +51,7 @@ 

    中文翻译

    JavaScript 的对象可以作为哈希表使用,主要用来保存命名的键与值的对应关系。

    使用对象的字面语法 - {} - 可以创建一个简单对象。这个新创建的对象从 Object.prototype -继承下面,没有任何自定义属性

    +继承下面,没有任何自定义属性

    var foo = {}; // 一个空对象
     
    @@ -157,18 +161,13 @@ 

    中文翻译

    {toString: ... /* etc. */};
    -

    上面的例子中,test 对象从 Bar.prototypeFoo.prototype 继承下来;因此, -它能否访问 Foo 的原型方法 method。但是它不能访问 Foo 的实例属性 value, -因为这个属性在Foo构造函数中定义。 -(But it will not have access to the property value of a -Foo instance, since that property gets defined in the constructor -of Foo. But this constructor has to be called explicitly.)

    +

    * +上面的例子中,test 对象从 Bar.prototypeFoo.prototype 继承下来;因此, +它能访问 Foo 的原型方法 method。它也同时能够访问那一个作为它原型的 Foo 实例 +的属性 value。需要注意的是 new Bar() 不会创造出一个新的 Foo 实例,而是 +重新使用设定为它的原型的实例;也就是说,所有的 Bar 实例都将会有同样value 属性。

    @@ -177,7 +176,7 @@

    中文翻译

    当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。

    -

    到查找到达原型链的顶部 - 也就是 Object.prototype - 但是仍然没有找到指定的属性,就会返回 undefined

    +

    到查找到达原型链的顶部 - 也就是 Object.prototype - 但是仍然没有找到指定的属性,就会返回 undefined

    原型属性

    @@ -185,7 +184,7 @@

    中文翻译

    然而将原子类型赋给 prototype 的操作将会被忽略。

    function Foo() {}
    -Foo.prototype = 1; // no effect
    +Foo.prototype = 1; // 无效
     

    而将对象赋值给 prototype,正如上面的例子所示,将会动态的创建原型链。

    @@ -194,7 +193,7 @@

    中文翻译

    如果一个属性在原型链的上端,则对于查找时间将带来不利影响。特别的,试图获取一个不存在的属性将会遍历整个原型链。

    -

    并且,当使用 for-in 循环遍历对象的属性时,原型链上的所有属性都将被访问。

    +

    并且,当使用 for in 循环遍历对象的属性时,原型链上的所有属性都将被访问。

    扩展内置类型的原型

    @@ -213,7 +212,7 @@

    中文翻译

    在写复杂的 JavaScript 应用之前,充分理解原型链继承的工作方式是每个 JavaScript 程序员必修的功课。 要提防原型链过长带来的性能问题,并知道如何通过缩短原型链来提高性能。 -更进一步,绝对不要扩展内置类型的原型,除非是为了和新的 JavaScript 引擎兼容。

    hasOwnProperty 函数

    为了判断一个对象是否包含自定义属性而不是原型链上的属性, +更进一步,绝对不要扩展内置类型的原型,除非是为了和新的 JavaScript 引擎兼容。

    hasOwnProperty 函数

    为了判断一个对象是否包含自定义属性而不是原型链上的属性, 我们需要使用继承自 Object.prototypehasOwnProperty 方法。

    结论

    当检查对象上某个属性是否存在时,hasOwnProperty唯一可用的方法。 -同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法, -这将会避免原型对象扩展带来的干扰。

    for in 循环

    in 操作符一样,for in 循环同样在查找对象属性时遍历原型链上的所有属性。

    +同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法, +这将会避免原型对象扩展带来的干扰。

    for in 循环

    in 操作符一样,for in 循环同样在查找对象属性时遍历原型链上的所有属性。

    命名函数的赋值表达式

    @@ -341,7 +340,7 @@

    中文翻译

    bar 函数声明外是不可见的,这是因为我们已经把函数赋值给了 foo; -然而在 bar 内部依然可见。这是由于 JavaScript 的 命名处理 所致, +然而在 bar 内部依然可见。这是由于 JavaScript 的 命名处理 所致, 函数名在函数内总是可见的。

    this 的工作原理

    JavaScript 有一套完全不同于其它语言的对 this 的处理机制。 在种不同的情况下 ,this 指向的各不相同。

    @@ -364,8 +363,7 @@

    中文翻译

    这里 this 也会指向全局对象。

    方法调用

    @@ -380,7 +378,7 @@

    中文翻译

    new foo(); 
     
    -

    如果函数倾向于和 new 关键词一块使用,则我们称这个函数是 构造函数。 +

    如果函数倾向于和 new 关键词一块使用,则我们称这个函数是 构造函数。 在函数内部,this 指向新创建的对象。

    显式的设置 this

    @@ -430,7 +428,7 @@

    中文翻译

    that 只是我们随意起的名字,不过这个名字被广泛的用来指向外部的 this 对象。 -在 闭包 一节,我们可以看到 that 可以作为参数传递。

    +在 闭包 一节,我们可以看到 that 可以作为参数传递。

    方法的赋值表达式

    @@ -442,7 +440,7 @@

    中文翻译

    上例中,test 就像一个普通的函数被调用;因此,函数内的 this 将不再被指向到 someObject 对象。

    -

    虽然 this 的晚绑定特性似乎并不友好,但是这确实基于原型继承赖以生存的土壤。

    +

    虽然 this 的晚绑定特性似乎并不友好,但是这确实基于原型继承赖以生存的土壤。

    function Foo() {}
     Foo.prototype.method = function() {};
    @@ -454,7 +452,7 @@ 

    中文翻译

    method 被调用时,this 将会指向 Bar 的实例对象。

    闭包和引用

    闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 -因为 函数 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。

    +因为 函数 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。

    模拟私有变量

    @@ -512,7 +510,7 @@

    中文翻译

    避免引用错误

    -

    为了正确的获得循环序号,最好使用 匿名包裹器译者注其实就是我们通常说的自执行匿名函数)。

    +

    为了正确的获得循环序号,最好使用 匿名包裹器译者注其实就是我们通常说的自执行匿名函数)。

    for(var i = 0; i < 10; i++) {
         (function(e) {
    @@ -811,7 +809,7 @@ 

    中文翻译

    译者注如果 return 对象的左括号和 return 不在一行上就会出错。

    @@ -856,7 +854,7 @@

    中文翻译

    起初这看起来并不是大问题,但是当有成千上万行代码时,不使用 var 声明变量将会带来难以跟踪的 BUG。

    // 全局作用域
    -var items = [/* some list */];
    +var items = [/* 数组 */];
     for(var i = 0; i < 10; i++) {
         subLoop();
     }
    @@ -864,7 +862,7 @@ 

    中文翻译

    function subLoop() { // subLoop 函数作用域 for(i = 0; i < 10; i++) { // 没有使用 var 声明变量 - // do amazing stuff! + // 干活 } }
    @@ -875,7 +873,7 @@

    中文翻译

    局部变量

    -

    JavaScript 中局部变量只可能通过两种方式声明,一个是作为函数参数,另一个是通过 var 关键字声明。

    +

    JavaScript 中局部变量只可能通过两种方式声明,一个是作为函数参数,另一个是通过 var 关键字声明。

    // 全局变量
     var foo = 1;
    @@ -979,9 +977,9 @@ 

    中文翻译

    名称解析顺序

    -

    JavaScript 中的所有作用域,包括全局作用域,都有一个特别的名称 this 指向当前对象。

    +

    JavaScript 中的所有作用域,包括全局作用域,都有一个特别的名称 this 指向当前对象。

    -

    函数作用域内也有默认的变量 arguments,其中包含了传递到函数中的参数。

    +

    函数作用域内也有默认的变量 arguments,其中包含了传递到函数中的参数。

    比如,当访问函数内的 foo 变量时,JavaScript 会按照下面顺序查找:

    @@ -1010,7 +1008,7 @@

    中文翻译

    })(); // 立即执行此匿名函数
    -

    匿名函数被认为是 表达式;因此为了可调用性,它们首先会被执行。

    +

    匿名函数被认为是 表达式;因此为了可调用性,它们首先会被执行。

    ( // 小括号内的函数首先被执行
     function() {}
    @@ -1155,7 +1153,7 @@ 

    中文翻译

    严格等于操作符由个等号组成:===

    -

    不想普通的等于操作符,严格的等于操作符不会进行强制类型转换。

    +

    不想普通的等于操作符,严格等于操作符不会进行强制类型转换。

    ""           ===   "0"           // false
     0            ===   ""            // false
    @@ -1186,7 +1184,7 @@ 

    中文翻译

    结论

    -

    强烈推荐使用严格的等于操作符。如果类型需要转换,应该在比较之前显式的转换, +

    强烈推荐使用严格等于操作符。如果类型需要转换,应该在比较之前显式的转换, 而不是使用语言本身复杂的强制转换规则。

    typeof 操作符

    typeof 操作符(和 instanceof 一起)或许是 JavaScript 中最大的设计缺陷, 因为几乎不可能从它们那里得到想要的结果。

    @@ -1284,7 +1282,7 @@

    中文翻译

    因此不同的引擎实现可能不同。

    除非为了检测一个变量是否已经定义,我们应尽量避免使用 typeof 操作符。

    instanceof 操作符

    instanceof 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 -如果用来比较内置类型,将会和 typeof 操作符 一样用处不大。

    +如果用来比较内置类型,将会和 typeof 操作符 一样用处不大。

    比较自定义对象

    @@ -1595,7 +1593,7 @@

    中文翻译

    From b9e1869da856e0da83c1bf51e064cb4968daedca Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Sat, 26 Mar 2011 09:26:12 +0800 Subject: [PATCH 113/641] More cleanup --- doc/zh/intro/authors.md | 2 +- doc/zh/intro/contributors.md | 4 ++-- doc/zh/intro/index.md | 2 +- doc/zh/intro/license.md | 2 +- site/zh/index.html | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/zh/intro/authors.md b/doc/zh/intro/authors.md index 427310b3..e923ba85 100755 --- a/doc/zh/intro/authors.md +++ b/doc/zh/intro/authors.md @@ -1,4 +1,4 @@ -## 关于作者 +##关于作者 这篇文章的作者是两位 [Stack Overflow][1] 用户, [伊沃·韦特泽尔 Ivo Wetzel][2](写作) 和 [张易江 Zhang Yi Jiang][3](设计)。 diff --git a/doc/zh/intro/contributors.md b/doc/zh/intro/contributors.md index 339b1c57..fd049216 100755 --- a/doc/zh/intro/contributors.md +++ b/doc/zh/intro/contributors.md @@ -1,9 +1,9 @@ -## 贡献者 +##贡献者 - [Caio Romão][1] (拼写检查) - [Andreas Blixt][2] (语言修正) -## 中文翻译 +##中文翻译 - [三生石上][29] 此中文翻译由[三生石上][29]独立完成,[博客园][30]首发,转载请注明出处。 diff --git a/doc/zh/intro/index.md b/doc/zh/intro/index.md index 0a93d9ab..93a4c73c 100755 --- a/doc/zh/intro/index.md +++ b/doc/zh/intro/index.md @@ -1,4 +1,4 @@ -## 简介 +##简介 **JavaScript 秘密花园**是一个不断更新,主要关心 JavaScript 一些古怪用法的文档。 对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, diff --git a/doc/zh/intro/license.md b/doc/zh/intro/license.md index 43397fb8..33aa7406 100755 --- a/doc/zh/intro/license.md +++ b/doc/zh/intro/license.md @@ -1,4 +1,4 @@ -## 许可 +##许可 JavaScript 秘密花园在 [MIT license][1] 许可协议下发布,并存放在 [GitHub][2] 开源社区。 如果你发现错误或者打字错误,请[新建一个任务单][3]或者发一个抓取请求。 diff --git a/site/zh/index.html b/site/zh/index.html index e122e8a8..2e24f7b9 100644 --- a/site/zh/index.html +++ b/site/zh/index.html @@ -1,7 +1,7 @@ JavaScript 秘密花园 -

    简介

    JavaScript 秘密花园是一个不断更新,主要关心 JavaScript 一些古怪用法的文档。 +

    简介

    JavaScript 秘密花园是一个不断更新,主要关心 JavaScript 一些古怪用法的文档。 对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, 初学者可以籍此深入了解 JavaScript 的语言特性。

    @@ -10,7 +10,7 @@

     贡献者

      +

     许可

    JavaScript 秘密花园在 MIT license 许可协议下发布,并存放在 GitHub 开源社区。 +

    此中文翻译由三生石上独立完成,博客园首发,转载请注明出处。

    对象

    对象使用和属性

    JavaScript 中所有变量都是对象,除了两个例外 nullundefined

    From 09db0bfe80975d79a53e9dc8a5acfa5cfa866ad3 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Sat, 26 Mar 2011 18:14:02 +0800 Subject: [PATCH 114/641] minor edits to mobile style, and added in SO link --- doc/en/intro/authors.md | 3 ++- site/style/garden.css | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/en/intro/authors.md b/doc/en/intro/authors.md index f88f94a7..684ca097 100644 --- a/doc/en/intro/authors.md +++ b/doc/en/intro/authors.md @@ -1,8 +1,9 @@ ## The Authors -This guide is the work of two lovely Stack Overflow users, [Ivo Wetzel][1] +This guide is the work of two lovely [Stack Overflow][3] 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 +[3]: http://stackoverflow.com/ diff --git a/site/style/garden.css b/site/style/garden.css index 1cebf569..9f636a5f 100644 --- a/site/style/garden.css +++ b/site/style/garden.css @@ -430,6 +430,7 @@ a abbr { @media screen and (max-width: 1000px) { body { width: 100%; + max-width: 1000px; overflow-x: hidden; } @@ -557,6 +558,10 @@ a abbr { @media screen and (max-width: 600px), screen and (-webkit-min-device-pixel-ratio: 2) { + body { + max-width: 600px; + } + section { margin-right: 30px; width: auto; From 6ca4789d2b5c86ce46a46390ec6387ec6c8a72a6 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Sat, 26 Mar 2011 23:57:36 +0800 Subject: [PATCH 115/641] Minor fixes for everyone! --- doc/en/function/scopes.md | 8 ++++---- doc/ru/core/semicolon.md | 3 ++- garden.jade | 2 +- site/javascript/garden.js | 4 ++-- site/style/garden.css | 22 +++++++++++----------- site/zh/index.html | 2 +- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/doc/en/function/scopes.md b/doc/en/function/scopes.md index 95735b07..7ae5e4f5 100644 --- a/doc/en/function/scopes.md +++ b/doc/en/function/scopes.md @@ -23,7 +23,7 @@ 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 +### The Bane of Global Variables // script A foo = '42'; @@ -70,7 +70,7 @@ overwrites the global value of `i`. Using a `var` for the second `for` loop woul 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 +### Local Variables The only source for local variables in JavaScript are [function](#function.general) parameters and variables that were declared via the @@ -170,7 +170,7 @@ moved to the top of the *global scope*. SomeImportantThing = {}; } -### Name resolution order +### 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*. @@ -220,7 +220,7 @@ while different in syntax, do behave the exact same way. +function(){}(); (function(){}()); -### In conclusion +### In Conclusion It is recommended to always use an *anonymous wrapper* for encapsulating code in its own namespace. This does not only protect code against name clashes, it diff --git a/doc/ru/core/semicolon.md b/doc/ru/core/semicolon.md index 083dbc45..0bcb7f01 100644 --- a/doc/ru/core/semicolon.md +++ b/doc/ru/core/semicolon.md @@ -62,7 +62,8 @@ 'и ещё одну на всякий случай' ); // <- вставлена - return; // <- вставлена, в результате оператор return разбит на два блока + return; // <- вставлена, в результате + // оператор return разбит на два блока { // теперь парсер считает этот блок отдельным // метка и одинокое выражение diff --git a/garden.jade b/garden.jade index e99266c4..944112fa 100644 --- a/garden.jade +++ b/garden.jade @@ -83,7 +83,7 @@ html(lang=language) a(href='https://github.com/visionmedia/jade/') jade | template. - script(src='http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js') + script(src='http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js') - if (language === baseLanguage) script(src='javascript/prettify.js') diff --git a/site/javascript/garden.js b/site/javascript/garden.js index bc5127ca..69918dc4 100644 --- a/site/javascript/garden.js +++ b/site/javascript/garden.js @@ -52,8 +52,8 @@ Sections.prototype = { } if (articleID !== page.article) { - nav.find('a[href=#' + page.article + ']').removeClass('active'); - nav.find('a[href=#' + articleID + ']').addClass('active'); + nav.find('a[href="#' + page.article + '"]').removeClass('active'); + nav.find('a[href="#' + articleID + '"]').addClass('active'); page.article = articleID; this.mobile(articleID); diff --git a/site/style/garden.css b/site/style/garden.css index 9f636a5f..d6843ce2 100644 --- a/site/style/garden.css +++ b/site/style/garden.css @@ -83,7 +83,7 @@ nav { nav > div li { float: left; - margin-right: 4px; + margin-right: 1px; } nav > div li a, #top, #hide_menu { @@ -107,11 +107,11 @@ nav > div li.active a { #nav_main > div { overflow: hidden; + border-bottom: 1px solid #6E8294; } #nav_main > div ul { float: left; - height: 40px; } nav li { @@ -125,7 +125,7 @@ nav > ul li:last-child a { } nav > ul > li { - border-bottom: 1px solid rgba(255, 255, 255, 0.4); + border-bottom: 1px solid #6E8294; } /* Sprite map for sidebar icons */ @@ -191,7 +191,6 @@ nav a:hover, nav ul.active a:hover, nav a, nav li { display: none; } - section { border-bottom: 4px dotted #8696a5; font-size: 16px; @@ -272,7 +271,7 @@ section:after { top: 30px; right: -190px; font-size: 80px; - color: rgba(255, 255, 255, 0.3); + color: #566D82; width: 180px; text-align: center; } @@ -331,6 +330,10 @@ aside { 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 { @@ -360,6 +363,8 @@ aside.es5:after { font-style: italic; top: -18px; right: -3px; + + -ms-filter: “progid:DXImageTransform.Microsoft.Alpha(Opacity=25)”; /* Just kill me already... */ opacity: 0.25; } @@ -418,7 +423,7 @@ a abbr { /* Desktop override */ -@media screen and (min-width: 1000px) { +@media not device and screen and (min-width: 1000px) { #nav_main { display: block !important; top: 0 !important; @@ -478,11 +483,6 @@ a abbr { margin-top: 10px; } - #nav_main > div { - overflow: hidden; - border-bottom: 1px solid rgba(255, 255, 255, 0.3); - } - #nav_main > div ul { height: auto; } diff --git a/site/zh/index.html b/site/zh/index.html index 2e24f7b9..1e6bb6e6 100644 --- a/site/zh/index.html +++ b/site/zh/index.html @@ -1764,4 +1764,4 @@

    中文翻译

    jade template. -

    \ No newline at end of file +

    \ No newline at end of file From 1fe03050f5f44a67a1a7aa5e30a4b3dce4a90d52 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 26 Mar 2011 18:02:24 +0100 Subject: [PATCH 116/641] Update .gitignore and the hosting section --- .gitignore | 1 + doc/en/intro/hosting.md | 3 +- site/zh/index.html | 1767 --------------------------------------- 3 files changed, 3 insertions(+), 1768 deletions(-) delete mode 100644 site/zh/index.html diff --git a/.gitignore b/.gitignore index 46d538e8..ff75dc4a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,6 @@ /node_modules /site/de /site/ru +/site/zh *.md~ *.src.md diff --git a/doc/en/intro/hosting.md b/doc/en/intro/hosting.md index 8d7e4116..3fe5ae28 100644 --- a/doc/en/intro/hosting.md +++ b/doc/en/intro/hosting.md @@ -1,6 +1,7 @@ ## Hosting -[Cramer Development][1] bought the domain name and has donated hosting space for [JavaScriptGarden.info][2]. +JavaScript Garden is hosted on GitHub, but [Cramer Development][1] supports us +with a mirror at [JavaScriptGarden.info][2]. [1]: http://cramerdev.com/ [2]: http://javascriptgarden.info/ diff --git a/site/zh/index.html b/site/zh/index.html deleted file mode 100644 index 1e6bb6e6..00000000 --- a/site/zh/index.html +++ /dev/null @@ -1,1767 +0,0 @@ -JavaScript 秘密花园 -

    简介

    JavaScript 秘密花园是一个不断更新,主要关心 JavaScript 一些古怪用法的文档。 -对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, -初学者可以籍此深入了解 JavaScript 的语言特性。

    - -

    JavaScript 秘密花园不是用来教你 JavaScript。为了更好的理解这篇文章的内容, -你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 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 不包含传统的类继承模型,而是使用 prototypal 原型模型。

    - -

    虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。 -实现传统的类继承模型是很简单,但是实现 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' }
    -        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)。 -然而将原子类型赋给 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 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 -没有其它方法可以用来排除原型链上的属性,而不是定义在对象自身上的属性。

    - -

    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 变量使用标准的数组方法,比如 push, pop 或者 slice。 -虽然使用 for 循环遍历也是可以的,但是为了更好的使用数组方法,最好把它转化为一个真正的数组。

    - -

    转化为数组

    - -

    下面的代码将会创建一个新的数组,包含所有 arguments 对象中的元素。

    - -
    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);
    -};
    -
    -// 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);
    -};
    -
    - -

    译者注:上面的 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);
    -assert(pair[0] === 42);
    -assert(pair[1] === 17);
    -
    - -

    然而,的确有一种情况会显著的影响现代 JavaScript 引擎的性能。这就是使用 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...
    -    }
    -}
    -
    - -

    上面代码中,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(a, b, c);
    -}, 1000)
    -
    - - - -

    结论

    - -

    绝对不要使用字符串作为 setTimeout 或者 setInterval 的第一个参数, -这么写的代码明显质量很差。当需要向回调函数传递参数时,可以创建一个匿名函数,在函数内执行真实的回调函数。

    - -

    另外,应该避免使用 setInterval,因为它的定时执行不会被 JavaScript 阻塞。

    \ No newline at end of file From acb84a76be06439aab0c1e55fc473dff7060bc39 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 26 Mar 2011 19:34:22 +0100 Subject: [PATCH 117/641] the last merge seems to have broken the url prefix --- garden.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/garden.jade b/garden.jade index d9a7f26b..86a02336 100644 --- a/garden.jade +++ b/garden.jade @@ -29,7 +29,7 @@ html(lang=language) a(href= '/', title=lang.title) #{lang.id} - else - a(href= '/' + lang.id, title=lang.title) #{lang.id} + a(href= '/JavaScript-Garden/' + lang.id, title=lang.title) #{lang.id} a(id='top', href='#intro', title='Back to top') #top a(id='hide_menu', class='tablet') Hide Menu From 7deae4184ad472f42cf4214bdd89a61092e09356 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sat, 26 Mar 2011 19:39:06 +0100 Subject: [PATCH 118/641] fix this --- build.js | 3 ++- garden.jade | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build.js b/build.js index dd2949de..7fb93be0 100644 --- a/build.js +++ b/build.js @@ -153,6 +153,7 @@ var Garden = Class(function(options) { } var options = { + pathPrefix: this.options.pathPrefix, baseLanguage: this.options.language.default, language: language, languages: languages, @@ -198,7 +199,7 @@ var Garden = Class(function(options) { }); exports.build = function (options) { - options = options || {dir: 'doc', template: 'garden.jade', out: 'site'}; + options = options || {dir: 'doc', pathPrefix: 'JavaScript-Garden/', template: 'garden.jade', out: 'site'}; new Garden(options); } diff --git a/garden.jade b/garden.jade index 86a02336..7f41c533 100644 --- a/garden.jade +++ b/garden.jade @@ -26,10 +26,10 @@ html(lang=language) - each lang in languages li(class=lang.id === language ? 'active' : '') - if (lang.id === baseLanguage) - a(href= '/', title=lang.title) #{lang.id} + a(href= '/' + pathPrefix, title=lang.title) #{lang.id} - else - a(href= '/JavaScript-Garden/' + lang.id, title=lang.title) #{lang.id} + a(href= '/' + pathPrefix + lang.id, title=lang.title) #{lang.id} a(id='top', href='#intro', title='Back to top') #top a(id='hide_menu', class='tablet') Hide Menu From ebed65071b8a6c9cc3363b1df0cbbcb3404c14ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?juho=20Veps=C3=A4l=C3=A4inen?= Date: Sat, 26 Mar 2011 22:01:10 +0200 Subject: [PATCH 119/641] Initial pass at Finnish translation. Still got some work to do. --- doc/language.json | 2 +- site/zh/index.html | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/doc/language.json b/doc/language.json index afa5fabe..260c7e17 100644 --- a/doc/language.json +++ b/doc/language.json @@ -1,5 +1,5 @@ { "default": "en", - "listed": ["en", "ru", "zh"] + "listed": ["en", "fi", "ru", "zh"] } diff --git a/site/zh/index.html b/site/zh/index.html index 1e6bb6e6..27798744 100644 --- a/site/zh/index.html +++ b/site/zh/index.html @@ -1,7 +1,7 @@ JavaScript 秘密花园 -

    简介

    JavaScript 秘密花园是一个不断更新,主要关心 JavaScript 一些古怪用法的文档。 +

    简介

    JavaScript 秘密花园是一个不断更新,主要关心 JavaScript 一些古怪用法的文档。 对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, 初学者可以籍此深入了解 JavaScript 的语言特性。

    @@ -1758,10 +1758,6 @@

    中文翻译

    这么写的代码明显质量很差。当需要向回调函数传递参数时,可以创建一个匿名函数,在函数内执行真实的回调函数。

    另外,应该避免使用 setInterval,因为它的定时执行不会被 JavaScript 阻塞。

    \ No newline at end of file From 2245eee53e2ee000773bc7729a27d6ff1aa56eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?juho=20Veps=C3=A4l=C3=A4inen?= Date: Sat, 26 Mar 2011 22:01:43 +0200 Subject: [PATCH 120/641] Added some missing files. --- doc/fi/array/constructor.md | 25 ++++ doc/fi/array/general.md | 40 ++++++ 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 | 98 ++++++++++++++ doc/fi/function/constructors.md | 128 ++++++++++++++++++ doc/fi/function/general.md | 48 +++++++ doc/fi/function/scopes.md | 231 ++++++++++++++++++++++++++++++++ doc/fi/function/this.md | 111 +++++++++++++++ doc/fi/index.json | 72 ++++++++++ doc/fi/intro/authors.md | 9 ++ doc/fi/intro/contributors.md | 8 ++ doc/fi/intro/index.md | 15 +++ doc/fi/intro/license.md | 12 ++ doc/fi/object/forinloop.md | 51 +++++++ doc/fi/object/general.md | 99 ++++++++++++++ doc/fi/object/hasownproperty.md | 53 ++++++++ doc/fi/object/prototype.md | 116 ++++++++++++++++ doc/fi/other/timeouts.md | 155 +++++++++++++++++++++ doc/fi/types/casting.md | 70 ++++++++++ doc/fi/types/equality.md | 71 ++++++++++ doc/fi/types/instanceof.md | 38 ++++++ doc/fi/types/typeof.md | 87 ++++++++++++ 25 files changed, 1824 insertions(+) create mode 100644 doc/fi/array/constructor.md create mode 100644 doc/fi/array/general.md create mode 100644 doc/fi/core/eval.md create mode 100644 doc/fi/core/semicolon.md create mode 100644 doc/fi/core/undefined.md create mode 100644 doc/fi/function/arguments.md create mode 100644 doc/fi/function/closures.md create mode 100644 doc/fi/function/constructors.md create mode 100644 doc/fi/function/general.md create mode 100644 doc/fi/function/scopes.md create mode 100644 doc/fi/function/this.md create mode 100644 doc/fi/index.json create mode 100644 doc/fi/intro/authors.md create mode 100644 doc/fi/intro/contributors.md create mode 100644 doc/fi/intro/index.md create mode 100644 doc/fi/intro/license.md create mode 100644 doc/fi/object/forinloop.md create mode 100644 doc/fi/object/general.md create mode 100644 doc/fi/object/hasownproperty.md create mode 100644 doc/fi/object/prototype.md create mode 100644 doc/fi/other/timeouts.md create mode 100644 doc/fi/types/casting.md create mode 100644 doc/fi/types/equality.md create mode 100644 doc/fi/types/instanceof.md create mode 100644 doc/fi/types/typeof.md diff --git a/doc/fi/array/constructor.md b/doc/fi/array/constructor.md new file mode 100644 index 00000000..ee3e61b8 --- /dev/null +++ b/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/doc/fi/array/general.md b/doc/fi/array/general.md new file mode 100644 index 00000000..238c075c --- /dev/null +++ b/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/doc/fi/core/eval.md b/doc/fi/core/eval.md new file mode 100644 index 00000000..d9b8901d --- /dev/null +++ b/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/doc/fi/core/semicolon.md b/doc/fi/core/semicolon.md new file mode 100644 index 00000000..07586f9b --- /dev/null +++ b/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/doc/fi/core/undefined.md b/doc/fi/core/undefined.md new file mode 100644 index 00000000..148c44d3 --- /dev/null +++ b/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/doc/fi/function/arguments.md b/doc/fi/function/arguments.md new file mode 100644 index 00000000..153dad34 --- /dev/null +++ b/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/doc/fi/function/closures.md b/doc/fi/function/closures.md new file mode 100644 index 00000000..17554dcb --- /dev/null +++ b/doc/fi/function/closures.md @@ -0,0 +1,98 @@ +## Closures and References + +One of JavaScript's most powerful features is the availability of *closures*, +this means that scopes **always** keep access to the outer scope they were +defined in. Since the only scoping that JavaScript has is +[function scope](#function.scopes), all functions, by default, act as closures. + +### Emulating private variables + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Here, `Counter` returns **two** closures. The function `increment` as well as +the function `get`. Both of these functions keep a **reference** to the scope of +`Counter` and, therefore, always keep access to the `count` variable that was +defined in that very scope. + +### Why Private Variables Work + +Since it is not possible to reference or assign scopes in JavaScript, there is +**no** way of accessing the variable `count` from the outside. The only way to +interact with it is via the two closures. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +The above code will **not** change the variable `count` in the scope of `Counter`, +since `foo.hack` was not defined in **that** scope. It will instead create - or +override - the *global* variable `count`. + +### Closures Inside Loops + +One often made mistake is to use closures inside of loops, as if they were +copying the value of the loops index variable. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +The above will **not** output the numbers `0` through `9`, but will simply print +the number `10` ten times. + +The *anonymous* function keeps a **reference** to `i` and at the time +`console.log` gets called, the `for loop` has already finished and the value of +`i` as been set to `10`. + +In order to get the desired behavior, it is necessary to create a **copy** of +the value of `i`. + +### Avoiding the Reference Problem + +In order to copy the value of the loop's index variable, it is best to use an +[anonymous wrapper](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +The anonymous outer function gets called immediately with `i` as its first +argument and will receive a copy of the **value** of `i` as its parameter `e`. + +The anonymous function that gets passed to `setTimeout` now has a reference to +`e`, whose value does **not** get changed by the loop. + +There is another possible way of achieving this; that is to return a function +from the anonymous wrapper, that will then have the same behavior as the code +above. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + diff --git a/doc/fi/function/constructors.md b/doc/fi/function/constructors.md new file mode 100644 index 00000000..ad90b028 --- /dev/null +++ b/doc/fi/function/constructors.md @@ -0,0 +1,128 @@ +## Constructors + +Constructors in JavaScript are yet again different from many other languages. Any +function call that is preceded by the `new` keyword acts as a constructor. + +Inside the constructor - the called function - the value of `this` refers to a +newly created `Object`. The [`prototype`](#object.prototype) of this **new** +object is set to the `prototype` of the function object that was invoked as the +constructor. + +If the function that was called has no explicit `return` statement, then it +implicitly returns the value of `this` - the new object. + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +The above calls `Foo` as constructor and sets the `prototype` of the newly +created object to `Foo.prototype`. + +In case of an explicit `return` statement the function returns the value +specified that statement, **but only** if the return value is an `Object`. + + function Bar() { + return 2; + } + new Bar(); // a new object + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // the returned object + +When the `new` keyword is omitted, the function will **not** return a new object. + + function Foo() { + this.bla = 1; // gets set on the global object + } + Foo(); // undefined + +While the above example might still appear to work in some cases, due to the +workings of [`this`](#function.this) in JavaScript, it will use the +*global object* as the value of `this`. + +### Factories + +In order to be able to omit the `new` keyword, the constructor function has to +explicitly return a value. + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +Both calls to `Bar` return the exact same thing, a newly create object which +has a property called `method` on it, that is a +[Closure](#function.closures). + +It is also to note that the call `new Bar()` does **not** affect the prototype +of the returned object. While the prototype will be set on the newly created +object, `Bar` never returns that new object. + +In the above example, there is no functional difference between using and +not using the `new` keyword. + + +### Creating New Objects via Factories + +An often made recommendation is to **not** use `new` since forgetting its use +may lead to bugs. + +In order to create new object, one should rather use a factory and construct a +new object inside of that factory. + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +While the above is robust against a missing `new` keyword and certainly makes +the use of [private variables](#function.closures) easier, it comes with some +downsides. + + 1. It uses more memory since the created objects do **not** share the methods + on a prototype. + 2. In order to inherit the factory needs to copy all the methods from another + object or put that object on the prototype of the new object. + 3. Dropping the prototype chain just because of a left out `new` keyword + somehow goes against the spirit of the language. + +### In Conclusion + +While omitting the `new` keyword might lead to bugs, it is certainly **not** a +reason to drop the use of prototypes altogether. In the end it comes down to +which solution is better suited for the needs of the application, it is +especially important to choose a specific style of object creation **and stick** +with it. + diff --git a/doc/fi/function/general.md b/doc/fi/function/general.md new file mode 100644 index 00000000..64c9e0c9 --- /dev/null +++ b/doc/fi/function/general.md @@ -0,0 +1,48 @@ +## Function Declarations and Expressions + +Functions in JavaScript are first class objects. That means they can be +passed around like any other value. One common use of this feature is to pass +an *anonymous function* as a callback to another, possibly asynchronous function. + +### The `function` Declaration + + function foo() {} + +The above function gets [hoisted](#function.scopes) before the execution of the +program starts; thus, it is available *everywhere* in the scope it was *defined* +in, even if called before the actual definition in the source. + + foo(); // Works because foo was created before this code runs + function foo() {} + +### The `function` Expression + + var foo = function() {}; + +This example assigns the unnamed and *anonymous* function to the variable `foo`. + + foo; // 'undefined' + foo(); // this raises a TypeError + var foo = function() {}; + +Due to the fact that `var` is a declaration, that hoists the variable name `foo` +before the actual execution of the code starts, `foo` is already defined when +the script gets executed. + +But since assignments only happen at runtime, the value of `foo` will default +to [undefined](#core.undefined) before the corresponding code is executed. + +### Named Function Expression + +Another special case is the assignment of named functions. + + var foo = function bar() { + bar(); // Works + } + bar(); // ReferenceError + +Here `bar` is not available in the outer scope, since the function only gets +assigned to `foo`; however, inside of `bar` it is available. This is due to +how [name resolution](#function.scopes) in JavaScript works, the name of the +function is *always* made available in the local scope of the function itself. + diff --git a/doc/fi/function/scopes.md b/doc/fi/function/scopes.md new file mode 100644 index 00000000..7ae5e4f5 --- /dev/null +++ b/doc/fi/function/scopes.md @@ -0,0 +1,231 @@ +## Scopes and Namespaces + +Although JavaScript deals fine with the syntax of two matching curly +braces for blocks, it does **not** support block scope; hence, all that is left +is in the language is *function scope*. + + function test() { // a scope + for(var i = 0; i < 10; i++) { // not a scope + // count + } + console.log(i); // 10 + } + +> **Note:** When not used in an assignment, return statement or as a function +> argument, the `{...}` notation will get interpreted as a block statement and +> **not** as an object literal. This, in conjunction with +> [automatic insertion of semicolons](#core.semicolon), can lead to subtle errors. + +There are also no distinct namespaces in JavaScript, that means that everything +gets defined in one *globally shared* namespace. + +Each time a variable is referenced, JavaScript will traverse upwards through all +the scopes until it finds it. In the case that it reaches the global scope and +still has not found the requested name, it will raise a `ReferenceError`. + +### The Bane of Global Variables + + // script A + foo = '42'; + + // script B + var foo = '42' + +The above two scripts do **not** have the same effect. Script A defines a +variable called `foo` in the *global* scope and script B defines a `foo` in the +*current* scope. + +Again, that is **not** at all the *same effect*, not using `var` can have major +implications. + + // global scope + var foo = 42; + function test() { + // local scope + foo = 21; + } + test(); + foo; // 21 + +Leaving out the `var` statement inside the function `test` will override the +value of `foo`. While this might not seem like a big deal at first, having +thousands of lines of JavaScript and not using `var` will introduce horrible and +hard to track down bugs. + + // global scope + var items = [/* some list */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // scope of subLoop + for(i = 0; i < 10; i++) { // missing var statement + // do amazing stuff! + } + } + +The outer loop will terminate after the first call to `subLoop`, since `subLoop` +overwrites the global value of `i`. Using a `var` for the second `for` loop would +have easily avoided this error. The `var` statement should **never** be left out +unless the *desired effect* is to affect the outer scope. + +### Local Variables + +The only source for local variables in JavaScript are +[function](#function.general) parameters and variables that were declared via the +`var` statement. + + // global scope + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // local scope of the function test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +While `foo` and `i` are local variables inside the scope of the function `test`, +the assignment of `bar` will override the global variable with the same name. + +### Hoisting + +JavaScript **hoists** declarations. This means that both `var` statements and +`function` declarations will be moved to the top of their enclosing scope. + + bar(); + var bar = function() {}; + var someValue = 42; + + test(); + function test(data) { + if (false) { + goo = 1; + + } else { + var goo = 2; + } + for(var i = 0; i < 100; i++) { + var e = data[i]; + } + } + +The above code gets transformed before any execution is started. JavaScript moves +the `var` statements as well as the `function` declarations to the top of the +nearest surrounding scope. + + // var statements got moved here + var bar, someValue; // default to 'undefined' + + // the function declartion got moved up too + function test(data) { + var goo, i, e; // missing block scope moves these here + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // fails with a TypeError since bar is still 'undefined' + someValue = 42; // assignments are not affected by hoisting + bar = function() {}; + + test(); + +Missing block scoping will not only move `var` statements out of loops and +their bodies, it will also make the results of certain `if` constructs +non-intuitive. + +In the original code the `if` statement seemed to modify the *global +variable* `goo`, while actually it modifies the *local variable* - after hoisting +has been applied. + +Without the knowledge about *hoisting*, below code might seem to raise a +`ReferenceError`. + + // check whether SomeImportantThing has been initiliazed + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +But of course, the above works due to the fact that the `var` statement is being +moved to the top of the *global scope*. + + var SomeImportantThing; + + // other code might initiliaze SomeImportantThing here, or not + + // make sure it's there + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Name Resolution Order + +All scopes in JavaScript, including the *global scope*, have the special name +[`this`](#function.this) defined in them, which refers to the *current object*. + +Function scopes also have the name [`arguments`](#function.arguments) defined in +them which contains the arguments that were passed to a function. + +For example, when trying to access a variable named `foo` inside the scope of a +function, JavaScript will lookup the name in the following order: + + 1. In case there is a `var foo` statement in the current scope use that. + 2. If one of the function parameters is named `foo` use that. + 3. If the function itself is called `foo` use that. + 4. Go to the next outer scope and start with **#1** again. + +> **Note:** Having a parameter called `arguments` will **prevent** the creation +> of the default `arguments` object. + +### Namespaces + +A common problem of having only one global namespace is the likeliness of running +into problems where variable names clash. In JavaScript, this problem can +easily be avoided with the help of *anonymous wrappers*. + + (function() { + // a self contained "namespace" + + window.foo = function() { + // an exposed closure + }; + + })(); // execute the function immediately + + +Unnamed functions are considered [expressions](#function.general); so in order to +being callable, they must first be evaluated. + + ( // evaluate the function inside the paranthesis + function() {} + ) // and return the function object + () // call the result of the evaluation + +There are other ways for evaluating and calling the function expression; which, +while different in syntax, do behave the exact same way. + + // Two other ways + +function(){}(); + (function(){}()); + +### In Conclusion + +It is recommended to always use an *anonymous wrapper* for encapsulating code in +its own namespace. This does not only protect code against name clashes, it +also allows for better modularization of programs. + +Additionally, the use of global variables is considered **bad practice**. **Any** +use of them indicates badly written code that is prone to errors and hard to maintain. + diff --git a/doc/fi/function/this.md b/doc/fi/function/this.md new file mode 100644 index 00000000..0cff9ee4 --- /dev/null +++ b/doc/fi/function/this.md @@ -0,0 +1,111 @@ +## How `this` Works + +JavaScript has a different concept of what the special name `this` refers to +than most other programming languages do. There are exactly **five** different +ways in which the value of `this` can be bound in the language. + +### The Global Scope + + this; + +When using `this` in global scope, it will simply refer to the *global* object. + + +### Calling a Function + + foo(); + +Here `this` will again refer to the *global* object. + +> **ES5 Note:** In strict mode, the global case **no longer** exists. +> `this` will instead have the value of `undefined` in that case. + +### Calling a Method + + test.foo(); + +In this example `this` will refer to `test`. + +### Calling a Constructor + + new foo(); + +A function call that is preceded by the `new` keyword acts as +a [constructor](#function.constructors). Inside the function `this` will refer +to a *newly created* `Object`. + +### Explicit Setting of `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // array will expand to the below + foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3 + +When using the `call` or `apply` methods of `Function.prototype`, the value of +`this` inside the called function gets **explicitly set** to the first argument +of the corresponding function call. + +As a result, the above example the *method case* does **not** apply, and `this` +inside of `foo` will be set to `bar`. + +> **Note:** `this` **cannot** be used to refer to the object inside of an `Object` +> literal. So `var obj = {me: this}` will **not** result in `me` referring to +> `obj`, since `this` only gets bound by one of the five listed cases. + +### Common Pitfalls + +While most of these cases make sense, the first one is to be considered another +mis-design of the language, as it **never** has any practical use. + + Foo.method = function() { + function test() { + // this is set to the global object + } + test(); + } + +A common misconception is that `this` inside of `test` refers to `Foo`, while in +fact it **does not**. + +In order to gain access to `Foo` from within `test` it is necessary to create a +local variable inside of `method` which refers to `Foo`. + + Foo.method = function() { + var that = this; + function test() { + // Use that instead of this here + } + test(); + } + +`that` is just a normal name, but it is commonly used for the reference to an +outer `this`. In combination with [closures](#function.closures), it can also +be used to pass `this` values around. + +### Assigning Methods + +Another thing that does **not** work in JavaScript is function aliasing, that is, +**assigning** a method to a variable. + + var test = someObject.methodTest; + test(); + +Due to the first case `test` now acts like like a plain function call; therefore, +`this` inside it will no longer refer to `someObject`. + +While the late binding of `this` might seem like a bad idea at first, it is in +fact what makes [prototypal inheritance](#object.prototype) work. + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +When `method` gets called on a instance of `Bar`, `this` will now refer to that +very instance. + + diff --git a/doc/fi/index.json b/doc/fi/index.json new file mode 100644 index 00000000..da354376 --- /dev/null +++ b/doc/fi/index.json @@ -0,0 +1,72 @@ +{ + "title": "JavaScript Puutarha", + "langTitle": "JavaScript Puutarha suomeksi", + "description": "Opas JavaScriptin outouksiin ja vikoihin ", + "sections": [ + { + "title": "Johdanto", + "dir": "intro", + "articles": [ + "authors", + "contributors", + "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/doc/fi/intro/authors.md b/doc/fi/intro/authors.md new file mode 100644 index 00000000..684ca097 --- /dev/null +++ b/doc/fi/intro/authors.md @@ -0,0 +1,9 @@ +## The Authors + +This guide is the work of two lovely [Stack Overflow][3] 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 +[3]: http://stackoverflow.com/ + diff --git a/doc/fi/intro/contributors.md b/doc/fi/intro/contributors.md new file mode 100644 index 00000000..73fb98ea --- /dev/null +++ b/doc/fi/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/doc/fi/intro/index.md b/doc/fi/intro/index.md new file mode 100644 index 00000000..f2d5d31b --- /dev/null +++ b/doc/fi/intro/index.md @@ -0,0 +1,15 @@ +## Intro + +**JavaScript Garden** is a growing collection of documentation about the most +quirky parts of the JavaScript programming language. It gives advice to +avoid common mistakes, subtle bugs, as well as performance issues and bad +practices that non-expert JavaScript programmers may encounter on their +endeavours into the depths of the language. + +JavaScript Garden does **not** aim to teach you JavaScript. Former knowledge +of the language is strongly recommended in order to understand the topics covered +in this guide. In order to learn the basics of the language, please head over to +the excellent [guide][1] on the Mozilla Developer Network. + +[1]: https://developer.mozilla.org/en/JavaScript/Guide + diff --git a/doc/fi/intro/license.md b/doc/fi/intro/license.md new file mode 100644 index 00000000..cd398699 --- /dev/null +++ b/doc/fi/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/doc/fi/object/forinloop.md b/doc/fi/object/forinloop.md new file mode 100644 index 00000000..30751ed9 --- /dev/null +++ b/doc/fi/object/forinloop.md @@ -0,0 +1,51 @@ +## The `for in` Loop + +Just like the `in` operator, the `for in` loop also traverses the prototype +chain when iterating over the properties of an object. + +> **Note:** The `for in` loop will **not** iterate over any properties that +> have their `enumerable` attribute set to `false`; for example, the `length` +> property of an array. + + // Poisoning Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // prints both bar and moo + } + +Since it is not possible to change the behavior of the `for in` loop itself, it +is necessary to filter out the unwanted properties inside the loop body , +this is done by using the [`hasOwnProperty`](#object.hasownproperty) method of +`Object.prototype`. + +> **Note:** Since the `for in` always traverses the complete prototype chain, it +> will get slower with each additional layer of inheritance added to an object. + +### Using `hasOwnProperty` for Filtering + + // still the foo from above + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +This version is the only correct one to use. Due to the use of `hasOwnProperty` it +will **only** print out `moo`. When `hasOwnProperty` is left out, the code is +prone to errors in cases where the native prototypes - e.g. `Object.prototype` - +have been extended. + +One widely used framework which does this is [Prototype][1]. When this +framework is included, `for in` loops that do not use `hasOwnProperty` are +guaranteed to break. + +### In Conclusion + +It is recommended to **always** use `hasOwnProperty`. Never should any +assumptions be made about the environment the code is running in, or whether the +native prototypes have been extended or not. + +[1]: http://www.prototypejs.org/ + diff --git a/doc/fi/object/general.md b/doc/fi/object/general.md new file mode 100644 index 00000000..cbad5f11 --- /dev/null +++ b/doc/fi/object/general.md @@ -0,0 +1,99 @@ +## Object Usage and Properties + +Everything in JavaScript acts like an object, with the only two exceptions being +[`null`](#core.undefined) and [`undefined`](#core.undefined). + + false.toString() // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +A common misconception is that number literals cannot be used as +objects. That is because a flaw in JavaScript's parser tries to parse the *dot +notation* on a number as a floating point literal. + + 2.toString(); // raises SyntaxError + +There are a couple of workarounds which can be used in order make number +literals act as objects too. + + 2..toString(); // the second point is correctly recognized + 2 .toString(); // note the space left to the dot + (2).toString(); // 2 is evaluated first + +### Objects as a Data Type + +Objects in JavaScript can also be used as a [*Hashmap*][1], they mainly consist +of named properties mapping to values. + +Using a object literal - `{}` notation - it is possible to create a +plain object. This new object [inherits](#object.prototype) from `Object.prototype` and +has no [own properties](#object.hasownproperty) defined on it. + + var foo = {}; // a new empty object + + // a new object with a property called 'test' with value 12 + var bar = {test: 12}; + +### Accessing Properties + +The properties of an object can be accessed in two ways, via either the dot +notation, or the square bracket notation. + + var foo = {name: 'Kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // works + +Both notations are identical in their workings, with the only difference being that +the square bracket notation allows for dynamic setting of properties, as well as +the use of property names that would otherwise lead to a syntax error. + +### Deleting Properties + +The only way to actually remove a property from an object is to use the `delete` +operator; setting the property to `undefined` or `null` only remove the +*value* associated with the property, but not the *key*. + + var obj = { + bar: 1, + foo: 2, + baz: 3 + }; + obj.bar = undefined; + obj.foo = null; + delete obj.baz; + + for(var i in obj) { + if (obj.hasOwnProperty(i)) { + console.log(i, '' + obj[i]); + } + } + +The above outputs both `bar undefined` and `foo null` - only `baz` was +removed and is therefore missing from the output. + +### Notation of Keys + + var test = { + 'case': 'I am a keyword so I must be notated as a string', + delete: 'I am a keyword too so me' // raises SyntaxError + }; + +Object properties can be both notated as plain characters and as strings. Due to +another mis-design in JavaScript's parser, the above will throw +a `SyntaxError` prior to ECMAScript 5. + +This error arises from the fact that `delete` is a *keyword*; therefore, it must be +notated as a *string literal* to ensure that it will be correctly interpreted by +older JavaScript engines. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/doc/fi/object/hasownproperty.md b/doc/fi/object/hasownproperty.md new file mode 100644 index 00000000..319a8801 --- /dev/null +++ b/doc/fi/object/hasownproperty.md @@ -0,0 +1,53 @@ +## `hasOwnProperty` + +In order to check whether a object has a property defined on *itself* and **not** +somewhere on its [prototype chain](#object.prototype), it is necessary to use the +`hasOwnProperty` method which all objects inherit from `Object.prototype`. + +> **Note:** It is **not** enough to check whether a property is `undefined`. The +> property might very well exist, but its value just happens to be set to +> `undefined`. + +`hasOwnProperty` is the only thing in JavaScript which deals with properties and +does **not** traverse the prototype chain. + + // Poisoning Object.prototype + Object.prototype.bar = 1; + var foo = {goo: undefined}; + + foo.bar; // 1 + 'bar' in foo; // true + + foo.hasOwnProperty('bar'); // false + foo.hasOwnProperty('goo'); // true + +Only `hasOwnProperty` will give the correct and expected result, this is +essential when iterating over the properties of any object. There is **no** other +way to exclude properties that are not defined on the object *itself*, but +somewhere on its prototype chain. + +### `hasOwnProperty` as a Property + +JavaScript does **not** protect the property name `hasOwnProperty`; thus, if the +possibility exists that an object might have a property with this name, it is +necessary to use an *external* `hasOwnProperty` in order to get correct results. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // always returns false + + // Use another Object's hasOwnProperty and call it with 'this' set to foo + ({}).hasOwnProperty.call(foo, 'bar'); // true + +### In Conclusion + +When checking for the existence of a property on a object, `hasOwnProperty` is +the **only** method of doing so. It is also recommended to make `hasOwnProperty` +part of **every** [`for in` loop](#object.forinloop), this will avoid errors from +extended native [prototypes](#object.prototype). + diff --git a/doc/fi/object/prototype.md b/doc/fi/object/prototype.md new file mode 100644 index 00000000..f780eba2 --- /dev/null +++ b/doc/fi/object/prototype.md @@ -0,0 +1,116 @@ +## The Prototype + +JavaScript does not feature a classical inheritance model, instead it uses a +*prototypal* one. + +While this is often considered to be one of JavaScript's weaknesses, the +prototypal inheritance model is in fact more powerful than the classic model. +It is for example fairly trivial to build a classic model on top of it, while the +other way around is a far more difficult task. + +Due to the fact that JavaScript is basically the only widely used language that +features prototypal inheritance, it takes some time to adjust to the +differences between the two models. + +The first major difference is that inheritance in JavaScript is done by using so +called *prototype chains*. + +> **Note:** Simply using `Bar.prototype = Foo.prototype` will result in both objects +> sharing the **same** prototype. Therefore, changes to either object's prototype +> will affect the prototype of the other as well, which in most cases is not the +> desired effect. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Set Bar's prototype to a new instance of Foo + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Make sure to list Bar as the actual constructor + Bar.prototype.constructor = Bar; + + var test = new Bar() // create a new bar instance + + // The resulting prototype chain + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World' } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +In the above, the object `test` will inherit from both `Bar.prototype` and +`Foo.prototype`; hence, it will have access to the function `method` that was +defined on `Foo`. It will also have access to the property `value` of the +**one** `Foo` instance that is its prototype. It is important to note that `new +Bar()` does **not** create a new `Foo` instance, but reuses the one assigned to +its prototype; thus, all `Bar` instances will share the **same** `value` property. + +> **Note:** Do **not** use `Bar.prototype = Foo`, since it will not point to +> the prototype of `Foo` but rather to the function object `Foo`. So the +> prototype chain will go over `Function.prototype` and not `Foo.prototype`; +> therefore, `method` will not be on the prototype chain. + +### Property Lookup + +When accessing the properties of an object, JavaScript will traverse the +prototype chain **upwards** until it finds a property with the requested name. + +When it reaches the top of the chain - namely `Object.prototype` - and still +hasn't found the specified property, it will return the value +[undefined](#core.undefined) instead. + +### The Prototype Property + +While the prototype property is used by the language to build the prototype +chains, it is still possible to assign **any** given value to it. Although +primitives will simply get ignored when assigned as a prototype. + + function Foo() {} + Foo.prototype = 1; // no effect + +Assigning objects, as shown in the example above, will work, and allows for dynamic +creation of prototype chains. + +### Performance + +The lookup time for properties that are high up on the prototype chain can have a +negative impact on performance critical sections of code. Additionally, trying to +access non-existent properties will always traverse the full prototype chain. + +Also, when [iterating](#object.forinloop) over the properties of an object +**every** property that is on the prototype chain will get enumerated. + +### Extension of Native Prototypes + +One mis-feature that is often used is to extend `Object.prototype` or one of the +other built in prototypes. + +This technique is called [monkey patching][1] and breaks *encapsulation*. While +used by widely spread frameworks such as [Prototype][2], there is still no good +reason for cluttering built-in types with additional *non-standard* functionality. + +The **only** good reason for extending a built-in prototype is to backport +the features of newer JavaScript engines; for example, +[`Array.forEach`][3]. + +### In Conclusion + +It is a **must** to understand the prototypal inheritance model completely +before writing complex code which makes use of it. Also, watch the length of +the prototype chains and break them up if necessary to avoid possible +performance issues. Further, the native prototypes should **never** be extended +unless it is for the sake of compatibility with newer JavaScript features. + +[1]: http://en.wikipedia.org/wiki/Monkey_patch +[2]: http://prototypejs.org/ +[3]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach + diff --git a/doc/fi/other/timeouts.md b/doc/fi/other/timeouts.md new file mode 100644 index 00000000..d196a3b7 --- /dev/null +++ b/doc/fi/other/timeouts.md @@ -0,0 +1,155 @@ +### `setTimeout` and `setInterval` + +Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the `setTimeout` and `setInterval` functions. + +> **Note:** Timeouts are **not** part of the ECMAScript Standard. They are +> implemented as part of the [DOM][1]. + + function foo() {} + var id = setTimeout(foo, 1000); // returns a Number > 0 + +When `setTimeout` gets called, it will return the ID of the timeout and schedule +`foo` to run in **approximately** one thousand milliseconds in the future. +`foo` will then get executed exactly **once**. + +Depending on the timer resolution of the JavaScript engine that is running the +code, as well as the fact that JavaScript is single threaded and other code that +gets executed might block the thread, it is by **no means** a safe bet that one +will get the exact delay that was specified in the `setTimeout` call. + +The function that was passed as the first parameter will get called by the +*global object*, that means, that [`this`](#function.this) inside the called function +refers to that very object. + + function Foo() { + this.value = 42; + this.method = function() { + // this refers to the global object + console.log(this.value); // will log undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **Note:** As `setTimeout` takes a **function object** as its first parameter, an +> often made mistake is to use `setTimeout(foo(), 1000)`, which will use the +> **return value** of the call `foo` and **not** `foo`. This is, most of the time, +> a silent error, since when the function returns `undefined` `setTimeout` will +> **not** raise any error. + +### Stacking Calls with `setInterval` + +While `setTimeout` only runs the function once, `setInterval` - as the name +suggests - will execute the function **every** `X` milliseconds. But its use is +discouraged. + +When code that is being executed blocks the timeout call, `setInterval` will +still issue more calls to the specified function. This can, especially with small +intervals, result in function calls stacking up. + + function foo(){ + // something that blocks for 1 second + } + setInterval(foo, 100); + +In the above code `foo` will get called once and will then block for one second. + +While `foo` blocks the code `setInterval` will still schedule further calls to +it. Now, when `foo` has finished, there will already be **ten** further calls to +it waiting for execution. + +### Dealing with Possible Blocking Code + +The easiest as well as most controllable solution, is to use `setTimeout` within +the function itself. + + function foo(){ + // something that blocks for 1 second + setTimeout(foo, 100); + } + foo(); + +Not only does this encapsulate the `setTimeout` call, but it also prevents the +stacking of calls and it gives additional control.`foo` itself can now decide +whether it wants to run again or not. + +### Manually Clearing Timeouts + +Clearing timeouts and intervals works by passing the respective ID to +`clearTimeout` or `clearInterval`, depending which `set` function was used in +the first place. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Clearing all timeouts + +As there is no built-in method for clearing all timeouts and/or intervals, +it is necessary to use brute force in order to achieve this functionality. + + // clear "all" timeouts + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +There might still be timeouts that are unaffected by this arbitrary number; +therefore, is is instead recommended to keep track of all the timeout IDs, so +they can be cleared specifically. + +### Hidden use of `eval` + +`setTimeout` and `setInterval` can also take a string as their first parameter. +This feature should **never** be used, since it internally makes use of `eval`. + +> **Note:** Since the timeout functions are **not** specified by the ECMAScript +> standard, the exact workings when a string is passed to them might differ in +> various JavaScript implementations. For example, Microsoft's JScript makes use of +> the `Function` constructor in place of `eval`. + + function foo() { + // will get called + } + + function bar() { + function foo() { + // never gets called + } + setTimeout('foo()', 1000); + } + bar(); + +Since `eval` is not getting called [directly](#core.eval) in this case, the string +passed to `setTimeout` will get executed in the *global scope*; thus, it will +not use the local variable `foo` from the scope of `bar`. + +It is further recommended to **not** use a string for passing arguments to the +function that will get called by either of the timeout functions. + + function foo(a, b, c) {} + + // NEVER use this + setTimeout('foo(1,2, 3)', 1000) + + // Instead use an anonymous function + setTimeout(function() { + foo(a, b, c); + }, 1000) + +> **Note:** While it is also possible to use the syntax +> `setTimeout(foo, 1000, a, b, c)`, it is not recommended, as its use may lead +> to subtle errors when used with [methods](#function.this). + +### In Conclusion + +**Never** should a string be used as the parameter of `setTimeout` or +`setInterval`. It is a clear sign of **really** bad code, when arguments need +to be supplied to the function that gets called. An *anonymous function* should +be passed that then takes care of the actual call. + +Further, the use of `setInterval` should be avoided since its scheduler is not +blocked by executing JavaScript. + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" + diff --git a/doc/fi/types/casting.md b/doc/fi/types/casting.md new file mode 100644 index 00000000..15d84e74 --- /dev/null +++ b/doc/fi/types/casting.md @@ -0,0 +1,70 @@ +## Type Casting + +JavaScript is a *weakly typed* language, so it will apply *type coercion* +**wherever** possible. + + // These are true + new Number(10) == 10; // Number.toString() is converted + // back to a number + + 10 == '10'; // Strings gets converted to Number + 10 == '+10 '; // More string madness + 10 == '010'; // And more + isNaN(null) == false; // null converts to 0 + // which of course is not NaN + + // These are false + 10 == 010; + 10 == '-10'; + +> **ES5 Note:** Number literals that start with a `0` are interpreted as octal +> (Base 8). Octal support for these has been **removed** in ECMAScript 5 strict +> mode. + +In order to avoid the above, use of the [strict equal operator](#types.equality) +is **highly** recommended. Although this avoids a lot of common pitfalls, there +are still many further issues that arise from JavaScript's weak typing system. + +### Constructors of Built-In Types + +The constructors of the built in types like `Number` and `String` behave +differently when being used with the `new` keyword and without it. + + new Number(10) === 10; // False, Object and Number + Number(10) === 10; // True, Number and Number + new Number(10) + 0 === 10; // True, due to implicit conversion + +Using a built-in type like `Number` as a constructor will create a new `Number` +object, but leaving out the `new` keyword will make the `Number` function behave +like a converter. + +In addition, having literals or non-object values in there will result in even +more type coercion. + +The best option is to cast to one of the three possible types **explicitly**. + +### Casting to a String + + '' + 10 === '10'; // true + +By prepending a empty string a value can easily be casted to a string. + +### Casting to a Number + + +'10' === 10; // true + +Using the **unary** plus operator it is possible to cast to a number. + +### Casting to a Boolean + +By using the **not** operator twice, a value can be converted a boolean. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + + diff --git a/doc/fi/types/equality.md b/doc/fi/types/equality.md new file mode 100644 index 00000000..a578b19c --- /dev/null +++ b/doc/fi/types/equality.md @@ -0,0 +1,71 @@ +## Equality and Comparisons + +JavaScript has two different ways of comparing the values of objects for equality. + +### The Equality Operator + +The equality operator consists of two equal signs: `==` + +JavaScript features *weak typing*. This means that the equality operator +**coerces** types in order to compare them. + + "" == "0" // false + 0 == "" // true + 0 == "0" // true + false == "false" // false + false == "0" // true + false == undefined // false + false == null // false + null == undefined // true + " \t\r\n" == 0 // true + +The above table shows the results of the type coercion and it is the main reason +why the use of `==` is widely regarded as bad practice, it introduces hard to +track down bugs due to its complicated conversion rules. + +Additionally there is also a performance impact when type coercion is in play; +for example, a string has to be converted to a number before it can be compared +to another number. + +### The Strict Equality Operator + +The strict equality operator consists of **three** equal signs: `===` + +It works exactly like the normal equality operator, except that strict equality +operator does **not** perform type coercion between its operands. + + "" === "0" // false + 0 === "" // false + 0 === "0" // false + false === "false" // false + false === "0" // false + false === undefined // false + false === null // false + null === undefined // false + " \t\r\n" === 0 // false + +The above results are a lot clearer and allow for early breakage of code. This +hardens code to a certain degree and also gives performance improvements in case +the operands are of different types. + +### Comparing Objects + +While both `==` and `===` are stated as **equality** operators, they behave +different when at least one of their operands happens to be an `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Here both operators compare for **identity** and **not** equality; that is, they +will compare for the same **instance** of the object, much like `is` in Python +and pointer comparison in C. + +### In Conclusion + +It is highly recommended to only use the **strict equality** operator. In cases +where types need to be coerced, it should be done [explicitly](#types.casting) +and not left to the language's complicated coercion rules. + diff --git a/doc/fi/types/instanceof.md b/doc/fi/types/instanceof.md new file mode 100644 index 00000000..8711331a --- /dev/null +++ b/doc/fi/types/instanceof.md @@ -0,0 +1,38 @@ +## The `instanceof` Operator + +The `instanceof` operator compares the constructors of its two operands. It is +only useful when comparing custom made objects. Used on built-in types, it is +nearly as useless as the [typeof operator](#types.typeof). + +### Comparing Custom Objects + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // This just sets Bar.prototype to the function object Foo + // But not to an actual instance of Foo + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### Using `instanceof` with Native Types + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +One important thing to note here is, that `instanceof` does not work on objects +that origin from different JavaScript contexts (e.g. different documents +in a web browser), since their constructors will not be the exact same object. + +### In Conclusion + +The `instanceof` operator should **only** be used when dealing with custom made +objects that origin from the same JavaScript context. Just like the +[`typeof`](#types.typeof) operator, every other use of it should be **avoided**. + diff --git a/doc/fi/types/typeof.md b/doc/fi/types/typeof.md new file mode 100644 index 00000000..e4b28d7f --- /dev/null +++ b/doc/fi/types/typeof.md @@ -0,0 +1,87 @@ +## The `typeof` Operator + +The `typeof` operator (together with +[`instanceof`](#types.instanceof)) is probably the biggest +design flaw of JavaScript, as it is near of being **completely broken**. + +Although `instanceof` still has its limited uses, `typeof` really has only one +practical use case, which does **not** happen to be checking the type of an +object. + +> **Note:** While `typeof` can also be called with a function like syntax +> i.e. `typeof(obj)`, this is not a function call. The two parenthesis will +> behave like normal and the return value will be used as the operand of the +> `typeof` operator. There is **no** `typeof` function. + +### The JavaScript Type Table + + Value Class Type + ------------------------------------- + "foo" String string + new String("foo") String object + 1.2 Number number + new Number(1.2) Number object + true Boolean boolean + new Boolean(true) Boolean object + new Date() Date object + new Error() Error object + [1,2,3] Array object + new Array(1, 2, 3) Array object + new Function("") Function function + /abc/g RegExp object (function in Nitro/V8) + new RegExp("meow") RegExp object (function in Nitro/V8) + {} Object object + new Object() Object object + +In the above table *Type* refers to the value, that the `typeof` operator returns. +As can be clearly seen, this value is anything but consistent. + +The *Class* refers to the value of the internal `[[Class]]` property of an object. + +> **From the Specification:** The value of `[[Class]]` can be one of the +> following strings. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +In order to retrieve the value of `[[Class]]` one has to make use of the +`toString` method of `Object.prototype`. + +### The Class of an Object + +The specification gives exactly one way of accessing the `[[Class]]` value, +with the use of `Object.prototype.toString`. + + function is(type, obj) { + var clas = Object.prototype.toString.call(obj).slice(8, -1); + return obj !== undefined && obj !== null && clas === type; + } + + is('String', 'test'); // true + is('String', new String('test')); // true + +In the above example, `Object.prototype.toString` gets called with the value of +[this](#function.this) being set to the object whose `[[Class]]` value should be +retrieved. + +> **ES5 Note:** For convenience the return value of `Object.prototype.toString` +> for both `null` and `undefined` was **changed** from `Object` to `Null` and +> `Undefined` in ECMAScript 5. + +### Testing for Undefined Variables + + typeof foo !== 'undefined' + +The above will check whether `foo` was actually declared or not; just +referencing it would result in a `ReferenceError`. This is the only thing +`typeof` is actually useful for. + +### In Conclusion + +In order to check the type of an object, it is highly recommended to use +`Object.prototype.toString`; as this is the only reliable way of doing so. +As shown in the above type table, some return values of `typeof` are not defined +in the specification; thus, they can differ across various implementations. + +Unless checking whether a variable is defined, `typeof` should be avoided at +**all costs**. + + From 67b6a8adfa7baaef4014fbba234958cbad645520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?juho=20Veps=C3=A4l=C3=A4inen?= Date: Sat, 26 Mar 2011 22:03:55 +0200 Subject: [PATCH 121/641] Removed redundant file. Added that there somehow... --- site/zh/index.html | 1763 -------------------------------------------- 1 file changed, 1763 deletions(-) delete mode 100644 site/zh/index.html diff --git a/site/zh/index.html b/site/zh/index.html deleted file mode 100644 index 27798744..00000000 --- a/site/zh/index.html +++ /dev/null @@ -1,1763 +0,0 @@ -JavaScript 秘密花园 -

    简介

    JavaScript 秘密花园是一个不断更新,主要关心 JavaScript 一些古怪用法的文档。 -对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, -初学者可以籍此深入了解 JavaScript 的语言特性。

    - -

    JavaScript 秘密花园不是用来教你 JavaScript。为了更好的理解这篇文章的内容, -你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 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 不包含传统的类继承模型,而是使用 prototypal 原型模型。

    - -

    虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。 -实现传统的类继承模型是很简单,但是实现 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' }
    -        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)。 -然而将原子类型赋给 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 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 -没有其它方法可以用来排除原型链上的属性,而不是定义在对象自身上的属性。

    - -

    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 变量使用标准的数组方法,比如 push, pop 或者 slice。 -虽然使用 for 循环遍历也是可以的,但是为了更好的使用数组方法,最好把它转化为一个真正的数组。

    - -

    转化为数组

    - -

    下面的代码将会创建一个新的数组,包含所有 arguments 对象中的元素。

    - -
    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);
    -};
    -
    -// 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);
    -};
    -
    - -

    译者注:上面的 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);
    -assert(pair[0] === 42);
    -assert(pair[1] === 17);
    -
    - -

    然而,的确有一种情况会显著的影响现代 JavaScript 引擎的性能。这就是使用 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...
    -    }
    -}
    -
    - -

    上面代码中,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(a, b, c);
    -}, 1000)
    -
    - - - -

    结论

    - -

    绝对不要使用字符串作为 setTimeout 或者 setInterval 的第一个参数, -这么写的代码明显质量很差。当需要向回调函数传递参数时,可以创建一个匿名函数,在函数内执行真实的回调函数。

    - -

    另外,应该避免使用 setInterval,因为它的定时执行不会被 JavaScript 阻塞。

    Copyright © 2011. Built with -Node.jsusing a -jadetemplate. -

    \ No newline at end of file From 9f506be95f7f5fb9160daca3caccec4a83e708e8 Mon Sep 17 00:00:00 2001 From: Dmitry Scriptin Date: Sun, 27 Mar 2011 00:37:00 +0300 Subject: [PATCH 122/641] added empty article about delete operator (EN) --- doc/en/core/delete.md | 0 doc/en/index.json | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 doc/en/core/delete.md diff --git a/doc/en/core/delete.md b/doc/en/core/delete.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/en/index.json b/doc/en/index.json index d4e52883..0c078397 100644 --- a/doc/en/index.json +++ b/doc/en/index.json @@ -59,7 +59,8 @@ "articles": [ "eval", "undefined", - "semicolon" + "semicolon", + "delete" ] }, { From bd0f12a7e39d45d314454e3fcbb23213b09c0735 Mon Sep 17 00:00:00 2001 From: Dmitry Scriptin Date: Sun, 27 Mar 2011 03:02:29 +0400 Subject: [PATCH 123/641] added content to operator article --- doc/en/core/delete.md | 87 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/doc/en/core/delete.md b/doc/en/core/delete.md index e69de29b..37a12816 100644 --- a/doc/en/core/delete.md +++ b/doc/en/core/delete.md @@ -0,0 +1,87 @@ +## The `delete` Operator + +In short, it's *impossible* to delete global variables, functions and some other +stuff in JavaScript which have a `DontDelete` attribute set. + +### Global code and Function code + +When a variable or a function is defined in a global +or a [function scope](#function.scopes) it is a property of either +Activation object or Global object. Such properties have a set of attributes, +one of these is `DontDelete`. Variable and function declarations in global +and function code always create properties with `DontDelete`, therefore +cannot be deleted. + + // global variable: + var a = 1; // DontDelete is set + delete a; // false + a; // 1 + + // normal function: + function f() {} // DontDelete is set + delete f; // false + typeof f; // "function" + + // reassigning doesn't help: + f = 1; + delete f; // false + f; // 1 + +### Explicit properties + +There are things which can be deleted normally: these are explicitly set +properties. + + // explicitly set property: + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // true + delete obj.y; // true + obj.x; // undefined + obj.y; // undefined + +In the example above `obj.x` and `obj.y` can be deleted because they have no +`DontDelete` atribute. That's why an example below works too. + + // this works fine, except for IE: + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // true - just a global var + delete GLOBAL_OBJECT.a; // true + GLOBAL_OBJECT.a; // undefined + +Here we use a trick to delete `a`. [`this`](#function.this) here refers +to the Global object and we explicitly declare variable `a` as it's property +which allows us to delete it. + +IE (at least 6-8) has some bugs, so code above doesn't work. + +### Function arguments and built-ins + +Functions' normal arguments, [`arguments` object](#function.arguments) +and built-in properties also have `DontDelete` set. + + // function arguments and properties: + (function (x) { + + delete arguments; // false + typeof arguments; // "object" + + delete x; // false + x; // 1 + + function f(){} + delete f.length; // false + typeof f.length; // "number" + + })(1); + +### Host objects + +Behaviour of `delete` operator can be unpredictable for hosted objects. Due to +specification, host objects are allowed to implement any kind of behavior. + +### In conclusion + +`delete` operator often has an unexpected behaviour and can be safely used +only for dealing with explicitly set properties on normal objects. From 9d9479066972374f7de2df874624c1c90b771cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?juho=20Veps=C3=A4l=C3=A4inen?= Date: Sun, 27 Mar 2011 11:50:52 +0300 Subject: [PATCH 124/641] Second pass. Still three sections to go. --- doc/fi/function/closures.md | 54 ++++------ doc/fi/function/constructors.md | 77 +++++--------- doc/fi/function/general.md | 38 +++---- doc/fi/function/scopes.md | 175 ++++++++++++-------------------- doc/fi/function/this.md | 78 ++++++-------- doc/fi/index.json | 5 +- doc/fi/intro/authors.md | 5 +- doc/fi/intro/contributors.md | 6 +- doc/fi/intro/index.md | 17 +--- doc/fi/intro/license.md | 7 +- doc/fi/intro/translators.md | 5 + 11 files changed, 172 insertions(+), 295 deletions(-) create mode 100644 doc/fi/intro/translators.md diff --git a/doc/fi/function/closures.md b/doc/fi/function/closures.md index 17554dcb..2e0a227a 100644 --- a/doc/fi/function/closures.md +++ b/doc/fi/function/closures.md @@ -1,11 +1,8 @@ -## Closures and References +## Sulkeumat ja viitteet -One of JavaScript's most powerful features is the availability of *closures*, -this means that scopes **always** keep access to the outer scope they were -defined in. Since the only scoping that JavaScript has is -[function scope](#function.scopes), all functions, by default, act as closures. +*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. -### Emulating private variables +### Paikallisten muuttujien emulointi function Counter(start) { var count = start; @@ -24,30 +21,22 @@ defined in. Since the only scoping that JavaScript has is foo.increment(); foo.get(); // 5 -Here, `Counter` returns **two** closures. The function `increment` as well as -the function `get`. Both of these functions keep a **reference** to the scope of -`Counter` and, therefore, always keep access to the `count` variable that was -defined in that very scope. +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. -### Why Private Variables Work +### Miksi paikalliset muuttujat toimivat -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. +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; }; -The above code will **not** change the variable `count` in the scope of `Counter`, -since `foo.hack` was not defined in **that** scope. It will instead create - or -override - the *global* variable `count`. +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`. -### Closures Inside Loops +### Sulkeumat luupeissa -One often made mistake is to use closures inside of loops, as if they were -copying the value of the loops index variable. +Usein sulkeumia käytetään väärin luuppien sisällä indeksimuuttujien arvon kopiointiin. for(var i = 0; i < 10; i++) { setTimeout(function() { @@ -55,20 +44,15 @@ copying the value of the loops index variable. }, 1000); } -The above will **not** output the numbers `0` through `9`, but will simply print -the number `10` ten times. +Yllä oleva koodi **ei** tulosta numeroita `nollasta` `yhdeksään`. Sen sijaan se tulostaa numeron `10` kymmenen kertaa. -The *anonymous* function keeps a **reference** to `i` and at the time -`console.log` gets called, the `for loop` has already finished and the value of -`i` as been set to `10`. +*Nimetön* funktio saa **viitteen** `i`-muuttujaan `console.log`-kutsuhetkellä. Tällöin luuppi on jo suoritettu ja `i`:n arvoksi on asetettu `10`. -In order to get the desired behavior, it is necessary to create a **copy** of -the value of `i`. +Päästäksemme haluttuun lopputulokseen on tarpeen luoda **kopio** `i`:n arvosta. -### Avoiding the Reference Problem +### Viiteongelman välttäminen -In order to copy the value of the loop's index variable, it is best to use an -[anonymous wrapper](#function.scopes). +Voimme välttää ongelman käyttämällä [nimetöntä käärettä](#function.scopes). for(var i = 0; i < 10; i++) { (function(e) { @@ -78,15 +62,11 @@ In order to copy the value of the loop's index variable, it is best to use an })(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`. +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`. -The anonymous function that gets passed to `setTimeout` now has a reference to -`e`, whose value does **not** get changed by the loop. +Nimetön funktio, jolle annetaan `setTimeout` sisältää nyt viitteen `e`:hen, jonka arvoa luuppi **ei** muuta. -There is another possible way of achieving this; that is to return a function -from the anonymous wrapper, that will then have the same behavior as the code -above. +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) { diff --git a/doc/fi/function/constructors.md b/doc/fi/function/constructors.md index ad90b028..87da08ec 100644 --- a/doc/fi/function/constructors.md +++ b/doc/fi/function/constructors.md @@ -1,15 +1,10 @@ -## Constructors +## Konstruktorit -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. +JavaScriptin konstruktorit eroavat monista muista kielistä selvästi. Jokainen funktiokutsu, joka sisältää avainsanan `new` toimii konstruktorina. -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. +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. -If the function that was called has no explicit `return` statement, then it -implicitly returns the value of `this` - the new object. +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; @@ -21,16 +16,14 @@ implicitly returns the value of `this` - the new object. var test = new Foo(); -The above calls `Foo` as constructor and sets the `prototype` of the newly -created object to `Foo.prototype`. +Yllä `Foo`:ta kutsutaan konstruktorina. Juuri luodun olion `prototyyppi` asetetaan osoittamaan ominaisuuteen `Foo.prototype`. -In case of an explicit `return` statement the function returns the value -specified that statement, **but only** if the return value is an `Object`. +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(); // a new object + new Bar(); // uusi olio function Test() { this.value = 2; @@ -39,23 +32,20 @@ specified that statement, **but only** if the return value is an `Object`. foo: 1 }; } - new Test(); // the returned object + new Test(); // palautettu olio -When the `new` keyword is omitted, the function will **not** return a new object. +Mikäli `new`-avainsanaa ei käytetä, funktio **ei** palauta uutta oliota. function Foo() { - this.bla = 1; // gets set on the global object + this.bla = 1; // asetetaan globaalisti } Foo(); // undefined -While the above example might still appear to work in some cases, due to the -workings of [`this`](#function.this) in JavaScript, it will use the -*global object* as the value of `this`. +Vaikka yllä oleva esimerkki saattaa näyttää toimivan joissain tapauksissa, viittaa [`this`](#function.this) globaalin olion `this`-ominaisuuteen. -### Factories +### Tehtaat -In order to be able to omit the `new` keyword, the constructor function has to -explicitly return a value. +Mikäli `new`-avainsanan käyttöä halutaan välttää, voidaan konstruktori pakottaa palauttamaan arvo. function Bar() { var value = 1; @@ -72,25 +62,17 @@ explicitly return a value. new Bar(); Bar(); -Both calls to `Bar` return the exact same thing, a newly create object which -has a property called `method` on it, that is a -[Closure](#function.closures). +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). -It is also to note that the call `new Bar()` does **not** affect the prototype -of the returned object. While the prototype will be set on the newly created -object, `Bar` never returns that new object. +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. -In the above example, there is no functional difference between using and -not using the `new` keyword. +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 -### Creating New Objects via Factories +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. -An often made recommendation is to **not** use `new` since forgetting its use -may lead to bugs. - -In order to create new object, one should rather use a factory and construct a -new object inside of that factory. +Sen sijaan suositellaan käytettävän tehdasta, jonka sisällä varsinainen olio konstruoidaan. function Foo() { var obj = {}; @@ -107,22 +89,13 @@ new object inside of that factory. return obj; } -While the above is robust against a missing `new` keyword and certainly makes -the use of [private variables](#function.closures) easier, it comes with some -downsides. +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. It uses more memory since the created objects do **not** share the methods - on a prototype. - 2. In order to inherit the factory needs to copy all the methods from another - object or put that object on the prototype of the new object. - 3. Dropping the prototype chain just because of a left out `new` keyword - somehow goes against the spirit of the language. + 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. -### In Conclusion +### Yhteenveto -While omitting the `new` keyword might lead to bugs, it is certainly **not** a -reason to drop the use of prototypes altogether. In the end it comes down to -which solution is better suited for the needs of the application, it is -especially important to choose a specific style of object creation **and stick** -with it. +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/doc/fi/function/general.md b/doc/fi/function/general.md index 64c9e0c9..2e038220 100644 --- a/doc/fi/function/general.md +++ b/doc/fi/function/general.md @@ -1,48 +1,38 @@ -## Function Declarations and Expressions +## Funktiomääreet ja lausekkeet -Functions in JavaScript are first class objects. That means they can be -passed around like any other value. One common use of this feature is to pass -an *anonymous function* as a callback to another, possibly asynchronous function. +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*. -### The `function` Declaration +### `function`-määre function foo() {} -The above function gets [hoisted](#function.scopes) before the execution of the -program starts; thus, it is available *everywhere* in the scope it was *defined* -in, even if called before the actual definition in the source. +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(); // Works because foo was created before this code runs + foo(); // Toimii, koska foo on luotu ennen kuin koodi suoritetaan function foo() {} -### The `function` Expression +### `function`-lauseke var foo = function() {}; -This example assigns the unnamed and *anonymous* function to the variable `foo`. +Tämä esimerkki asettaa nimeämättömän ja *nimettömän* funktion muuttujan `foo` arvoksi. foo; // 'undefined' - foo(); // this raises a TypeError + foo(); // tämä palauttaa TypeError-virheen var foo = function() {}; -Due to the fact that `var` is a declaration, that hoists the variable name `foo` -before the actual execution of the code starts, `foo` is already defined when -the script gets executed. +`var` on määre. Tästä johtuen se hilaa muuttujanimen `foo` ennen kuin itse koodia ryhdytään suorittamaan. -But since assignments only happen at runtime, the value of `foo` will default -to [undefined](#core.undefined) before the corresponding code is executed. +Sijoituslauseet suoritetaan *vasta* kun niihin saavutaan. Tästä johtuen `foo` saa arvokseen [undefined](#core.undefined) ennen kuin varsinaista sijoitusta päästään suorittamaan. -### Named Function Expression +### Nimetty funktiolauseke -Another special case is the assignment of named functions. +Nimettyjen funktioiden sijoitus tarjoaa toisen erikoistapauksen. var foo = function bar() { - bar(); // Works + bar(); // Toimii } 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. +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/doc/fi/function/scopes.md b/doc/fi/function/scopes.md index 7ae5e4f5..be7b56d3 100644 --- a/doc/fi/function/scopes.md +++ b/doc/fi/function/scopes.md @@ -1,88 +1,69 @@ -## Scopes and Namespaces +## Näkyvyysalueet ja nimiavaruudet -Although JavaScript deals fine with the syntax of two matching curly -braces for blocks, it does **not** support block scope; hence, all that is left -is in the language is *function scope*. +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() { // a scope - for(var i = 0; i < 10; i++) { // not a scope + function test() { // näkyvyysalue + for(var i = 0; i < 10; i++) { // tämä ei ole näkyvyysalue // 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. +> **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ä. -There are also no distinct namespaces in JavaScript, that means that everything -gets defined in one *globally shared* namespace. +JavaScript ei myöskään sisällä erityistä tukea nimiavaruuksille. Tämä tarkoittaa sitä, että kaikki määritellään oletuksena *globaalissa* nimiavaruudessa. -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`. +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. -### The Bane of Global Variables +### Riesa nimeltä globaalit muuttujat - // script A + // skripti A foo = '42'; - // script B + // skripti 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. +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. -Again, that is **not** at all the *same effect*, not using `var` can have major -implications. +Tämä **ei** ole **sama asia**. `var`-avainsanan käyttämättä jättäminen voi johtaa vakaviin seurauksiin. - // global scope + // globaali näkyvyysalue var foo = 42; function test() { - // local scope + // paikallinen näkyvyysalue foo = 21; } test(); foo; // 21 -Leaving out the `var` statement inside the function `test` will override the -value of `foo`. While this might not seem like a big deal at first, having -thousands of lines of JavaScript and not using `var` will introduce horrible and -hard to track down bugs. - - // global scope - var items = [/* some list */]; +`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() { - // scope of subLoop - for(i = 0; i < 10; i++) { // missing var statement - // do amazing stuff! + // aliluupin näkyvyysalue + for(i = 0; i < 10; i++) { // hups, var jäi pois + // jotain makeaa ja hienoa } } - -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 +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 -The only source for local variables in JavaScript are -[function](#function.general) parameters and variables that were declared via the -`var` statement. +Ainoastaan [funktion](#function.general) parametrit ja muuttujat, jotka sisältävät `var`-määreen ovat paikallisia. - // global scope + // globaali näkyvyysalue var foo = 1; var bar = 2; var i = 2; function test(i) { - // local scope of the function test + // paikallinen näkyvyysalue i = 5; var foo = 3; @@ -90,13 +71,11 @@ The only source for local variables in JavaScript are } 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. +`foo` ja `i` ovatkin `test`-funktiolle paikallisia. `bar` sijoitus muuttaa globaalin muuttujan arvoa. -### Hoisting +### Hilaaminen -JavaScript **hoists** declarations. This means that both `var` statements and -`function` declarations will be moved to the top of their enclosing scope. +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() {}; @@ -115,16 +94,14 @@ JavaScript **hoists** declarations. This means that both `var` statements and } } -The above code gets transformed before any execution is started. JavaScript moves -the `var` statements as well as the `function` declarations to the top of the -nearest surrounding scope. +Yllä olevaa koodia muutetaan ennen suoritusta. JavaScript siirtää `var`-lausekkeet ja `function`-määreet lähimmän näkyvyysalueen huipulle. - // var statements got moved here - var bar, someValue; // default to 'undefined' + // var-lausekkeet siirrettiin tänne + var bar, someValue; // oletuksena 'undefined' - // the function declartion got moved up too + // myös funktio-määre siirtyi tänne function test(data) { - var goo, i, e; // missing block scope moves these here + var goo, i, e; // ei blokkinäkyvyyttä, siirretään siis tänne if (false) { goo = 1; @@ -136,96 +113,78 @@ nearest surrounding scope. } } - bar(); // fails with a TypeError since bar is still 'undefined' - someValue = 42; // assignments are not affected by hoisting + bar(); // TypeError-virhe, baria ei ole vielä määritelty + someValue = 42; // hilaus ei koske sijoituksia 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. +Sen lisäksi, että puuttuva blokkinäkyvyys siirtää `var`-lausekkeet luuppien ulkopuolelle, tekee se myös eräistä `if`-rakenteista vaikeita käsittää. -In the original code the `if` statement seemed to modify the *global -variable* `goo`, while actually it modifies the *local variable* - after hoisting -has been applied. +Alkuperäisessä koodissa `if`-lause näytti muokkaavan *globaalia muuttujaa* `goo`. Todellisuudessa se muokkaa *paikallista muuttujaa* varsinaisen hilauksen jälkeen. -Without the knowledge about *hoisting*, below code might seem to raise a -`ReferenceError`. +Seuraava koodi saattaisi ensi näkemältä aiheuttaa `ReferenceError`-virheen. Näin ei kuitenkaan tapahdu *hilauksen* ansiosta. - // check whether SomeImportantThing has been initiliazed + // onko SomeImportantThing alustettu if (!SomeImportantThing) { var SomeImportantThing = {}; } -But of course, the above works due to the fact that the `var` statement is being -moved to the top of the *global scope*. +Tämä toimii, koska `var`-lauseke on hilattu *globaalin näkyvyysalueen* huipulle. var SomeImportantThing; - // other code might initiliaze SomeImportantThing here, or not + // mahdollista alustuskoodia - // make sure it's there + // onhan se alustettu if (!SomeImportantThing) { SomeImportantThing = {}; } -### Name Resolution Order +### Nimienerottelujärjestys -All scopes in JavaScript, including the *global scope*, have the special name -[`this`](#function.this) defined in them, which refers to the *current object*. +Kaikki JavaScriptin näkyvyysalueet, *globaalin näkyvyysalue* mukaanlukien, sisältävät erikoismuuttujan [`this`](#function.this). `this` viittaa *tämänhetkiseen olioon*. -Function scopes also have the name [`arguments`](#function.arguments) defined in -them which contains the arguments that were passed to a function. +Funktioiden näkyvyysalueet sisältävät myös [`arguments`](#function.arguments)-olion. Se sisältää funktiolle annetut argumentit. -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: +Mikäli näkyvyysalueen sisällä pyritään pääsemään käsiksi esimerkiksi `foo`:n arvoon JavaScript käyttäytyy seuraavasti: - 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. + 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. -> **Note:** Having a parameter called `arguments` will **prevent** the creation -> of the default `arguments` object. +> **Huomio:** Mikäli funktio sisältää `arguments`-nimisen parametrin, estää se `arguments`-olion luonnin kokonaan. -### Namespaces +### Nimiavaruudet -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*. +Globaalin nimiavaruuden ongelmana voidaan pitää nimitörmäyksiä. JavaScriptissä tätä ongelmaa voidaan kiertää käyttämällä *nimettömiä kääreitä*. (function() { - // a self contained "namespace" + // "nimiavaruus" itsessään window.foo = function() { - // an exposed closure + // paljastettu sulkeuma }; - })(); // execute the function immediately - + })(); // suorita funktio heti -Unnamed functions are considered [expressions](#function.general); so in order to -being callable, they must first be evaluated. +Nimettömiä funktioita pidetään [lauseina](#function.general). Jotta niitä voidaan kutsua, tulee ne suorittaa ensin. - ( // evaluate the function inside the paranthesis + ( // suorita sulkeiden sisältämä funktio function() {} - ) // and return the function object - () // call the result of the evaluation + ) // ja palauta funktio-olio + () // kutsu suorituksen tulosta -There are other ways for evaluating and calling the function expression; which, -while different in syntax, do behave the exact same way. +Samaan lopputulokseen voidaan päästä myös hieman eri syntaksia käyttäen. - // Two other ways + // Kaksi muuta tapaa +function(){}(); (function(){}()); -### In Conclusion +### Yhteenveto -It is recommended to always use an *anonymous wrapper* for encapsulating code in -its own namespace. This does not only protect code against name clashes, it -also allows for better modularization of programs. +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. -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. +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/doc/fi/function/this.md b/doc/fi/function/this.md index 0cff9ee4..3ebf0a7b 100644 --- a/doc/fi/function/this.md +++ b/doc/fi/function/this.md @@ -1,101 +1,82 @@ -## How `this` Works +## Kuinka `this` toimii -JavaScript has a different concept of what the special name `this` refers to -than most other programming languages do. There are exactly **five** different -ways in which the value of `this` can be bound in the language. +JavaScripting `this` toimii eri tavoin kuin useimmissa kielissä. Tarkalleen ottaen on olemassa **viisi** eri tapaa, joiden mukaan sen arvo voi määrittyä. -### The Global Scope +### Globaali näkyvyysalue this; -When using `this` in global scope, it will simply refer to the *global* object. +Kun `this`-muuttujaa käytetään globaalissa näkyvyysalueessa, viittaa se *globaaliin* olioon. - -### Calling a Function +### Funktiokutsu foo(); -Here `this` will again refer to the *global* object. +Tässä tapauksessa `this` viittaa jälleen *globaaliin* olioon. -> **ES5 Note:** In strict mode, the global case **no longer** exists. -> `this` will instead have the value of `undefined` in that case. +> **ES5 Huomio:** Globaalia tapausta ei ole **enää** olemassa, kun käytetään tiukkaa moodia. Sen sijaan `this` saa arvon `undefined`. -### Calling a Method +### Metodikutsu test.foo(); -In this example `this` will refer to `test`. +Tässä esimerkissä `this` viittaa `test`-olioon. -### Calling a Constructor +### Konstruktorikutsu 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`. +Funktiokutsu, jota edeltää `new`-avainsana toimii [konstruktorina](#function.constructors). Funktion sisällä `this` viittaa *juuri luotuun* `Object`-olioon. -### Explicit Setting of `this` +### `this`-arvon asettaminen 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 + foo.apply(bar, [1, 2, 3]); // taulukko laajenee alla olevaksi + foo.call(bar, 1, 2, 3); // tuloksena 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. +`Function.prototype`-olion `call`- ja `apply`-metodeita käytettäessä `this`-ominaisuuden arvo määrittyy ensimmäisen annetun argumentin perusteella. -As a result, the above example the *method case* does **not** apply, and `this` -inside of `foo` will be set to `bar`. +Seurauksena `foo`-funktion sisältämä `this` asettuu `bar`-olioon toisin kuin perustapauksessa. -> **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. +> **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. -### Common Pitfalls +### Yleisiä ongelmakohtia -While most of these cases make sense, the first one is to be considered another -mis-design of the language, as it **never** has any practical use. +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 is set to the global object + // this asettuu globaaliin olioon } test(); } -A common misconception is that `this` inside of `test` refers to `Foo`, while in -fact it **does not**. +Yleisesti luullaan, että test-funktion sisältämä `this` viittaa tässä tapauksessa `Foo`-olioon. Todellisuudessa se **ei** kuitenkaan tee näin. -In order to gain access to `Foo` from within `test` it is necessary to create a -local variable inside of `method` which refers to `Foo`. +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() { - // Use that instead of this here + // Käytä thatia thissin sijasta } test(); } -`that` is just a normal 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. +`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. -### Assigning Methods +### Metodien sijoittaminen -Another thing that does **not** work in JavaScript is function aliasing, that is, -**assigning** a method to a variable. +JavaScriptissä funktioita **ei** voida nimetä uudelleen eli siis sijoittaa **edelleen**. var test = someObject.methodTest; test(); -Due to the first case `test` now acts like like a plain function call; therefore, -`this` inside it will no longer refer to `someObject`. +Ensimmäisestä tapauksesta johtuen `test` toimii kuten normaali funktiokutsu; tällöin sen sisältämä `this` ei enää osoita `someObject`-olioon. -While the late binding of `this` might seem like a bad idea at first, it is in -fact what makes [prototypal inheritance](#object.prototype) work. +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() {}; @@ -105,7 +86,6 @@ fact what makes [prototypal inheritance](#object.prototype) work. new Bar().method(); -When `method` gets called on a instance of `Bar`, `this` will now refer to that -very instance. +Kun `method`-metodia kutsutaan `Bar`-oliossa, sen `this` viittaa juurikin tuohon olioon. diff --git a/doc/fi/index.json b/doc/fi/index.json index da354376..cee13c41 100644 --- a/doc/fi/index.json +++ b/doc/fi/index.json @@ -1,6 +1,6 @@ { - "title": "JavaScript Puutarha", - "langTitle": "JavaScript Puutarha suomeksi", + "title": "JavaScript-puutarha", + "langTitle": "JavaScript-puutarha suomeksi", "description": "Opas JavaScriptin outouksiin ja vikoihin ", "sections": [ { @@ -9,6 +9,7 @@ "articles": [ "authors", "contributors", + "translators", "license" ] }, diff --git a/doc/fi/intro/authors.md b/doc/fi/intro/authors.md index 684ca097..0d557519 100644 --- a/doc/fi/intro/authors.md +++ b/doc/fi/intro/authors.md @@ -1,7 +1,6 @@ -## The Authors +## Tekijät -This guide is the work of two lovely [Stack Overflow][3] users, [Ivo Wetzel][1] -(Writing) and [Zhang Yi Jiang][2] (Design). +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 diff --git a/doc/fi/intro/contributors.md b/doc/fi/intro/contributors.md index 73fb98ea..7147d319 100644 --- a/doc/fi/intro/contributors.md +++ b/doc/fi/intro/contributors.md @@ -1,7 +1,7 @@ -## Contributors +## Osallistujat - - [Caio Romão][1] (Spelling corrections) - - [Andreas Blixt][2] (Language corrections) + - [Caio Romão][1] (oikeinkirjoituskorjauksia) + - [Andreas Blixt][2] (kielikorjauksia) [1]: https://github.com/caio [2]: https://github.com/blixt diff --git a/doc/fi/intro/index.md b/doc/fi/intro/index.md index f2d5d31b..f79ac337 100644 --- a/doc/fi/intro/index.md +++ b/doc/fi/intro/index.md @@ -1,15 +1,8 @@ -## Intro - -**JavaScript Garden** is a growing collection of documentation about the most -quirky parts of the JavaScript programming language. It gives advice to -avoid common mistakes, subtle bugs, as well as performance issues and bad -practices that non-expert JavaScript programmers may encounter on their -endeavours into the depths of the language. - -JavaScript Garden does **not** aim to teach you JavaScript. Former knowledge -of the language is strongly recommended in order to understand the topics covered -in this guide. In order to learn the basics of the language, please head over to -the excellent [guide][1] on the Mozilla Developer Network. +## 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/doc/fi/intro/license.md b/doc/fi/intro/license.md index cd398699..09ed4d4c 100644 --- a/doc/fi/intro/license.md +++ b/doc/fi/intro/license.md @@ -1,9 +1,6 @@ -## License +## Lisenssi -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. +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 diff --git a/doc/fi/intro/translators.md b/doc/fi/intro/translators.md new file mode 100644 index 00000000..62ae637d --- /dev/null +++ b/doc/fi/intro/translators.md @@ -0,0 +1,5 @@ +## Kääntäjät + + - [Juho Vepsäläinen][1] + +[1]: https://github.com/bebraw From 491468c278e072d4a73280d9e6b5ec77be23f0bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?juho=20Veps=C3=A4l=C3=A4inen?= Date: Sun, 27 Mar 2011 14:50:43 +0300 Subject: [PATCH 125/641] Final pass done. --- doc/fi/object/forinloop.md | 42 ++++------- doc/fi/object/general.md | 69 +++++++----------- doc/fi/object/hasownproperty.md | 38 ++++------ doc/fi/object/prototype.md | 100 +++++++++------------------ doc/fi/other/timeouts.md | 119 +++++++++++--------------------- doc/fi/types/casting.md | 81 ++++++++++------------ doc/fi/types/equality.md | 91 +++++++++++------------- doc/fi/types/instanceof.md | 39 +++++------ doc/fi/types/typeof.md | 69 ++++++------------ 9 files changed, 239 insertions(+), 409 deletions(-) diff --git a/doc/fi/object/forinloop.md b/doc/fi/object/forinloop.md index 30751ed9..15c9fc64 100644 --- a/doc/fi/object/forinloop.md +++ b/doc/fi/object/forinloop.md @@ -1,51 +1,37 @@ -## The `for in` Loop +## `for in`-luuppi -Just like the `in` operator, the `for in` loop also traverses the prototype -chain when iterating over the properties of an object. +Aivan kuten `in`-operaattori, myös `for in`-luuppi käy olion prototyyppiketjun läpi iteroidessaan sen ominaisuuksia. -> **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 +> **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); // prints both bar and moo + console.log(i); // tulostaa sekä bar että moo } -Since it is not possible to change the behavior of the `for in` loop itself, it -is necessary to filter out the unwanted properties inside the loop body , -this is done by using the [`hasOwnProperty`](#object.hasownproperty) method of -`Object.prototype`. +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. -> **Note:** Since the `for in` always traverses the complete prototype chain, it -> will get slower with each additional layer of inheritance added to an object. +> **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. -### Using `hasOwnProperty` for Filtering +### `hasOwnProperty`-metodin käyttäminen karsimiseen - // still the foo from above + // foo kuten yllä for(var i in foo) { if (foo.hasOwnProperty(i)) { console.log(i); } } -This version is the only correct one to use. Due to the use of `hasOwnProperty` it -will **only** print out `moo`. When `hasOwnProperty` is left out, the code is -prone to errors in cases where the native prototypes - e.g. `Object.prototype` - -have been extended. +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. -One widely used framework which does this is [Prototype][1]. When this -framework is included, `for in` loops that do not use `hasOwnProperty` are -guaranteed to break. +[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. -### In Conclusion +### Yhteenveto -It is recommended to **always** use `hasOwnProperty`. Never should any -assumptions be made about the environment the code is running in, or whether the -native prototypes have been extended or not. +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/doc/fi/object/general.md b/doc/fi/object/general.md index cbad5f11..c1099897 100644 --- a/doc/fi/object/general.md +++ b/doc/fi/object/general.md @@ -1,7 +1,6 @@ -## Object Usage and Properties +## Olioiden käyttö ja ominaisuudet -Everything in JavaScript acts like an object, with the only two exceptions being -[`null`](#core.undefined) and [`undefined`](#core.undefined). +Kaikki muuttujat, kahta poikkeusta lukuunottamatta, käyttäytyvät JavaScriptissä kuten olio. Nämä poikkeukset ovat [`null`](#core.undefined) sekä [`undefined`](#core.undefined). false.toString() // 'false' [1, 2, 3].toString(); // '1,2,3' @@ -10,38 +9,31 @@ Everything in JavaScript acts like an object, with the only two exceptions being Foo.bar = 1; Foo.bar; // 1 -A common misconception is that number literals cannot be used as -objects. That is because a flaw in JavaScript's parser tries to parse the *dot -notation* on a number as a floating point literal. +Yleisesti luullaan ettei numeroliteraaleja voida käyttää olioina. Tämä johtuu viasta JavaScriptin parserissa. Se yrittää parsia numeron *pistenotaatiota* liukulukuliteraalina. - 2.toString(); // raises SyntaxError + 2.toString(); // palauttaa SyntaxError-virheen -There are a couple of workarounds which can be used in order make number -literals act as objects too. +Tämä voidaan välttää esimerkiksi seuraavasti. - 2..toString(); // the second point is correctly recognized - 2 .toString(); // note the space left to the dot - (2).toString(); // 2 is evaluated first + 2..toString(); // toinen piste tunnistuu oikein + 2 .toString(); // huomaa pisteen vasemmalla puolen oleva väli + (2).toString(); // 2 arvioidaan ensi -### Objects as a Data Type +### Oliot tietotyyppinä -Objects in JavaScript can also be used as a [*Hashmap*][1], they mainly consist -of named properties mapping to values. +JavaScriptin olioita voidaan käyttää myös [*hajautustauluna*][1], koska ne muodostavat pääasiassa avaimien ja niihin liittyvien arvojen välisen mappauksen. -Using a object literal - `{}` notation - it is possible to create a -plain object. This new object [inherits](#object.prototype) from `Object.prototype` and -has no [own properties](#object.hasownproperty) defined on it. +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 = {}; // a new empty object + var foo = {}; // uusi, tyhjä olio - // a new object with a property called 'test' with value 12 + // uusi, tyhjä olio, joka sisältää ominaisuuden 'test' arvolla 12 var bar = {test: 12}; -### Accessing Properties +### Pääsy ominaisuuksiin + +Olion ominaisuuksiin voidaan päästä käsiksi kahta eri tapaa käyttäen. Siihen voidaan käyttää joko piste- tai hakasulkunotaatiota. -The properties of an object can be accessed in two ways, via either the dot -notation, or the square bracket notation. - var foo = {name: 'Kitten'} foo.name; // kitten foo['name']; // kitten @@ -50,17 +42,13 @@ notation, or the square bracket notation. foo[get]; // kitten foo.1234; // SyntaxError - foo['1234']; // works + foo['1234']; // toimii -Both notations are identical in their workings, with the only difference being that -the square bracket notation allows for dynamic setting of properties, as well as -the use of property names that would otherwise lead to a syntax error. +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. -### Deleting Properties +### Ominaisuuksien poistaminen -The only way to actually remove a property from an object is to use the `delete` -operator; setting the property to `undefined` or `null` only remove the -*value* associated with the property, but not the *key*. +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, @@ -77,23 +65,18 @@ operator; setting the property to `undefined` or `null` only remove the } } -The above outputs both `bar undefined` and `foo null` - only `baz` was -removed and is therefore missing from the output. +Yllä oleva koodi tulostaa sekä `both undefined` että `foo null`. Ainoastaan `baz` on poistettu. Täten sitä ei myöskään näy tulosteessa. -### Notation of Keys +### Avainnotaatio var test = { - 'case': 'I am a keyword so I must be notated as a string', - delete: 'I am a keyword too so me' // raises SyntaxError + 'case': 'Olen avainsana, joten minun tulee olla merkkijono', + delete: 'Myös minä olen avainsana' // palauttaa SyntaxError-virheen }; -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. +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. -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. +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/doc/fi/object/hasownproperty.md b/doc/fi/object/hasownproperty.md index 319a8801..6da1a5b4 100644 --- a/doc/fi/object/hasownproperty.md +++ b/doc/fi/object/hasownproperty.md @@ -1,17 +1,12 @@ ## `hasOwnProperty` -In order to check whether a object has a property defined on *itself* and **not** -somewhere on its [prototype chain](#object.prototype), it is necessary to use the -`hasOwnProperty` method which all objects inherit from `Object.prototype`. +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. -> **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`. +> **Huomio:** **Ei** riitä tarkistaa vain että ominaisuuden arvo on `undefined`. Ominaisuus voi hyvinkin olla olemassa. Sen arvoksi on vain satuttu asettamaan `undefined`. -`hasOwnProperty` is the only thing in JavaScript which deals with properties and -does **not** traverse the prototype chain. +`hasOwnProperty` on ainut JavaScriptin sisältämä metodi, joka käsittelee ominaisuuksia **eikä** käy prototyyppiketjun sisältöä läpi. - // Poisoning Object.prototype + // Object.prototypen myrkyttäminen Object.prototype.bar = 1; var foo = {goo: undefined}; @@ -21,33 +16,26 @@ does **not** traverse the prototype chain. foo.hasOwnProperty('bar'); // false foo.hasOwnProperty('goo'); // true -Only `hasOwnProperty` will give the correct and expected result, this is -essential when iterating over the properties of any object. There is **no** other -way to exclude properties that are not defined on the object *itself*, but -somewhere on its prototype chain. +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` as a Property +### `hasOwnProperty` ominaisuutena -JavaScript does **not** protect the property name `hasOwnProperty`; thus, if the -possibility exists that an object might have a property with this name, it is -necessary to use an *external* `hasOwnProperty` in order to get correct results. +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: 'Here be dragons' + bar: 'Olkoon vaikka lohikäärmeitä' }; - foo.hasOwnProperty('bar'); // always returns false + foo.hasOwnProperty('bar'); // palauttaa aina false - // Use another Object's hasOwnProperty and call it with 'this' set to foo + // Käytä toisen olion hasOwnProperty-metodia ja kutsu sitä asettamalla + // 'this' foohon ({}).hasOwnProperty.call(foo, 'bar'); // true -### In Conclusion +### Yhteenveto -When checking for the existence of a property on a object, `hasOwnProperty` is -the **only** method of doing so. It is also recommended to make `hasOwnProperty` -part of **every** [`for in` loop](#object.forinloop), this will avoid errors from -extended native [prototypes](#object.prototype). +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/doc/fi/object/prototype.md b/doc/fi/object/prototype.md index f780eba2..92903d7e 100644 --- a/doc/fi/object/prototype.md +++ b/doc/fi/object/prototype.md @@ -1,24 +1,14 @@ -## The Prototype +## Prototyyppi -JavaScript does not feature a classical inheritance model, instead it uses a -*prototypal* one. +JavaScript ei sisällä klassista perintämallia. Sen sijaan se käyttää *prototyyppeihin* pohjautuvaa ratkaisua. -While this is often considered to be one of JavaScript's weaknesses, the -prototypal inheritance model is in fact more powerful than the classic model. -It is for example fairly trivial to build a classic model on top of it, while the -other way around is a far more difficult task. +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. -Due to the fact that JavaScript is basically the only widely used language that -features prototypal inheritance, it takes some time to adjust to the -differences between the two models. +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. -The first major difference is that inheritance in JavaScript is done by using so -called *prototype chains*. +Ensimmäinen suuri ero liittyy siihen, kuinka perintä toimii. JavaScriptissä se pohjautuu erityisiin *prototyyppiketjuihin*. -> **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. +> **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; @@ -29,86 +19,60 @@ called *prototype chains*. function Bar() {} - // Set Bar's prototype to a new instance of Foo + // Aseta Barin prototyypin uuteen Foo-olioon Bar.prototype = new Foo(); - Bar.prototype.foo = 'Hello World'; + Bar.prototype.foo = 'Terve maailma'; - // Make sure to list Bar as the actual constructor + // Huolehdi siitä, että Bar on todellinen konstruktori Bar.prototype.constructor = Bar; - var test = new Bar() // create a new bar instance + var test = new Bar() // luo uusi bar - // The resulting prototype chain - test [instance of Bar] - Bar.prototype [instance of Foo] - { foo: 'Hello World' } + // Prototyyppiketju + test [Bar-olio] + Bar.prototype [Foo-olio] + { foo: 'Terve maailma' } Foo.prototype { method: ... } Object.prototype { toString: ... /* etc. */ } -In the above, the object `test` will inherit from both `Bar.prototype` and -`Foo.prototype`; hence, it will have access to the function `method` that was -defined on `Foo`. It will also have access to the property `value` of the -**one** `Foo` instance that is its prototype. It is important to note that `new -Bar()` does **not** create a new `Foo` instance, but reuses the one assigned to -its prototype; thus, all `Bar` instances will share the **same** `value` property. +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. -> **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. +> **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. -### Property Lookup +### Ominaisuushaut -When accessing the properties of an object, JavaScript will traverse the -prototype chain **upwards** until it finds a property with the requested name. +Kun olion ominaisuuksien arvoa haetaan, JavaScript käy prototyyppiketjua läpi **ylöspäin**, kunnes se löytää ominaisuuden nimeä vastaavan arvon. -When it reaches the top of the chain - namely `Object.prototype` - and still -hasn't found the specified property, it will return the value -[undefined](#core.undefined) instead. +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. -### The Prototype Property +### Prototyyppi-ominaisuus -While the prototype property is used by the language to build the prototype -chains, it is still possible to assign **any** given value to it. Although -primitives will simply get ignored when assigned as a prototype. +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; // no effect + Foo.prototype = 1; // ei vaikutusta -Assigning objects, as shown in the example above, will work, and allows for dynamic -creation of prototype chains. +Kuten esimerkissä yllä, prototyyppiin on mahdollista asettaa olioita. Tällä tavoin prototyyppiketjuja voidaan koostaa dynaamisesti. -### Performance +### Suorituskyky -The lookup time for properties that are high up on the prototype chain can have a -negative impact on performance critical sections of code. Additionally, trying to -access non-existent properties will always traverse the full prototype chain. +Prototyyppiketjussa korkealla olevien ominaisuuksien hakeminen voi hidastaa koodin kriittisiä osia. Tämän lisäksi olemattomien ominaisuuksien hakeminen käy koko ketjun läpi. -Also, when [iterating](#object.forinloop) over the properties of an object -**every** property that is on the prototype chain will get enumerated. +Ominaisuuksia [iteroidessa](#object.forinloop) prototyyppiketjun **jokainen** ominaisuus käydään läpi. -### Extension of Native Prototypes +### Natiivien prototyyppien laajentaminen -One mis-feature that is often used is to extend `Object.prototype` or one of the -other built in prototypes. +JavaScript mahdollistaa `Object.prototype`-olion sekä muiden natiivityyppien laajentamisen. -This technique is called [monkey patching][1] and breaks *encapsulation*. While -used by widely spread frameworks such as [Prototype][2], there is still no good -reason for cluttering built-in types with additional *non-standard* functionality. +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. -The **only** good reason for extending a built-in prototype is to backport -the features of newer JavaScript engines; for example, -[`Array.forEach`][3]. +**Ainut** hyvä syy on uudempien JavaScript-tulkkien sisältämien ominaisuuksien siirtäminen vanhemmille alustoille. Eräs esimerkki tästä on [`Array.forEach`][3]. -### In Conclusion +### Yhteenveto -It is a **must** to understand the prototypal inheritance model completely -before writing complex code which makes use of it. Also, watch the length of -the prototype chains and break them up if necessary to avoid possible -performance issues. Further, the native prototypes should **never** be extended -unless it is for the sake of compatibility with newer JavaScript features. +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/ diff --git a/doc/fi/other/timeouts.md b/doc/fi/other/timeouts.md index d196a3b7..ba361f54 100644 --- a/doc/fi/other/timeouts.md +++ b/doc/fi/other/timeouts.md @@ -1,155 +1,114 @@ -### `setTimeout` and `setInterval` +### `setTimeout` ja `setInterval` -Since JavaScript is asynchronous, it is possible to schedule the execution of a -function by using the `setTimeout` and `setInterval` functions. +Koska JavaScript on luonteeltaan asynkroninen, voidaan funktioiden suoritusta ajastaa käyttäen `setTimeout` sekä `setInterval`-funktioita. -> **Note:** Timeouts are **not** part of the ECMAScript Standard. They are -> implemented as part of the [DOM][1]. +> **Huomio:** Aikakatkaisufunktiot **eivät** ole osa ECMAScript-standardia. Ne on toteutettu osana [DOM][1]ia. function foo() {} - var id = setTimeout(foo, 1000); // returns a Number > 0 + var id = setTimeout(foo, 1000); // palauttaa Numeron > 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**. +Kun `setTimeout`-funktiota kutsutaan, se palauttaa aikakatkaisun tunnisteen ja ajastaa `foo`-funktion suoritettavaksi **suunnilleen** tuhannen millisekunnin päästä. `foo` suoritetaan tarkalleen **kerran**. -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. +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. -The function that was passed as the first parameter will get called by the -*global object*, that means, that [`this`](#function.this) inside the called function -refers to that very object. +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 refers to the global object - console.log(this.value); // will log undefined + // 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ä. -> **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. +### Kutsujen pinoaminen `setInterval`-funktion avulla -### Stacking Calls with `setInterval` +`setTimeout` suoritetaan vain kerran. `setInterval` sen sijaan, kuten nimestä voi päätellä, suoritetaan **aina** `X` millisekunnin välein. Sen käyttöä ei kuitenkaan suositella. -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. +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(){ - // something that blocks for 1 second + // jotain joka blokkaa sekunnin ajaksi } setInterval(foo, 100); -In the above code `foo` will get called once and will then block for one second. +Yllä olevassa koodissa `foo`-funktiota kutsutaan, jonka jälleen se blokkaa sekunnin ajan. -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. +Tämän ajan aikana `setInterval` kasvattaa kutsupinon sisältöä. Kun `foo` on valmis, kutsupinoon on ilmestynyt jo **kymmenen** uutta kutsua suoritettavaksi. -### Dealing with Possible Blocking Code +### Mahdollisesti blokkaavan koodin kanssa pärjääminen -The easiest as well as most controllable solution, is to use `setTimeout` within -the function itself. +Helpoin ja joustavin tapa on käyttää `setTimeout`-funktiota funktiossa itsessään. function foo(){ - // something that blocks for 1 second + // jotain joka blokkaa sekunnin ajaksi 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. +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. -### Manually Clearing Timeouts +### Katkaisujen poistaminen käsin -Clearing timeouts and intervals works by passing the respective ID to -`clearTimeout` or `clearInterval`, depending which `set` function was used in -the first place. +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); -### Clearing all timeouts +### Kaikkien katkaisujen poistaminen -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. +JavaScript ei sisällä erityistä funktiota kaikkien katkaisujen ja/tai intervallien poistamiseen. Sen sijaan tämä voidaan toteuttaa raakaa voimaa käyttäen. - // clear "all" timeouts + // poista "kaikki" katkaisut 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. +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. -### Hidden use of `eval` +### Piilotettu `eval` -`setTimeout` and `setInterval` can also take a string as their first parameter. -This feature should **never** be used, since it internally makes use of `eval`. +`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. -> **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`. +> **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() { - // will get called + // kutsutaan } function bar() { function foo() { - // never gets called + // ei kutsuta ikinä } 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`. +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. -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. +Tämän lisäksi on suositeltavaa olla **käyttämättä** merkkijonoja parametrien antamiseen. function foo(a, b, c) {} - // NEVER use this + // Älä käytä tätä IKINÄ setTimeout('foo(1,2, 3)', 1000) - // Instead use an anonymous function + // Käytä nimetöntä funktiota sen sijaan setTimeout(function() { foo(a, b, c); }, 1000) -> **Note:** While it is also possible to use the syntax -> `setTimeout(foo, 1000, a, b, c)`, it is not recommended, as its use may lead -> to subtle errors when used with [methods](#function.this). +> **Huomio:** Vaikka syntaksi `setTimeout(foo, 1000, a, b, c)` onkin mahdollinen, ei sen käyttöä suositella. Tämä johtuu siitä, että sen käyttö voi johtaa virheisiin erityisesti [metodien](#function.this) kanssa. -### In Conclusion +### Yhteenveto -**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. +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. -Further, the use of `setInterval` should be avoided since its scheduler is not -blocked by executing JavaScript. +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/doc/fi/types/casting.md b/doc/fi/types/casting.md index 15d84e74..6460bf3d 100644 --- a/doc/fi/types/casting.md +++ b/doc/fi/types/casting.md @@ -1,70 +1,61 @@ -## Type Casting +## Tyyppimuunnokset -JavaScript is a *weakly typed* language, so it will apply *type coercion* -**wherever** possible. +JavaScript on tyypitetty *heikosti*. Tämä tarkoittaa sitä, että se pyrkii *pakottamaan tyyppejä* *aina* kun se on mahdollista. - // These are true - new Number(10) == 10; // Number.toString() is converted - // back to a number + // Nämä ovat totta + new Number(10) == 10; // Number.toString() muutetaan + // takaisin numeroksi - 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 == '10'; // Merkkijonot muutetaan Number-tyyppiin + 10 == '+10 '; // Lisää merkkijonohauskuutta + 10 == '010'; // Ja lisää + isNaN(null) == false; // null muuttuu nollaksi, + // joka ei ole NaN - // These are false + // Nämä ovat epätosia 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. +> **ES5 Huomio:** Nollalla alkavat numeroliteraalit tulkitaan oktaaleina (kantaluku 8). Tuki oktaaleille on **poistettu** ECMAScript 5:den tiukassa moodissa. -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. +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. -### Constructors of Built-In Types +### Natiivien tyyppien konstruktorit -The constructors of the built in types like `Number` and `String` behave -differently when being used with the `new` keyword and without it. +Natiivien tyyppien, kuten `Number` tai `String`, konstruktorit käyttäytyvät eri tavoin `new`-avainsanan kanssa ja ilman. - 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 + new Number(10) === 10; // Epätosi, Object ja Number + Number(10) === 10; // Tosi, Number ja Number + new Number(10) + 0 === 10; // Tosi, johtuu tyyppimuunnoksesta -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. +`Number`-tyypin kaltaisen natiivityypin käyttäminen luo uuden `Number`-olion. `new`-avainsanan pois jättäminen tekee `Number`-funktiosta pikemminkin muuntimen. -In addition, having literals or non-object values in there will result in even -more type coercion. +Tämän lisäksi literaalit tai ei-oliomaiset arvot johtavat edelleen uusiin tyyppimuunnoksiin. -The best option is to cast to one of the three possible types **explicitly**. +Paras tapa suorittaa tyyppimuunnoksia on tehdä niitä **selvästi**. -### Casting to a String +### Muunnos merkkijonoksi - '' + 10 === '10'; // true + '' + 10 === '10'; // tosi -By prepending a empty string a value can easily be casted to a string. +Arvo voidaan muuttaa merkkijonoksi helposti lisäämällä sen eteen tyhjä merkkijono. -### Casting to a Number +### Muunnos numeroksi - +'10' === 10; // true + +'10' === 10; // tosi -Using the **unary** plus operator it is possible to cast to a number. +**Unaarinen** plus-operaattori mahdollistaa numeroksi muuttamisen. -### Casting to a Boolean +### Muunnos totuusarvoksi -By using the **not** operator twice, a value can be converted a boolean. +Arvo voidaan muuttaa totuusarvoksi käyttämällä **not**-operaattoria kahdesti. - !!'foo'; // true - !!''; // false - !!'0'; // true - !!'1'; // true - !!'-1' // true - !!{}; // true - !!true; // true + !!'foo'; // tosi + !!''; // epätosi + !!'0'; // tosi + !!'1'; // tosi + !!'-1' // tosi + !!{}; // tosi + !!true; // tosi diff --git a/doc/fi/types/equality.md b/doc/fi/types/equality.md index a578b19c..92c70b77 100644 --- a/doc/fi/types/equality.md +++ b/doc/fi/types/equality.md @@ -1,71 +1,58 @@ -## Equality and Comparisons +## Yhtäsuuruus ja vertailut -JavaScript has two different ways of comparing the values of objects for equality. +JavaScript sisältää kaksi erilaista tapaa, joiden avulla olioiden arvoa voidaan verrata toisiinsa. -### The Equality Operator +### Yhtäsuuruusoperaattori -The equality operator consists of two equal signs: `==` +Yhtäsuuruusoperaattori koostuu kahdesta yhtäsuuruusmerkistä: `==` -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 +JavaScript tyypittyy *heikosti*. Tämä tarkoittaa sitä, että yhtäsuuruusoperaattori **muuttaa** tyyppejä verratakseen niitä keskenään. -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. + "" == "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 -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. +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. -### The Strict Equality Operator +Tämän lisäksi tyyppimuunnos vaikuttaa suorituskykyyn. Esimerkiksi merkkijono tulee muuttaa numeroksi ennen kuin sitä voidaan verrata toiseen numeroon. -The strict equality operator consists of **three** equal signs: `===` +### Tiukka yhtäsuuruusoperaattori -It works exactly like the normal equality operator, except that strict equality -operator does **not** perform type coercion between its operands. +Tiukka yhtäsuuruusoperaattori koostuu **kolmesta** yhtäsuuruusmerkistä: `===` - "" === "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 +Se toimii aivan kuten normaali yhtäsuuruusoperaattori. Se **ei** tosin tee minkäänlaista tyyppimuunnosta ennen vertailua. -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. + "" === "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 -### Comparing Objects +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ä. -While both `==` and `===` are stated as **equality** operators, they behave -different when at least one of their operands happens to be an `Object`. +### Olioiden vertailu - {} === {}; // false - new String('foo') === 'foo'; // false - new Number(10) === 10; // false +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; // true + foo === foo; // tosi -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. +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. -### In Conclusion +### Yhteenveto -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. +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/doc/fi/types/instanceof.md b/doc/fi/types/instanceof.md index 8711331a..99d44328 100644 --- a/doc/fi/types/instanceof.md +++ b/doc/fi/types/instanceof.md @@ -1,38 +1,33 @@ -## The `instanceof` Operator +## `instanceof`-operaattori -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). +`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). -### Comparing Custom Objects +### Itsetehtyjen olioiden vertailu function Foo() {} function Bar() {} Bar.prototype = new Foo(); - new Bar() instanceof Bar; // true - new Bar() instanceof Foo; // true + new Bar() instanceof Bar; // tosi + new Bar() instanceof Foo; // tosi - // This just sets Bar.prototype to the function object Foo - // But not to an actual instance of Foo + // Tämä asettaa vain Bar.prototype-ominaisuudeksi + // funktio-olion Foo + // Se ei kuitenkaan ole Foon todellinen instanssi Bar.prototype = Foo; - new Bar() instanceof Foo; // false + new Bar() instanceof Foo; // epätosi -### Using `instanceof` with Native Types +### `instanceof` ja natiivit tyypit - new String('foo') instanceof String; // true - new String('foo') instanceof Object; // true + new String('foo') instanceof String; // tosi + new String('foo') instanceof Object; // tosi - 'foo' instanceof String; // false - 'foo' instanceof Object; // false + 'foo' instanceof String; // epätosi + 'foo' instanceof Object; // epätosi -One important thing to note here is, that `instanceof` does not work on objects -that origin from different JavaScript contexts (e.g. different documents -in a web browser), since their constructors will not be the exact same object. +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. -### In Conclusion +### Yhteenveto -The `instanceof` operator should **only** be used when dealing with custom made -objects that origin from the same JavaScript context. Just like the -[`typeof`](#types.typeof) operator, every other use of it should be **avoided**. +`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/doc/fi/types/typeof.md b/doc/fi/types/typeof.md index e4b28d7f..4ea28ff8 100644 --- a/doc/fi/types/typeof.md +++ b/doc/fi/types/typeof.md @@ -1,21 +1,14 @@ -## The `typeof` Operator +## `typeof`-operaattori -The `typeof` operator (together with -[`instanceof`](#types.instanceof)) is probably the biggest -design flaw of JavaScript, as it is near of being **completely broken**. +`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. -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. +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. -> **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. +> **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. -### The JavaScript Type Table +### JavaScriptin tyyppitaulukko - Value Class Type + Arvo Luokka Tyyppi ------------------------------------- "foo" String string new String("foo") String object @@ -28,60 +21,44 @@ 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) + /abc/g RegExp object (Nitro/V8-funktio) + new RegExp("meow") RegExp object (Nitro/V8-funktio) {} 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. +Yllä olevassa taulukossa *Tyyppi* viittaa arvoon, jonka `typeof`-operaattori palauttaa. Kuten voidaan havaita, tämä arvo voi olla varsin ristiriitainen. -The *Class* refers to the value of the internal `[[Class]]` property of an object. +*Luokka* viittaa olion sisäisen `[[Luokka]]`-ominaisuuden arvoon. -> **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`. +> **Määritelmää lainaten:** `[[Luokka]]`-arvon tulee olla jokin seuraavista merkkijonoista. `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`. +Jotta kyseiseen arvoon päästään käsiksi, tulee soveltaa `Object.prototype`-ominaisuuden `toString`-metodia. -### The Class of an Object +### Olion luokka -The specification gives exactly one way of accessing the `[[Class]]` value, -with the use of `Object.prototype.toString`. +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'); // true - is('String', new String('test')); // true + is('String', 'test'); // tosi + is('String', new String('test')); // tosi -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. +Yllä olevassa esimerkissä `Object.prototype.toString`-metodia kutsutaan arvolla [this](#function.this), jonka arvo on asetettu olion `[[Luokka]]` arvoon. -> **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. +> **ES5 Huomio:** Käytännöllisyyden vuoksi `Object.prototype.toString` palautusarvo **muutettiin** `Object`-arvosta `Null`- ja `Undefined`-arvoiksi ECMAScript 5:ssä. -### Testing for Undefined Variables +### Määrittelemättömien muuttujien testaaminen 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. +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ää. -### In Conclusion +### Yhteenveto -In order to check the type of an object, it is highly recommended to use -`Object.prototype.toString`; as this is the only reliable way of doing so. -As shown in the above type table, some return values of `typeof` are not defined -in the specification; thus, they can differ across various implementations. - -Unless checking whether a variable is defined, `typeof` should be avoided at -**all costs**. +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ä**. From 4914f06b92f2fd15acc72e319f62062c5ba31652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?juho=20Veps=C3=A4l=C3=A4inen?= Date: Sun, 27 Mar 2011 14:57:09 +0300 Subject: [PATCH 126/641] Minor tweaks here and there. --- doc/fi/intro/index.md | 2 +- doc/fi/object/general.md | 4 ++-- doc/fi/object/hasownproperty.md | 10 +++++----- doc/fi/object/prototype.md | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/fi/intro/index.md b/doc/fi/intro/index.md index f79ac337..e9100fbf 100644 --- a/doc/fi/intro/index.md +++ b/doc/fi/intro/index.md @@ -2,7 +2,7 @@ **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]. +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/doc/fi/object/general.md b/doc/fi/object/general.md index c1099897..c169117f 100644 --- a/doc/fi/object/general.md +++ b/doc/fi/object/general.md @@ -1,8 +1,8 @@ ## Olioiden käyttö ja ominaisuudet -Kaikki muuttujat, kahta poikkeusta lukuunottamatta, käyttäytyvät JavaScriptissä kuten olio. Nämä poikkeukset ovat [`null`](#core.undefined) sekä [`undefined`](#core.undefined). +Kaikki muuttujat, kahta poikkeusta lukuunottamatta, käyttäytyvät JavaScriptissä oliomaisesti. Nämä poikkeukset ovat [`null`](#core.undefined) sekä [`undefined`](#core.undefined). - false.toString() // 'false' + false.toString() // epätosi [1, 2, 3].toString(); // '1,2,3' function Foo(){} diff --git a/doc/fi/object/hasownproperty.md b/doc/fi/object/hasownproperty.md index 6da1a5b4..9371dcad 100644 --- a/doc/fi/object/hasownproperty.md +++ b/doc/fi/object/hasownproperty.md @@ -11,10 +11,10 @@ Jotta voimme tarkistaa onko olion ominaisuus määritelty siinä *itsessään*, var foo = {goo: undefined}; foo.bar; // 1 - 'bar' in foo; // true + 'bar' in foo; // tosi - foo.hasOwnProperty('bar'); // false - foo.hasOwnProperty('goo'); // true + 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. @@ -29,11 +29,11 @@ JavaScript **ei** suojele `hasOwnProperty`-metodin nimeä. Täten on mahdollista bar: 'Olkoon vaikka lohikäärmeitä' }; - foo.hasOwnProperty('bar'); // palauttaa aina false + foo.hasOwnProperty('bar'); // palauttaa aina epätoden // Käytä toisen olion hasOwnProperty-metodia ja kutsu sitä asettamalla // 'this' foohon - ({}).hasOwnProperty.call(foo, 'bar'); // true + ({}).hasOwnProperty.call(foo, 'bar'); // tosi ### Yhteenveto diff --git a/doc/fi/object/prototype.md b/doc/fi/object/prototype.md index 92903d7e..4ecdae89 100644 --- a/doc/fi/object/prototype.md +++ b/doc/fi/object/prototype.md @@ -35,7 +35,7 @@ Ensimmäinen suuri ero liittyy siihen, kuinka perintä toimii. JavaScriptissä s Foo.prototype { method: ... } Object.prototype - { toString: ... /* etc. */ } + { 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. From 42852555b99f44a799e7f9d5c561e67f9d7987b8 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Mon, 28 Mar 2011 18:31:42 +0800 Subject: [PATCH 127/641] Cleaned up code slightly with $.extend --- site/javascript/garden.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/site/javascript/garden.js b/site/javascript/garden.js index 69918dc4..3795bcc4 100644 --- a/site/javascript/garden.js +++ b/site/javascript/garden.js @@ -22,7 +22,7 @@ Sections.prototype = { return { id: this.id.replace('.intro', ''), offset: $(this).offset().top - 20, - title: $(this).find(':header:first').text() + title: $(this).find(':header:first').html() }; }).get(); @@ -121,7 +121,7 @@ Sections.prototype = { setLink: function(ele, data) { ele.slideDown(100).attr('href', '#' + data.id) - .find('.nav_section_name').text(data.title); + .find('.nav_section_name').html(data.title); } }; @@ -129,11 +129,14 @@ Sections.prototype = { // This more or less controls the page ------------------------------------------ // ------------------------------------------------------------------------------ function Page() { - this.window = $(window); - this.nav = $('nav > ul > li'); + $.extend(true, this, { + window: $(window), + nav: $('nav > ul > li'), + section: null, + articule: null + }); + this.sections = new Sections(this); - this.section = null; - this.article = null; this.init(); } @@ -142,12 +145,15 @@ Page.prototype = { var that = this, mainNav = $('#nav_main'); - this.scrollLast = 0; + $.extend(this, { + scrollLast: 0, + resizeTimeout: null + }); + this.window.scroll(function() { that.onScroll(); }); - - this.resizeTimeout = null; + this.window.resize(function() { that.onResize(); }); From afd901ce50c796756d8d25a237451d837750eda4 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Mon, 28 Mar 2011 18:32:14 +0800 Subject: [PATCH 128/641] Merged all the intro stuff; no more intro subsections --- doc/en/index.json | 7 +------ doc/en/intro/authors.md | 9 --------- doc/en/intro/contributors.md | 8 -------- doc/en/intro/hosting.md | 8 -------- doc/en/intro/index.md | 34 +++++++++++++++++++++++++++++++++- doc/en/intro/license.md | 12 ------------ 6 files changed, 34 insertions(+), 44 deletions(-) delete mode 100644 doc/en/intro/authors.md delete mode 100644 doc/en/intro/contributors.md delete mode 100644 doc/en/intro/hosting.md delete mode 100644 doc/en/intro/license.md diff --git a/doc/en/index.json b/doc/en/index.json index d4e52883..d4012ed9 100644 --- a/doc/en/index.json +++ b/doc/en/index.json @@ -6,12 +6,7 @@ { "title": "Intro", "dir": "intro", - "articles": [ - "authors", - "contributors", - "hosting", - "license" - ] + "articles": [] }, { "title": "Objects", diff --git a/doc/en/intro/authors.md b/doc/en/intro/authors.md deleted file mode 100644 index 684ca097..00000000 --- a/doc/en/intro/authors.md +++ /dev/null @@ -1,9 +0,0 @@ -## The Authors - -This guide is the work of two lovely [Stack Overflow][3] 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 -[3]: http://stackoverflow.com/ - diff --git a/doc/en/intro/contributors.md b/doc/en/intro/contributors.md deleted file mode 100644 index 73fb98ea..00000000 --- a/doc/en/intro/contributors.md +++ /dev/null @@ -1,8 +0,0 @@ -## 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/doc/en/intro/hosting.md b/doc/en/intro/hosting.md deleted file mode 100644 index 3fe5ae28..00000000 --- a/doc/en/intro/hosting.md +++ /dev/null @@ -1,8 +0,0 @@ -## Hosting - -JavaScript Garden is hosted on GitHub, but [Cramer Development][1] supports us -with a mirror at [JavaScriptGarden.info][2]. - -[1]: http://cramerdev.com/ -[2]: http://javascriptgarden.info/ - diff --git a/doc/en/intro/index.md b/doc/en/intro/index.md index f2d5d31b..6ddd7c71 100644 --- a/doc/en/intro/index.md +++ b/doc/en/intro/index.md @@ -11,5 +11,37 @@ of the language is strongly recommended in order to understand the topics covere 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. -[1]: https://developer.mozilla.org/en/JavaScript/Guide +## The Authors + +This guide is the work of two lovely [Stack Overflow][2] users, [Ivo Wetzel][3] +(Writing) and [Zhang Yi Jiang][4] (Design). + +## Contributors + + - [Caio Romão][5] (Spelling corrections) + - [Andreas Blixt][6] (Language corrections) + +## Hosting +JavaScript Garden is hosted on GitHub, but [Cramer Development][7] supports us +with a mirror at [JavaScriptGarden.info][8]. + +## License + +JavaScript Garden is published under the [MIT license][9] and hosted on +[GitHub][10]. If you find errors or typos please [file an issue][11] or a pull +request on the repository. You can also find us in the [JavaScript room][12] on +Stack Overflow chat. + +[1]: https://developer.mozilla.org/en/JavaScript/Guide +[2]: http://stackoverflow.com/ +[3]: http://stackoverflow.com/users/170224/ivo-wetzel +[4]: http://stackoverflow.com/users/313758/yi-jiang +[5]: https://github.com/caio +[6]: https://github.com/blixt +[7]: http://cramerdev.com/ +[8]: http://javascriptgarden.info/ +[9]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE +[10]: https://github.com/BonsaiDen/JavaScript-Garden +[11]: https://github.com/BonsaiDen/JavaScript-Garden/issues +[12]: http://chat.stackoverflow.com/rooms/17/javascript diff --git a/doc/en/intro/license.md b/doc/en/intro/license.md deleted file mode 100644 index cd398699..00000000 --- a/doc/en/intro/license.md +++ /dev/null @@ -1,12 +0,0 @@ -## 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 - From 66e621bca5a5fd0b7b58cb49be863ccc8ae73120 Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Mon, 28 Mar 2011 23:27:12 +0800 Subject: [PATCH 129/641] Merge branches 'fn-translation' and 'master' From 84ad5a9c252087272cd30ccd12b2025771afbf9e Mon Sep 17 00:00:00 2001 From: ZhangYiJiang Date: Wed, 30 Mar 2011 01:16:48 +0800 Subject: [PATCH 130/641] Cleaned up working directory --- .gitignore | 2 + site/en/index.html | 1859 -------------------------------------------- 2 files changed, 2 insertions(+), 1859 deletions(-) delete mode 100644 site/en/index.html diff --git a/.gitignore b/.gitignore index ff75dc4a..e1867192 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,7 @@ /site/de /site/ru /site/zh +/site/en +/site/fi *.md~ *.src.md diff --git a/site/en/index.html b/site/en/index.html deleted file mode 100644 index 8a91e498..00000000 --- a/site/en/index.html +++ /dev/null @@ -1,1859 +0,0 @@ -JavaScript Garden -

    Intro

    JavaScript Garden is a growing collection of documentation about the most -quirky parts of the JavaScript programming language. It gives advice to -avoid common mistakes, subtle bugs, as well as performance issues and bad -practices that non-expert JavaScript programmers may encounter on their -endeavours into the depths of the language.

    - -

    JavaScript Garden does not aim to teach you JavaScript. Former knowledge -of the language is strongly recommended in order to understand the topics covered -in this guide. In order to learn the basics of the language, please head over to -the excellent guide 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).

    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 Foo(){}
    -Foo.bar = 1;
    -Foo.bar; // 1
    -
    - -

    A common misconception is that number literals cannot be used as -objects. That is because a flaw in JavaScript's parser tries to parse the dot -notation on a number as a floating point literal.

    - -
    2.toString(); // raises SyntaxError
    -
    - -

    There are a couple of workarounds which can be used in order make number -literals act as objects too.

    - -
    2..toString(); // the second point is correctly recognized
    -2 .toString(); // note the space left to the dot
    -(2).toString(); // 2 is evaluated first
    -
    - -

    Objects as a data type

    - -

    Objects in JavaScript can also be used as a Hashmap, they mainly consist -of named properties mapping to values.

    - -

    Using a object literal - {} notation - it is possible to create a -plain object. This new object inherits from Object.prototype and -has no own properties defined on it.

    - -
    var foo = {}; // a new empty object
    -
    -// a new object with a property called 'test' with value 12
    -var bar = {test: 12}; 
    -
    - -

    Accessing properties

    - -

    The properties of an object can be accessed in two ways, via either the dot -notation, or the square bracket notation.

    - -
    var foo = {name: 'Kitten'}
    -foo.name; // kitten
    -foo['name']; // kitten
    -
    -var get = 'name';
    -foo[get]; // kitten
    -
    -foo.1234; // SyntaxError
    -foo['1234']; // works
    -
    - -

    Both notations are identical in their workings, with the only difference being that -the square bracket notation allows for dynamic setting of properties, as well as -the use of property names that would otherwise lead to a syntax error.

    - -

    Deleting properties

    - -

    The only way to actually remove a property from an object is to use the delete -operator; setting the property to undefined or null only remove the -value associated with the property, but not the key.

    - -
    var obj = {
    -    bar: 1,
    -    foo: 2,
    -    baz: 3
    -};
    -obj.bar = undefined;
    -obj.foo = null;
    -delete obj.baz;
    -
    -for(var i in obj) {
    -    if (obj.hasOwnProperty(i)) {
    -        console.log(i, '' + obj[i]);
    -    }
    -}
    -
    - -

    The above outputs both bar undefined and foo null - only baz was -removed and is therefore missing from the output.

    - -

    Notation of keys

    - -
    var test = {
    -    'case': 'I am a keyword so I must be notated as a string',
    -    delete: 'I am a keyword too so me' // raises SyntaxError
    -};
    -
    - -

    Object properties can be both notated as plain characters and as strings. Due to -another mis-design in JavaScript's parser, the above will throw -a SyntaxError prior to ECMAScript 5.

    - -

    This error arises from the fact that delete is a keyword; therefore, it must be -notated as a string literal to ensure that it will be correctly interpreted by -older JavaScript engines.

    The Prototype

    JavaScript does not feature a classical inheritance model, instead it uses a -prototypal one.

    - -

    While this is often considered to be one of JavaScript's weaknesses, the -prototypal inheritance model is in fact more powerful than the classic model. -It is for example fairly trivial to build a classic model on top of it, while the -other way around is a far more difficult task.

    - -

    Due to the fact that JavaScript is basically the only widely used language that -features prototypal inheritance, it takes some time to adjust to the -differences between the two models.

    - -

    The first major difference is that inheritance in JavaScript is done by using so -called prototype chains.

    - - - -
    function Foo() {
    -    this.value = 42;
    -}
    -Foo.prototype = {
    -    method: function() {}
    -};
    -
    -function Bar() {}
    -
    -// Set Bar's prototype to a new instance of Foo
    -Bar.prototype = new Foo();
    -Bar.prototype.foo = 'Hello World';
    -
    -// Make sure to list Bar as the actual constructor
    -Bar.prototype.constructor = Bar;
    -
    -var test = new Bar() // create a new bar instance
    -
    -// The resulting prototype chain
    -test [instance of Bar]
    -    Bar.prototype [instance of Foo] 
    -        { foo: 'Hello World' }
    -        Foo.prototype
    -            { method: ... }
    -            Object.prototype
    -                { toString: ... /* etc. */ }
    -
    - -

    In the above, the object test will inherit from both Bar.prototype and -Foo.prototype; hence, it will have access to the function method that was -defined on Foo. It will also have access to the property value of the -one Foo instance that is its prototype. It is important to note that new -Bar() does not create a new Foo instance, but reuses the one assigned to -its prototype; thus, all Bar instances will share the same value property.

    - - - -

    Property lookup

    - -

    When accessing the properties of an object, JavaScript will traverse the -prototype chain upwards until it finds a property with the requested name.

    - -

    When it reaches the top of the chain - namely Object.prototype - and still -hasn't found the specified property, it will return the value -undefined instead.

    - -

    The prototype property

    - -

    While the prototype property is used by the language to build the prototype -chains, it is still possible to assign any given value to it. Although -primitives will simply get ignored when assigned as a prototype.

    - -
    function Foo() {}
    -Foo.prototype = 1; // no effect
    -
    - -

    Assigning objects, as shown in the example above, will work, and allows for dynamic -creation of prototype chains.

    - -

    Performance

    - -

    The lookup time for properties that are high up on the prototype chain can have a -negative impact on performance critical sections of code. Additionally, trying to -access non-existent properties will always traverse the full prototype chain.

    - -

    Also, when iterating over the properties of an object -every property that is on the prototype chain will get enumerated.

    - -

    Extension of native prototypes

    - -

    One mis-feature that is often used is to extend Object.prototype or one of the -other built in prototypes.

    - -

    This technique is called monkey patching and breaks encapsulation. While -used by widely spread frameworks such as Prototype, there is still no good -reason for cluttering built-in types with additional non-standard functionality.

    - -

    The only good reason for extending a built-in prototype is to backport -the features of newer JavaScript engines; for example, -Array.forEach.

    - -

    In conclusion

    - -

    It is a must to understand the prototypal inheritance model completely -before writing complex code which makes use of it. Also, watch the length of -the prototype chains and break them up if necessary to avoid possible -performance issues. Further, the native prototypes should never be extended -unless it is for the sake of compatibility with newer JavaScript features.

    hasOwnProperty

    In order to check whether a object has a property defined on itself and not -somewhere on its prototype chain, it is necessary to use the -hasOwnProperty method which all objects inherit from Object.prototype.

    - - - -

    hasOwnProperty is the only thing in JavaScript which deals with properties and -does not traverse the prototype chain.

    - -
    // Poisoning Object.prototype
    -Object.prototype.bar = 1; 
    -var foo = {goo: undefined};
    -
    -foo.bar; // 1
    -'bar' in foo; // true
    -
    -foo.hasOwnProperty('bar'); // false
    -foo.hasOwnProperty('goo'); // true
    -
    - -

    Only hasOwnProperty will give the correct and expected result, this is -essential when iterating over the properties of any object. There is no other -way to exclude properties that are not defined on the object itself, but -somewhere on its prototype chain.

    - -

    hasOwnProperty as a property

    - -

    JavaScript does not protect the property name hasOwnProperty; thus, if the -possibility exists that an object might have a property with this name, it is -necessary to use an external hasOwnProperty in order to get correct results.

    - -
    var foo = {
    -    hasOwnProperty: function() {
    -        return false;
    -    },
    -    bar: 'Here be dragons'
    -};
    -
    -foo.hasOwnProperty('bar'); // always returns false
    -
    -// Use another hasOwnProperty and call it with 'this' set to foo
    -{}.hasOwnProperty.call(foo, 'bar'); // true
    -
    - -

    In conclusion

    - -

    When checking for the existence of a property on a object, hasOwnProperty is -the only method of doing so. It is also recommended to make hasOwnProperty -part of every for in loop, this will avoid errors from -extended native prototypes.

    The for in Loop

    Just like the in operator, the for in loop also traverses the prototype -chain when iterating over the properties of an object.

    - - - -
    // Poisoning Object.prototype
    -Object.prototype.bar = 1;
    -
    -var foo = {moo: 2};
    -for(var i in foo) {
    -    console.log(i); // prints both bar and moo
    -}
    -
    - -

    Since it is not possible to change the behavior of the for in loop itself, it -is necessary to filter out the unwanted properties inside the loop body , -this is done by using the hasOwnProperty method of -Object.prototype.

    - - - -

    Using hasOwnProperty for filtering

    - -
    // still the foo from above
    -for(var i in foo) {
    -    if (foo.hasOwnProperty(i)) {
    -        console.log(i);
    -    }
    -}
    -
    - -

    This version is the only correct one to use. Due to the use of hasOwnProperty it -will only print out moo. When hasOwnProperty is left out, the code is -prone to errors in cases where the native prototypes - e.g. Object.prototype - -have been extended.

    - -

    One widely used framework which does this is Prototype. When this -framework is included, for in loops that do not use hasOwnProperty are -guaranteed to break.

    - -

    Best practices

    - -

    It is recommended to always use hasOwnProperty. Never should any -assumptions be made about the environment the code is running in, or whether the -native prototypes have been extended or not.

    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 asynchronous function.

    - -

    The function declaration

    - -
    function foo() {}
    -
    - -

    The above function gets hoisted before the execution of the -program starts; thus, it is available everywhere in the scope it was defined -in, even if called before the actual definition in the source.

    - -
    foo(); // Works because foo was created before this code runs
    -function foo() {}
    -
    - -

    The function expression

    - -
    var foo = function() {};
    -
    - -

    This example assigns the unnamed and anonymous function to the variable foo.

    - -
    foo; // 'undefined'
    -foo(); // this raises a TypeError
    -var foo = function() {};
    -
    - -

    Due to the fact that var is a declaration, that hoists the variable name foo -before the actual execution of the code starts, foo is already defined when -the script gets executed.

    - -

    But since assignments only happen at runtime, the value of foo will default -to undefined before the corresponding code is executed.

    - -

    Named function expression

    - -

    Another special case is the assignment of named functions.

    - -
    var foo = function bar() {
    -    bar(); // Works
    -}
    -bar(); // ReferenceError
    -
    - -

    Here bar is not available in the outer scope, since the function only gets -assigned to foo; however, inside of bar it is available. This is due to -how name resolution in JavaScript works, the name of the -function is always made available in the local scope of the function itself.

    How this Works

    JavaScript has a different concept of what the special name this refers to -than most other programming languages do. There are exactly five different -ways in which the value of this can be bound in the language.

    - -

    The global scope

    - -
    this;
    -
    - -

    When using this in global scope, it will simply refer to the global object.

    - -

    Calling a function

    - -
    foo();
    -
    - -

    Here this will again refer to the global object.

    - - - -

    Calling a method

    - -
    test.foo(); 
    -
    - -

    In this example this will refer to test.

    - -

    Calling a constructor

    - -
    new foo(); 
    -
    - -

    A function call that is preceded by the new keyword acts as -a constructor. Inside the function this will refer -to a newly created Object.

    - -

    Explicit setting of this

    - -
    function foo(a, b, c) {}
    -
    -var bar = {};
    -foo.apply(bar, [1, 2, 3]); // array will expand to the below
    -foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3
    -
    - -

    When using the call or apply methods of Function.prototype, the value of -this inside the called function gets explicitly set to the first argument -of the corresponding function call.

    - -

    As a result, the above example the method case does not apply, and this -inside of foo will be set to bar.

    - - - -

    Common pitfalls

    - -

    While most of these cases make sense, the first one is to be considered another -mis-design of the language, as it never has any practical use.

    - -
    Foo.method = function() {
    -    function test() {
    -        // this is set to the global object
    -    }
    -    test();
    -}
    -
    - -

    A common misconception is that this inside of test refers to Foo, while in -fact it does not.

    - -

    In order to gain access to Foo from within test it is necessary to create a -local variable inside of method which refers to Foo.

    - -
    Foo.method = function() {
    -    var that = this;
    -    function test() {
    -        // Use that instead of this here
    -    }
    -    test();
    -}
    -
    - -

    that is just a normal name, but it is commonly used for the reference to an -outer this. In combination with closures, it can also -be used to pass this values around.

    - -

    Assigning methods

    - -

    Another thing that does not work in JavaScript is function aliasing, that is, -assigning a method to a variable.

    - -
    var test = someObject.methodTest;
    -test();
    -
    - -

    Due to the first case test now acts like like a plain function call; therefore, -this inside it will no longer refer to someObject.

    - -

    While the late binding of this might seem like a bad idea at first, it is in -fact what makes prototypal inheritance work.

    - -
    function Foo() {}
    -Foo.prototype.method = function() {};
    -
    -function Bar() {}
    -Bar.prototype = Foo.prototype;
    -
    -new Bar().method();
    -
    - -

    When method gets called on a instance of Bar, this will now refer to that -very instance.

    Closures and References

    One of JavaScript's most powerful features is the availability of closures, -this means that scopes always keep access to the outer scope they were -defined in. Since the only scoping that JavaScript has is -function scope, all functions, by default, act as closures.

    - -

    Emulating private variables

    - -
    function Counter(start) {
    -    var count = start;
    -    return {
    -        increment: function() {
    -            count++;
    -        },
    -
    -        get: function() {
    -            return count;
    -        }
    -    }
    -}
    -
    -var foo = Counter(4);
    -foo.increment();
    -foo.get(); // 5
    -
    - -

    Here, Counter returns two closures. The function increment as well as -the function get. Both of these functions keep a reference to the scope of -Counter and, therefore, always keep access to the count variable that was -defined in that very scope.

    - -

    Why private variables work

    - -

    Since it is not possible to reference or assign scopes in JavaScript, there is -no way of accessing the variable count from the outside. The only way to -interact with it is via the two closures.

    - -
    var foo = new Counter(4);
    -foo.hack = function() {
    -    count = 1337;
    -};
    -
    - -

    The above code will not change the variable count in the scope of Counter, -since foo.hack was not defined in that scope. It will instead create - or -override - the global variable count.

    - -

    Closures inside loops

    - -

    One often made mistake is to use closures inside of loops, as if they were -copying the value of the loops index variable.

    - -
    for(var i = 0; i < 10; i++) {
    -    setTimeout(function() {
    -        console.log(i);  
    -    }, 1000);
    -}
    -
    - -

    The above will not output the numbers 0 through 9, but will simply print -the number 10 ten times.

    - -

    The anonymous function keeps a reference to i and at the time -console.log gets called, the for loop has already finished and the value of -i as been set to 10.

    - -

    In order to get the desired behavior, it is necessary to create a copy of -the value of i.

    - -

    Avoiding the reference problem

    - -

    In order to copy the value of the loop's index variable, it is best to use an -anonymous wrapper.

    - -
    for(var i = 0; i < 10; i++) {
    -    (function(e) {
    -        setTimeout(function() {
    -            console.log(e);  
    -        }, 1000);
    -    })(i);
    -}
    -
    - -

    The anonymous outer function gets called immediately with i as its first -argument and will receive a copy of the value of i as its parameter e.

    - -

    The anonymous function that gets passed to setTimeout now has a reference to -e, whose value does not get changed by the loop.

    - -

    There is another possible way of achieving this; that is to return a function -from the anonymous wrapper, that will then have the same behavior as the code -above.

    - -
    for(var i = 0; i < 10; i++) {
    -    setTimeout((function(e) {
    -        return function() {
    -            console.log(e);
    -        }
    -    })(i), 1000)
    -}
    -

    The arguments Object

    Every function scope in JavaScript can access the special variable arguments. -This variable holds a list of all the arguments that were passed to the function.

    - - - -

    The arguments object is not an Array. While it has some of the -semantics of an array - namely the length property - it does not inherit from -Array.prototype and is in fact an Object.

    - -

    Due to this, it is not possible to use standard array methods like push, -pop or slice on arguments. While iteration with a plain for loop works -just fine, it is necessary to convert it to a real Array in order to use the -standard Array methods on it.

    - -

    Converting to an array

    - -

    The code below will return a new Array containing all the elements of the -arguments object.

    - -
    Array.prototype.slice.call(arguments);
    -
    - -

    This conversion is slow, it is not recommended to use it in performance -critical sections of code.

    - -

    Passing arguments

    - -

    The following is the recommended way of passing arguments from one function to -another.

    - -
    function foo() {
    -    bar.apply(null, arguments);
    -}
    -function bar(a, b, c) {
    -    // do stuff here
    -}
    -
    - -

    Another trick is to use both call and apply together to create fast, unbound -wrappers.

    - -
    function Foo() {}
    -
    -Foo.prototype.method = function(a, b, c) {
    -    console.log(this, a, b, c);
    -};
    -
    -// Create an unbound version of "method" 
    -// It takes the parameters: this, arg1, arg2...argN
    -Foo.method = function() {
    -
    -    // Result: Foo.prototype.method.call(this, arg1, arg2... argN)
    -    Function.call.apply(Foo.prototype.method, arguments);
    -};
    -
    - -

    Formal parameters and arguments indexes

    - -

    The arguments object creates getter and setter functions for both its -properties as well as the function's formal parameters.

    - -

    As a result, changing the value of a formal parameter will also change the value -of the corresponding property on the arguments object, and the other way around.

    - -
    function foo(a, b, c) {
    -    arguments[0] = 2;
    -    a; // 2                                                           
    -
    -    b = 4;
    -    arguments[1]; // 4
    -
    -    var d = c;
    -    d = 9;
    -    c; // 3
    -}
    -foo(1, 2, 3);
    -
    - -

    Performance myths and truths

    - -

    The arguments object is always created with the only two exceptions being the -cases where it is declared as a name inside of a function or one of its formal -parameters. It does not matter whether it is used or not.

    - -

    Both getters and setters are always created; thus, using it has nearly -no performance impact at all, especially not in real world code where there is -more than a simple access to the arguments object's properties.

    - - - -

    However, there is one case which will drastically reduce the performance in -modern JavaScript engines. That case is the use of arguments.callee.

    - -
    function foo() {
    -    arguments.callee; // do something with this function object
    -    arguments.callee.caller; // and the calling function object
    -}
    -
    -function bigLoop() {
    -    for(var i = 0; i < 100000; i++) {
    -        foo(); // Would normally be inlined...
    -    }
    -}
    -
    - -

    In the above code, foo can no longer be a subject to inlining since it -needs to know about both itself and its caller. This not only defeats possible -performance gains that would arise from inlining, it also breaks encapsulation -since the function may now be dependent on a specific calling context.

    - -

    It is highly recommended to never make use of arguments.callee or any of -its properties.

    - -

    Constructors

    Constructors in JavaScript are yet again different from many other languages. Any -function call that is preceded by the new keyword acts as a constructor.

    - -

    Inside the constructor - the called function - the value of this refers to a -newly created Object. The prototype of this new -object is set to the prototype of the function object that was invoked as the -constructor.

    - -

    If the function that was called has no explicit return statement, then it -implicitly returns the value of this - the new object.

    - -
    function Foo() {
    -    this.bla = 1;
    -}
    -
    -Foo.prototype.test = function() {
    -    console.log(this.bla);
    -};
    -
    -var test = new Foo();
    -
    - -

    The above calls Foo as constructor and sets the prototype of the newly -created object to Foo.prototype.

    - -

    In case of an explicit return statement the function returns the value -specified that statement, but only if the return value is an Object.

    - -
    function Bar() {
    -    return 2;
    -}
    -new Bar(); // a new object
    -
    -function Test() {
    -    this.value = 2;
    -
    -    return {
    -        foo: 1
    -    };
    -}
    -new Test(); // the returned object
    -
    - -

    When the new keyword is omitted, the function will not return a new object.

    - -
    function Foo() {
    -    this.bla = 1; // gets set on the global object
    -}
    -Foo(); // undefined
    -
    - -

    While the above example might still appear to work in some cases, due to the -workings of this in JavaScript, it will use the -global object as the value of this.

    - -

    Factories

    - -

    In order to be able to omit the new keyword, the constructor function has to -explicitly return a value.

    - -
    function Bar() {
    -    var value = 1;
    -    return {
    -        method: function() {
    -            return value;
    -        }
    -    }
    -}
    -Bar.prototype = {
    -    foo: function() {}
    -};
    -
    -new Bar();
    -Bar();
    -
    - -

    Both calls to Bar return the exact same thing, a newly create object which -has a property called method on it, that is a -Closure.

    - -

    It is also to note that the call new Bar() does not affect the prototype -of the returned object. While the prototype will be set on the newly created -object, Bar never returns that new object.

    - -

    In the above example, there is no functional difference between using and -not using the new keyword.

    - -

    Creating new objects via factories

    - -

    An often made recommendation is to not use new since forgetting its use -may lead to bugs.

    - -

    In order to create new object, one should rather use a factory and construct a -new object inside of that factory.

    - -
    function Foo() {
    -    var obj = {};
    -    obj.value = 'blub';
    -
    -    var private = 2;
    -    obj.someMethod = function(value) {
    -        this.value = value;
    -    }
    -
    -    obj.getPrivate = function() {
    -        return private;
    -    }
    -    return obj;
    -}
    -
    - -

    While the above is robust against a missing new keyword and certainly makes -the use of private variables easier, it comes with some -downsides.

    - -
      -
    1. It uses more memory since the created objects do not share the methods -on a prototype.
    2. -
    3. In order to inherit the factory needs to copy all the methods from another -object or put that object on the prototype of the new object.
    4. -
    5. Dropping the prototype chain just because of a left out new keyword -somehow goes against the spirit of the language.
    6. -
    - -

    In conclusion

    - -

    While omitting the new keyword might lead to bugs, it is certainly not a -reason to drop the use of prototypes altogether. In the end it comes down to -which solution is better suited for the needs of the application, it is -especially important to choose a specific style of object creation and stick -with it.

    Scopes and Namespaces

    Although JavaScript deals fine with the syntax of two matching curly -braces for blocks, it does not support block scope; hence, all that is left -is in the language is function scope.

    - -
    function test() { // a scope
    -    for(var i = 0; i < 10; i++) { // not a scope
    -        // count
    -    }
    -    console.log(i); // 10
    -}
    -
    - - - -

    There are also no distinct namespaces in JavaScript, that means that everything -gets defined in one globally shared namespace.

    - -

    Each time a variable is referenced, JavaScript will traverse upwards through all -the scopes until it finds it. In the case that it reaches the global scope and -still has not found the requested name, it will raise a ReferenceError.

    - -

    The bane of global variables

    - -
    // script A
    -foo = '42';
    -
    -// script B
    -var foo = '42'
    -
    - -

    The above two scripts do not have the same effect. Script A defines a -variable called foo in the global scope and script B defines a foo in the -current scope.

    - -

    Again, that is not at all the same effect, not using var can have major -implications.

    - -
    // global scope
    -var foo = 42;
    -function test() {
    -    // local scope
    -    foo = 21;
    -}
    -test();
    -foo; // 21
    -
    - -

    Leaving out the var statement inside the function test will override the -value of foo. While this might not seem like a big deal at first, having -thousands of lines of JavaScript and not using var will introduce horrible and -hard to track down bugs.

    - -
    // global scope
    -var items = [/* some list */];
    -for(var i = 0; i < 10; i++) {
    -    subLoop();
    -}
    -
    -function subLoop() {
    -    // scope of subLoop
    -    for(i = 0; i < 10; i++) { // missing var statement
    -        // do amazing stuff!
    -    }
    -}
    -
    - -

    The outer loop will terminate after the first call to subLoop, since subLoop -overwrites the global value of i. Using a var for the second for loop would -have easily avoided this error. The var statement should never be left out -unless the desired effect is to affect the outer scope.

    - -

    Local variables

    - -

    The only source for local variables in JavaScript are -function parameters and variables that were declared via the -var statement.

    - -
    // global scope
    -var foo = 1;
    -var bar = 2;
    -var i = 2;
    -
    -function test(i) {
    -    // local scope of the function test
    -    i = 5;
    -
    -    var foo = 3;
    -    bar = 4;
    -}
    -test(10);
    -
    - -

    While foo and i are local variables inside the scope of the function test, -the assignment of bar will override the global variable with the same name.

    - -

    Hoisting

    - -

    JavaScript hoists declarations. This means that both var statements and -function declarations will be moved to the top of their enclosing scope.

    - -
    bar();
    -var bar = function() {};
    -var someValue = 42;
    -
    -test();
    -function test(data) {
    -    if (false) {
    -        goo = 1;
    -
    -    } else {
    -        var goo = 2;
    -    }
    -    for(var i = 0; i < 100; i++) {
    -        var e = data[i];
    -    }
    -}
    -
    - -

    The above code gets transformed before any execution is started. JavaScript moves -the var statements as well as the function declarations to the top of the -nearest surrounding scope.

    - -
    // var statements got moved here
    -var bar, someValue; // default to 'undefined'
    -
    -// the function declartion got moved up too
    -function test(data) {
    -    var goo, i, e; // missing block scope moves these here
    -    if (false) {
    -        goo = 1;
    -
    -    } else {
    -        goo = 2;
    -    }
    -    for(i = 0; i < 100; i++) {
    -        e = data[i];
    -    }
    -}
    -
    -bar(); // fails with a TypeError since bar is still 'undefined'
    -someValue = 42; // assignments are not affected by hoisting
    -bar = function() {};
    -
    -test();
    -
    - -

    Missing block scoping will not only move var statements out of loops and -their bodies, it will also make the results of certain if constructs -non-intuitive.

    - -

    In the original code the if statement seemed to modify the global -variable goo, while actually it modifies the local variable - after hoisting -has been applied.

    - -

    Without the knowledge about hoisting, below code might seem to raise a -ReferenceError.

    - -
    // check whether SomeImportantThing has been initiliazed
    -if (!SomeImportantThing) {
    -    var SomeImportantThing = {};
    -}
    -
    - -

    But of course, the above works due to the fact that the var statement is being -moved to the top of the global scope.

    - -
    var SomeImportantThing;
    -
    -// other code might initiliaze SomeImportantThing here, or not
    -
    -// make sure it's there
    -if (!SomeImportantThing) {
    -    SomeImportantThing = {};
    -}
    -
    - -

    Name resolution order

    - -

    All scopes in JavaScript, including the global scope, have the special name -this defined in them, which refers to the current object.

    - -

    Function scopes also have the name arguments defined in -them which contains the arguments that were passed to a function.

    - -

    For example, when trying to access a variable named foo inside the scope of a -function, JavaScript will lookup the name in the following order:

    - -
      -
    1. In case there is a var foo statement in the current scope use that.
    2. -
    3. If one of the function parameters is named foo use that.
    4. -
    5. If the function itself is called foo use that.
    6. -
    7. Go to the next outer scope and start with #1 again.
    8. -
    - - - -

    Namespaces

    - -

    A common problem of having only one global namespace is the likeliness of running -into problems where variable names clash. In JavaScript, this problem can -easily be avoided with the help of anonymous wrappers.

    - -
    (function() {
    -    // a self contained "namespace"
    -
    -    window.foo = function() {
    -        // an exposed closure
    -    };
    -
    -})(); // execute the function immediately
    -
    - -

    Unnamed functions are considered expressions; so in order to -being callable, they must first be evaluated.

    - -
    ( // evaluate the function inside the paranthesis
    -function() {}
    -) // and return the function object
    -() // call the result of the evaluation
    -
    - -

    There are other ways for evaluating and calling the function expression; which, -while different in syntax, do behave the exact same way.

    - -
    // Two other ways
    -+function(){}();
    -(function(){}());
    -
    - -

    In conclusion

    - -

    It is recommended to always use an anonymous wrapper for encapsulating code in -its own namespace. This does not only protect code against name clashes, it -also allows for better modularization of programs.

    - -

    Additionally, the use of global variables is considered bad practice. Any -use of them indicates badly written code that is prone to errors and hard to maintain.

    Arrays

    Array Iteration and Properties

    Although arrays in JavaScript are objects, there are no good reasons to use -the for in loop in for iteration on them. In fact there -are a number of good reasons against the use of for in on arrays.

    - - - -

    Since the for in loop enumerates all the properties that are on the prototype -chain and the only way to exclude those properties is to use -hasOwnProperty, it is already up to twenty times -slower than a normal for loop.

    - -

    Iteration

    - -

    In order to achieve the best performance when iterating over arrays, it is best -to use the classic for loop.

    - -
    var list = [1, 2, 3, 4, 5, ...... 100000000];
    -for(var i = 0, l = list.length; i < l; i++) {
    -    console.log(list[i]);
    -}
    -
    - -

    There is one extra catch in the above example, that is the caching of the -length of the array via l = list.length.

    - -

    Although the length property is defined on the array itself, there is still an -overhead for doing the lookup on each iteration of the loop. And while recent -JavaScript engines may apply optimization in this case, there is no way of -telling whether the code will run on one of these newer engines or not.

    - -

    In fact, leaving out the caching may result in the loop being only half as -fast as with the cached length.

    - -

    The length property

    - -

    While the getter of the length property simply returns the number of -elements that are contained in the array, the setter can be used to -truncate the array.

    - -
    var foo = [1, 2, 3, 4, 5, 6];
    -foo.length = 3;
    -foo; // [1, 2, 3]
    -
    -foo.length = 6;
    -foo; // [1, 2, 3]
    -
    - -

    Assigning a smaller length does truncate the array, but increasing the length -does not have any effect on the array.

    - -

    In conclusion

    - -

    For the best performance it is recommended to always use the plain for loop -and cache the length property. The use of for in on an array is a sign of -badly written code that is prone to bugs and bad performance.

    The Array Constructor

    Since the Array constructor is ambiguous in how it deals with its parameters, -it is highly recommended to always use the array literals - [] notation - -when creating new arrays.

    - -
    [1, 2, 3]; // Result: [1, 2, 3]
    -new Array(1, 2, 3); // Result: [1, 2, 3]
    -
    -[3]; // Result: [3]
    -new Array(3); // Result: []
    -new Array('3') // Result: ['3']
    -
    - -

    In cases when there is only one argument passed to the Array constructor, -and that argument is a Number, the constructor will return a new sparse -array with the length property set to the value of the argument. It should be -noted that only the length property of the new array will be set this way, -the actual indexes of the array will not be initialized.

    - -
    var arr = new Array(3);
    -arr[1]; // undefined
    -1 in arr; // false, the index was not set
    -
    - -

    The behavior of being able to set the length of the array upfront only comes in -handy in a few cases, like repeating a string, in which it avoids the use of a -for loop code.

    - -
    new Array(count + 1).join(stringToRepeat);
    -
    - -

    In conclusion

    - -

    The use of the Array constructor should be avoided as much as possible. -Literals are definitely preferred. They are shorter and have a clearer syntax; -therefore, they also increase the readability of the code.

    Types

    Equality and comparisons

    JavaScript has two different ways of comparing the values of objects for equality.

    - -

    The equals operator

    - -

    The equals operator consists of two equal signs: ==

    - -

    JavaScript features weak typing, that means, that the equals 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 equals operator

    - -

    The strict equals operator consists of three equal signs: ===

    - -

    Other than the normal equals operator, the strict equals operator does not -perform type coercion between its operands.

    - -
    ""           ===   "0"           // false
    -0            ===   ""            // false
    -0            ===   "0"           // false
    -false        ===   "false"       // false
    -false        ===   "0"           // false
    -false        ===   undefined     // false
    -false        ===   null          // false
    -null         ===   undefined     // false
    -" \t\r\n"    ===   0             // false
    -
    - -

    The above results are a lot clearer and allow for early breakage of code. This -hardens code to a certain degree and also gives performance improvements in case -the operands are of different types.

    - -

    Comparing objects

    - -

    While both == and === are stated as equality operators, they behave -different when at least one of their operands happens to be an Object.

    - -
    {} === {};                   // false
    -new String('foo') === 'foo'; // false
    -new Number(10) === 10;       // false
    -var foo = {};
    -foo === foo;                 // true
    -
    - -

    Here both operators compare for identity and not equality; that is, they -will compare for the same instance of the object, much like is in Python -and a pointer comparison in C do.

    - -

    In conclusion

    - -

    It is highly recommended to only use the strict equals operator. In cases -where types need to be coerced, it should be done explicitly -and not left to the language's complicated coercion rules.

    The typeof operator

    The typeof operator (together with -instanceof) is probably the biggest -design flaw of JavaScript, as it is near of being completely broken.

    - -

    Although instanceof still has its limited uses, typeof really has only one -practical use case, which does not happen to be checking the type of an -object.

    - - - -

    The JavaScript type table

    - -
    Value               Class      Type
    --------------------------------------
    -"foo"               String     string
    -new String("foo")   String     object
    -1.2                 Number     number
    -new Number(1.2)     Number     object
    -true                Boolean    boolean
    -new Boolean(true)   Boolean    object
    -new Date()          Date       object
    -new Error()         Error      object
    -[1,2,3]             Array      object
    -new Array(1, 2, 3)  Array      object
    -new Function("")    Function   function
    -/abc/g              RegExp     object (function in Nitro/V8)
    -new RegExp("meow")  RegExp     object (function in Nitro/V8)
    -{}                  Object     object
    -new Object()        Object     object
    -
    - -

    In the above table Type refers to the value, that the typeof operator returns. -As can be clearly seen, this value is anything but consistent.

    - -

    The Class refers to the value of the internal [[Class]] property of an object.

    - - - -

    In order to retrieve the value of [[Class]] one has to make use of the -toString method of Object.prototype.

    - -

    The Class of an object

    - -

    The specification gives exactly one way of accessing the [[Class]] value, -with the use of Object.prototype.toString.

    - -
    function is(type, obj) {
    -    var clas = Object.prototype.toString.call(obj).slice(8, -1);
    -    return obj !== undefined && obj !== null && clas === type;
    -}
    -
    -is('String', 'test'); // true
    -is('String', new String('test')); // true
    -
    - -

    In the above example, Object.prototype.toString gets called with the value of -this being set to the object whose [[Class]] value should be -retrieved.

    - - - -

    Testing for undefined variables

    - -
    typeof foo !== 'undefined'
    -
    - -

    The above will check whether foo was actually declared or not; just -referencing it would result in a ReferenceError. This is the only thing -typeof is actually useful for.

    - -

    In conclusion

    - -

    In order to check the type of an object, it is highly recommended to use -Object.prototype.toString; as this is the only reliable way of doing so. -As shown in the above type table, some return values of typeof are not defined -in the specification; thus, they can differ across various implementations.

    - -

    Unless checking whether a variable is defined, typeof should be avoided at -all costs.

    The instanceof operator

    The instanceof operator compares the constructors of its two operands. It is -only useful when comparing custom made objects. Used on built-in types, it is -nearly as useless as the typeof operator.

    - -

    Comparing custom objects

    - -
    function Foo() {}
    -function Bar() {}
    -Bar.prototype = new Foo();
    -
    -new Bar() instanceof Bar; // true
    -new Bar() instanceof Foo; // true
    -
    -// This just sets Bar.prototype to the function object Foo
    -// But not to an actual instance of Foo
    -Bar.prototype = Foo;
    -new Bar() instanceof Foo; // false
    -
    - -

    Using instanceof with native types

    - -
    new String('foo') instanceof String; // true
    -new String('foo') instanceof Object; // true
    -
    -'foo' instanceof String; // false
    -'foo' instanceof Object; // false
    -
    - -

    One important thing to note here is, that instanceof does not work on objects -that origin from different JavaScript contexts (e.g. different documents -in a web browser), since their constructors will not be the exact same object.

    - -

    In conclusion

    - -

    The instanceof operator should only be used when dealing with custom made -objects that origin from the same JavaScript context. Just like the -typeof operator, every other use of it should be avoided.

    Type casting

    JavaScript is a weakly typed language, so it will apply type coercion -wherever possible.

    - -
    // These are true
    -new Number(10) == 10; // Number.toString() is converted
    -                      // back to a number
    -
    -10 == '10';           // Strings gets converted to Number
    -10 == '+10 ';         // More string madness
    -10 == '010';          // And more 
    -isNaN(null) == false; // null converts to 0
    -                      // which of course is not NaN
    -
    -// These are false
    -10 == 010;
    -10 == '-10';
    -
    - - - -

    In order to avoid the above, use of the strict equal operator -is highly recommended. Although this avoids a lot of common pitfalls, there -are still many further issues that arise from JavaScript's weak typing system.

    - -

    Constructors of built-in types

    - -

    The constructors of the built in types like Number and String behave -differently when being used with the new keyword and without it.

    - -
    new Number(10) === 10;     // False, Object and Number
    -Number(10) === 10;         // True, Number and Number
    -new Number(10) + 0 === 10; // True, due to implicit conversion
    -
    - -

    Using a built-in type like Number as a constructor will create a new Number -object, but leaving out the new keyword will make the Number function behave -like a converter.

    - -

    In addition, having literals or non-object values in there will result in even -more type coercion.

    - -

    The best option is to cast to one of the three possible types explicitly.

    - -

    Casting to a string

    - -
    '' + 10 === '10'; // true
    -
    - -

    By prepending a empty string a value can easily be casted to a string.

    - -

    Casting to a number

    - -
    +'10' === 10; // true
    -
    - -

    Using the unary plus operator it is possible to cast to a number.

    - -

    Casting to a boolean

    - -

    By using the not operator twice, a value can be converted a boolean.

    - -
    !!'foo';   // true
    -!!'';      // false
    -!!'0';     // true
    -!!'1';     // true
    -!!'-1'     // true
    -!!{};      // true
    -!!true;    // true
    -

    Core

    Why not to use eval

    The eval function will execute a string of JavaScript code in the local scope.

    - -
    var foo = 1;
    -function test() {
    -    var foo = 2;
    -    eval('foo = 3');
    -    return foo;
    -}
    -test(); // 3
    -foo; // 1
    -
    - -

    But eval only executes in local scope when it is being called directly and -the name of the called function is actually eval.

    - -
    var foo = 1;
    -function test() {
    -    var foo = 2;
    -    var bar = eval;
    -    bar('foo = 3');
    -    return foo;
    -}
    -test(); // 2
    -foo; // 3
    -
    - -

    The use of eval should be avoided at all costs. 99.9% of its "uses" can be -achieved without it.

    - -

    eval in disguise

    - -

    The timeout functions setTimeout and setInterval can both -take a string as their first argument. This string will always get executed -in the global scope since eval is not being called directly in that case.

    - -

    Security issues

    - -

    eval also is a security problem as it executes any code given to it, -it should never be used with strings of unknown or untrusted origins.

    - -

    In conclusion

    - -

    eval should never be used, any code that makes use of it is to be questioned in -its workings, performance and security. In case something requires eval in -order to work, its design is to be questioned and should not be used in the -first place, a better design should be used, that does not require the use of -eval.

    undefined and null

    JavaScript has two distinct values for nothing, the more useful of these two -being undefined.

    - -

    The value undefined

    - -

    undefined is a type with exactly one value: undefined.

    - -

    The language also defines a global variable that has the value of undefined, -this variable is also called undefined. But this variable is not a constant, -nor is it a keyword of the language. This means that its value can be easily -overwritten.

    - - - -

    Some examples for when the value undefined is returned:

    - -
      -
    • Accessing the (unmodified) global variable undefined.
    • -
    • Implicit returns of functions due to missing return statements.
    • -
    • return statements which do not explicitly return anything.
    • -
    • Lookups of non-existent properties.
    • -
    • Function parameters which do not had any explicit value passed.
    • -
    • Anything that has been set to the value of undefined.
    • -
    - -

    Handling changes to the value of undefined

    - -

    Since the global variable undefined only holds a copy of the actual value of -undefined, assigning a new value to it does not change the value of the -type undefined.

    - -

    Still, in order to compare something against the value of undefined it is -necessary to retrieve the value of undefined first.

    - -

    In order to protect code against a possible overwritten undefined variable, a -common technique used is to add an additional parameter to an -anonymous wrapper, that gets no argument passed to it.

    - -
    var undefined = 123;
    -(function(something, foo, undefined) {
    -    // undefined in the local scope does 
    -    // now again refer to the value
    -
    -})('Hello World', 42);
    -
    - -

    Another way to achieve the same effect would be to use a declaration inside the -wrapper.

    - -
    var undefined = 123;
    -(function(something, foo) {
    -    var undefined;
    -    ...
    -
    -})('Hello World', 42);
    -
    - -

    The only difference being here, that this version results in 4 more bytes being -used in case it is minified and there is no other var statement inside the -anonymous wrapper.

    - -

    Uses of null

    - -

    While undefined in the context of the JavaScript language is mostly used in -the sense of a traditional null, the actual null (both a literal and a type) -is more or less just another data type.

    - -

    It is used in some JavaScript internals (like declaring the end of the -prototype chain by setting Foo.prototype = null), but in almost all cases it -can be replaced by undefined.

    Automatic semicolon insertion

    Although JavaScript has C style syntax, it does not enforce the use of -semicolons in the source code, it is possible to omit them.

    - -

    But JavaScript is not a semicolon-less language, it in fact needs the -semicolons in order to understand the source code. Therefore the JavaScript -parser automatically inserts them whenever it encounters a parse -error due to a missing semicolon.

    - -
    var foo = function() {
    -} // parse error, semicolon expected
    -test()
    -
    - -

    Insertion happens, and the parser tries again.

    - -
    var foo = function() {
    -}; // no error, parser continues
    -test()
    -
    - -

    The automatic insertion of semicolon is considered to be one of biggest -design flaws in the language, as it can change the behavior of code.

    - -

    How it works

    - -

    The code below has no semicolons in it, so it is up to the parser to decide where -to insert them.

    - -
    (function(window, undefined) {
    -    function test(options) {
    -        log('testing!')
    -
    -        (options.list || []).forEach(function(i) {
    -
    -        })
    -
    -        options.value.test(
    -            'long string to pass here',
    -            'and another long string to pass'
    -        )
    -
    -        return
    -        {
    -            foo: function() {}
    -        }
    -    }
    -    window.test = test
    -
    -})(window)
    -
    -(function(window) {
    -    window.someLibrary = {}
    -
    -})(window)
    -
    - -

    Below is the result of the parser's "guessing" game.

    - -
    (function(window, undefined) {
    -    function test(options) {
    -
    -        // Not inserted, lines got merged
    -        log('testing!')(options.list || []).forEach(function(i) {
    -
    -        }); // <- inserted
    -
    -        options.value.test(
    -            'long string to pass here',
    -            'and another long string to pass'
    -        ); // <- inserted
    -
    -        return; // <- inserted, breaks the return statement
    -        { // treated as a block
    -
    -            // a label and a single expression statement
    -            foo: function() {} 
    -        }; // <- inserted
    -    }
    -    window.test = test; // <- inserted
    -
    -// The lines got merged again
    -})(window)(function(window) {
    -    window.someLibrary = {}; // <- inserted
    -
    -})(window); //<- inserted
    -
    - - - -

    The parser drastically changed the behavior of the code above, in certain cases -it does the wrong thing.

    - -

    Leading parenthesis

    - -

    In case of a leading parenthesis, the parser will not insert a semicolon.

    - -
    log('testing!')
    -(options.list || []).forEach(function(i) {})
    -
    - -

    This code gets transformed into one line.

    - -
    log('testing!')(options.list || []).forEach(function(i) {})
    -
    - -

    Chances are very high that log does not return a function; therefore, -the above will yield a TypeError stating that undefined is not a function.

    - -

    In conclusion

    - -

    It is highly recommended to never omit semicolons, it is also advocated to -keep braces on the same line with their corresponding statements and to never omit -them for one single-line if / else statements. Both of these measures will -not only improve the consistency of the code, they will also prevent the -JavaScript parser from changing its behavior.

    Other

    setTimeout and setInterval

    Since JavaScript is asynchronous, it is possible to schedule the execution of a -function by using the setTimeout and setInterval functions.

    - - - -
    function foo() {}
    -var id = setTimeout(foo, 1000); // returns a Number > 0
    -
    - -

    When setTimeout gets called, it will return the ID of the timeout and schedule -foo to run in approximately one thousand milliseconds in the future. -foo will then get executed exactly once.

    - -

    Depending on the timer resolution of the JavaScript engine that is running the -code, as well as the fact that JavaScript is single threaded and other code that -gets executed might block the thread, it is by no means a safe bet that one -will get the exact delay that was specified in the setTimeout call.

    - -

    The function that was passed as the first parameter will get called by the -global object, that means, that this inside the called function -refers to that very object.

    - -
    function Foo() {
    -    this.value = 42;
    -    this.method = function() {
    -        // this refers to the global object
    -        console.log(this.value); // will log undefined
    -    };
    -    setTimeout(this.method, 500);
    -}
    -new Foo();
    -
    - - - -

    Stacking calls with setInterval

    - -

    While setTimeout only runs the function once, setInterval - as the name -suggests - will execute the function every X milliseconds. But its use is -discouraged.

    - -

    When code that is being executed blocks the timeout call, setInterval will -still issue more calls to the specified function. This can, especially with small -intervals, result in function calls stacking up.

    - -
    function foo(){
    -    // something that blocks for 1 second
    -}
    -setInterval(foo, 100);
    -
    - -

    In the above code foo will get called once and will then block for one second.

    - -

    While foo blocks the code setInterval will still schedule further calls to -it. Now, when foo has finished, there will already be ten further calls to -it waiting for execution.

    - -

    Dealing with possible blocking code

    - -

    The easiest as well as most controllable solution, is to use setTimeout within -the function itself.

    - -
    function foo(){
    -    // something that blocks for 1 second
    -    setTimeout(foo, 100);
    -}
    -foo();
    -
    - -

    Not only does this encapsulate the setTimeout call, but it also prevents the -stacking of calls and it gives additional control.foo itself can now decide -whether it wants to run again or not.

    - -

    Manually clearing timeouts

    - -

    Clearing timeouts and intervals works by passing the respective ID to -clearTimeout or clearInterval, depending which set function was used in -the first place.

    - -
    var id = setTimeout(foo, 1000);
    -clearTimeout(id);
    -
    - -

    Clearing all timeouts

    - -

    As there is no built-in method for clearing all timeouts and/or intervals, -it is necessary to use brute force in order to achieve this functionality.

    - -
    // clear "all" timeouts
    -for(var i = 1; i < 1000; i++) {
    -    clearTimeout(i);
    -}
    -
    - -

    There might still be timeouts that are unaffected by this arbitrary number; -therefore, is is instead recommended to keep track of all the timeout IDs, so -they can be cleared specifically.

    - -

    Hidden use of eval

    - -

    setTimeout and setInterval can also take a string as their first parameter. -This feature should never be used, since it internally makes use of eval.

    - - - -
    function foo() {
    -    // will get called
    -}
    -
    -function bar() {
    -    function foo() {
    -        // never gets called
    -    }
    -    setTimeout('foo()', 1000);
    -}
    -bar();
    -
    - -

    Since eval is not getting called directly in this case, the string -passed to setTimeout will get executed in the global scope; thus, it will -not use the local variable foo from the scope of bar.

    - -

    It is further recommended to not use a string for passing arguments to the -function that will get called by either of the timeout functions.

    - -
    function foo(a, b, c) {}
    -
    -// NEVER use this
    -setTimeout('foo(1,2, 3)', 1000)
    -
    -// Instead use an anonymous function
    -setTimeout(function() {
    -    foo(a, b, c);
    -}, 1000)
    -
    - - - -

    In conclusion

    - -

    Never should a string be used as the parameter of setTimeout or -setInterval. It is a clear sign of really bad code, when arguments need -to be supplied to the function that gets called. An anonymous function should -be passed that then takes care of the actual call.

    - -

    Further, the use of setInterval should be avoided since its scheduler is not -blocked by executing JavaScript.

    Copyright © 2011. Built with -Node.jsusing a -jadetemplate. -

    \ No newline at end of file From e9327b55a2e9b9356013c3b105610473f20eb619 Mon Sep 17 00:00:00 2001 From: Zhang Yi Jiang Date: Wed, 30 Mar 2011 00:36:32 -0700 Subject: [PATCH 131/641] Fixed issue #70 --- doc/en/function/this.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/function/this.md b/doc/en/function/this.md index 0cff9ee4..85b11f0f 100644 --- a/doc/en/function/this.md +++ b/doc/en/function/this.md @@ -79,7 +79,7 @@ local variable inside of `method` which refers to `Foo`. test(); } -`that` is just a normal name, but it is commonly used for the reference to an +`that` is just a normal variable name, but it is commonly used for the reference to an outer `this`. In combination with [closures](#function.closures), it can also be used to pass `this` values around. @@ -91,7 +91,7 @@ Another thing that does **not** work in JavaScript is function aliasing, that is var test = someObject.methodTest; test(); -Due to the first case `test` now acts like like a plain function call; therefore, +Due to the first case `test` now acts like a plain function call; therefore, `this` inside it will no longer refer to `someObject`. While the late binding of `this` might seem like a bad idea at first, it is in From ea8b09f3913b12a060959db7ac02008ecae7ea3f Mon Sep 17 00:00:00 2001 From: Lukasz Kufel Date: Fri, 1 Apr 2011 16:16:21 +0200 Subject: [PATCH 132/641] Polish translation. Intro and object translated. --- doc/language.json | 2 +- doc/pl/full_index.json | 74 ++++++++++++++++++++ doc/pl/index.json | 28 ++++++++ doc/pl/intro/authors.md | 8 +++ doc/pl/intro/contributors.md | 7 ++ doc/pl/intro/hosting.md | 6 ++ doc/pl/intro/index.md | 13 ++++ doc/pl/intro/license.md | 11 +++ doc/pl/intro/translators.md | 6 ++ doc/pl/object/forinloop.md | 50 ++++++++++++++ doc/pl/object/general.md | 102 +++++++++++++++++++++++++++ doc/pl/object/hasownproperty.md | 52 ++++++++++++++ doc/pl/object/prototype.md | 118 ++++++++++++++++++++++++++++++++ 13 files changed, 476 insertions(+), 1 deletion(-) create mode 100644 doc/pl/full_index.json create mode 100644 doc/pl/index.json create mode 100644 doc/pl/intro/authors.md create mode 100644 doc/pl/intro/contributors.md create mode 100644 doc/pl/intro/hosting.md create mode 100644 doc/pl/intro/index.md create mode 100644 doc/pl/intro/license.md create mode 100644 doc/pl/intro/translators.md create mode 100644 doc/pl/object/forinloop.md create mode 100644 doc/pl/object/general.md create mode 100644 doc/pl/object/hasownproperty.md create mode 100644 doc/pl/object/prototype.md diff --git a/doc/language.json b/doc/language.json index 260c7e17..e646a219 100644 --- a/doc/language.json +++ b/doc/language.json @@ -1,5 +1,5 @@ { "default": "en", - "listed": ["en", "fi", "ru", "zh"] + "listed": ["en", "fi", "ru", "zh", "pl"] } diff --git a/doc/pl/full_index.json b/doc/pl/full_index.json new file mode 100644 index 00000000..469324be --- /dev/null +++ b/doc/pl/full_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": "?Wnętrze?", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon" + ] + }, + { + "title": "Inne", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/doc/pl/index.json b/doc/pl/index.json new file mode 100644 index 00000000..9d904471 --- /dev/null +++ b/doc/pl/index.json @@ -0,0 +1,28 @@ +{ + "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" + ] + } + ] +} diff --git a/doc/pl/intro/authors.md b/doc/pl/intro/authors.md new file mode 100644 index 00000000..380c158e --- /dev/null +++ b/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/doc/pl/intro/contributors.md b/doc/pl/intro/contributors.md new file mode 100644 index 00000000..a71575a1 --- /dev/null +++ b/doc/pl/intro/contributors.md @@ -0,0 +1,7 @@ +## Kontrybutorzy + + - [Caio Romão][1] (Poprawki pisowni) + - [Andreas Blixt][2] (Poprawki językowe) + +[1]: https://github.com/caio +[2]: https://github.com/blixt diff --git a/doc/pl/intro/hosting.md b/doc/pl/intro/hosting.md new file mode 100644 index 00000000..96bc2a1d --- /dev/null +++ b/doc/pl/intro/hosting.md @@ -0,0 +1,6 @@ +## Hosting +JavaScript Garden znajduje się na serwerach GitHub, ale dzięki wsparciu +[Cramer Rozwoju] [1] posiadamy mirror na serwerze [JavaScriptGarden.info] [2]. + +[1]: http://cramerdev.com/ +[2]: http://javascriptgarden.info/ diff --git a/doc/pl/intro/index.md b/doc/pl/intro/index.md new file mode 100644 index 00000000..03ea1493 --- /dev/null +++ b/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/doc/pl/intro/license.md b/doc/pl/intro/license.md new file mode 100644 index 00000000..7e2c77d0 --- /dev/null +++ b/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ówek zgłoś proszę +[problem] [3] lub rozwiązag go i zglosić 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/doc/pl/intro/translators.md b/doc/pl/intro/translators.md new file mode 100644 index 00000000..b827667b --- /dev/null +++ b/doc/pl/intro/translators.md @@ -0,0 +1,6 @@ +## Tłumaczenie + + - [Łukasz Kufel][1] ([qfel13.pl][2]) + +[1]: https://github.com/qfel13 +[2]: http://qfel13.pl \ No newline at end of file diff --git a/doc/pl/object/forinloop.md b/doc/pl/object/forinloop.md new file mode 100644 index 00000000..218b0117 --- /dev/null +++ b/doc/pl/object/forinloop.md @@ -0,0 +1,50 @@ +## The `for in` Loop + +Podobnie jak operator `in`, pętla `for in` przeszukuje łańcuch prototypów +podczas iteracji po właściwościach obiektu. + +> **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ż nie jest możliwe, aby zmienić zachowanie pętli `for in` to niezbędne +jest odfiltrowanie niechcianych właściwości wewnątrz ciała pętli, korzystając +z metody [`hasOwnProperty`](#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. + +### Korzystanie z `hasOwnProperty` do odfiltrowania + + // foo z przykładu powyżej + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +To jest jedyna poprawna wersja, którą należy używać. Ze względu na użycie +`hasOwnProperty` zostanie wypisane **jedynie** `moo`. Gdy opuścimy `hasOwnProperty` +kod będzie podatny na błędy, gdy natywne prototypy np. `Object.prototype` +zostanie rozszerzony. + +[Prototype][1] jest jednym z szeroko rozpowszechniony framework, który dokonuje +takiego rozszerzenia. Używanie tego frameworku oraz nie używanie w pętle `for in` +metody `hasOwnProperty` gwarantuje błędy w wykonaniu. + +### Wnioski + +Zaleca się aby zawsze używać metody `hasOwnProperty`. Nigdy nie powinno się dokonywać +żadnych założeń na temat środowiska, w którym kod będzie wykonywany i czy natywne +prototypy zostały rozszerzone czy nie. + +[1]: http://www.prototypejs.org/ diff --git a/doc/pl/object/general.md b/doc/pl/object/general.md new file mode 100644 index 00000000..24a074e3 --- /dev/null +++ b/doc/pl/object/general.md @@ -0,0 +1,102 @@ +## Wykorzystanie obiektów i ich właściwości + +Wszystko w JavaScripcie zachowuje sie 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 wykorzystanie literałów liczbowych jako obiektu. +Spowodowanie jest to usterką w parserze JavaScript, który interpretuje kropkę +po literale liczbowym jako rozdzielenie części całkowitej od części po przecinku +liczby. + + 2.toString(); // wyrzuca błąd SyntaxError + +Istnieje kilka rozwiązań, dzieki którym literał liczbowy będzie zachowywał się +jak obiekt. + + 2..toString(); // druga kropka jest poprawnie rozpoznana + 2 .toString(); // zauważ, że pozostawiona jest spacja przed kropką + (2).toString(); // 2 zostanie zewaluowane najpiewr + +### Obiekty jako typy danych + +Obiekty w języku JavaScript mogą być używana jako [*tablice asocjacyjne*][1]. +Ponieważ obiekty głównie składają się z mapowań pomiędzy nazwanymi właściwościami (kluczami) +a wartościami dla tych atrybutów. + +Używając literału obiektu - notacji `{}` - istnieje możliwość stworzenie obiektu prostego. +Ten nowy obiekt bedzie [dziedziczył](#object.prototype) z `Object.prototype` oraz +nie bedzie posiadał żadnych [własnych właściwości](#object.hasownproperty) zdefiniowanych w sobie. + + var foo = {}; // nowy pusty obiekt + + // nowy obiekt z właściwością test o wartości 12 + var bar = {test: 12}; + +### Dostęp do właściwości + +Właściwości obiektu można uzyskać na dwa sposoby, poprzez notację z kropką +lub notacje z nawiasami kwadratowymi. + + var foo = {name: 'Kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // wyrzuca błąd SyntaxError + foo['1234']; // działa, zwraca undefined + +Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą że notacja z nawiasami +kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzycenia +błędu podczas odczytu nieistniejącej właściwości. + +### Usuwanie właściwości + +Jedynym sposobem na faktycze usunięcie własności z obiektu jest użycie operatora +`delete`. Ustawienie własności na `undefined` lub `null` usunie tylko *wartość* +związaną z własnością, ale nie usunie to *klucza* (nazwy własności) z obiektu. + + var obj = { + bar: 1, + foo: 2, + baz: 3 + }; + obj.bar = undefined; + obj.foo = null; + delete obj.baz; + + for(var i in obj) { + if (obj.hasOwnProperty(i)) { + console.log(i, '' + obj[i]); + } + } + +Powyższy kod wypisuje dwie linie `bar undefined` i `foo null` - tylko własność `baz` +została usunięta i dlatego nie została wypisana. + +### Notacja właściwości + + var test = { + 'case': 'I am a keyword so I must be notated as a string', + delete: 'I am a keyword too so me' // wyrzuca błąd SyntaxError + }; + +Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst(bez cudzysłowów +lub apostrofów) lub jako string (w cudzisłowach lub apostrofach). +Ze względu na kolejne niedociągnięcie w parserze JavaScript +powyższy kod wyrzuci błąd `SyntaxError` dla implementacji JavaScript ponizej ECMAScript 5. + +Ten błąd wynika z faktu, że `delete` jest *słowem kluczowym*, dlatego musi zostać +zapisany jako *string* (z cudzysłowami lub apostrofami), aby zapewnić, że zostanie +to poprawnie zinterpretowane przez starsze silniki języka JavaScript. + +[1]: http://pl.wikipedia.org/wiki/Tablica_asocjacyjna + diff --git a/doc/pl/object/hasownproperty.md b/doc/pl/object/hasownproperty.md new file mode 100644 index 00000000..9c1775c0 --- /dev/null +++ b/doc/pl/object/hasownproperty.md @@ -0,0 +1,52 @@ +## `hasOwnProperty` + +W celu sprawdzenia czy dana właściwość została zdefiniowana *w tym* obiekcie a **nie** +w [łańcuchu prototypów](#object.prototype) niezbędne jest skorzystanie z metody +`hasOwnProperty`, która wszystkie obiekty dziedziczą z `Object.prototype`. + +> **Uwaga:** **Nie** jest wystarczające, by sprawdzić, czy właściwość jest `undefined`. +> Ponieważ właściwość może istnieć, ale jej wartość być ustawiona na `undefined`. + +`hasOwnProperty` jest jedyna metodą w języku JavaScript która operuje na właściwościach +i **nie** przegląda całego łańcucha prototypów. + + // Zatrucie Object.prototype + Object.prototype.bar = 1; + var foo = {goo: undefined}; + + foo.bar; // 1 + 'bar' in foo; // true + + foo.hasOwnProperty('bar'); // false + foo.hasOwnProperty('goo'); // true + +Tylko `hasOwnProperty` da prawidłowy i oczekiwany rezultat. Jest to istotne podczas +iteracji po właściwościach obiektu. **Nie** ma innego sposobu na ominięcie +właściwości, która nie została zdefiniowana przez ten **konkretny** obiekt, +ale gdzieś indziej w łańcuchu prototypów. + +### `hasOwnProperty` jako właściwość + +JavaScript **nie** chroni właściwości o nazwie `hasOwnProperty`, zatem istnieje +możliwość, że obiekt może posiadać tak nazwaną właściwość. Konieczne jest użycie +*zewnętrznego* `hasOwnProperty`, aby otrzymać poprawne rezultaty. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // zawsze zwraca false + + // Została użyta metoda innego obiektu i wywołana z konkekstem `this` ustawionym na foo + ({}).hasOwnProperty.call(foo, 'bar'); // true + +### Wnioski + +**Jedyną** metodą służącą do sprawdzenia zdefiniowania jakiejś właściwości w konkretnym +obiekcie jest metoda `hasOwnProperty`. Zaleca się korzystać z `hasOwnProperty` jako część +**każdej** [pętli `for in`](#object.forinloop), pozwoli to uniknąć błędów pochodzących z +rozszerzonych natywnych [prototypów](#object.prototype). + diff --git a/doc/pl/object/prototype.md b/doc/pl/object/prototype.md new file mode 100644 index 00000000..c25e1df1 --- /dev/null +++ b/doc/pl/object/prototype.md @@ -0,0 +1,118 @@ +## Prototyp + +JavaScript nie posiada klasycznego modelu dziedziczenia, lecz zamiast tego +dziedziczenie jest realizowane poprzez *prototypy*. + +Choć jest to często uważane za jedną ze słabości języka JavaScript, +prototypowy model dziedziczenia, jest w rzeczywistości potężniejszy od klasycznego +modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowym +jest dość proste, podczas gdy zrobienie odwrotnie to już o wiele trudniejsze zadanie. + +Ze względu na fakt, że w JavaScript jest w zasadzie jedynym powszechnie stosowanym +językiem, któy posiada prototypowy model dziedziczenia, to wymaga troche czasu aby +dostosować się do różnic pomiędzy tymi dwoma modelami. + +Pierwszą znaczącą różnicą jest to, że dziedziczenie w JavaScript odbywa się za pomocą +tak zwanych *łańcuchów prototypów*. + +> **Uwaga:** Używanie po prosstu `Bar.prototype = Foo.prototype` spowoduje, że oba obiekty +> będą korzystały z **tego samego** prototypu. W związku z tym zmiany na prototypie jednego +> obiektu będą również zmieniały prototyp drugiego obiektu, co jest w wiekszości przypadków +> 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' } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +W powyższym przykładzie obiekt `test będzie dziedziczył z obydwu tj. +`Bar.prototyp` i `Foo.prototyp`, stąd będzie miał dostęp do funkcji `method`, +która była zdefiniowana w `Foo`. Ponadto obiekt będzie miał dostęp do +właściwości `value`, która jest jednyną instancją `Foo` i stała się jego prototypem. +Ważne jest, aby pamiętać `new Bar` **nie** tworzy nowej instancji `Foo`, +ale wykorzystuje instancje, którą jest przypisana do własności `prototype`. +Zatem Wszystkie instancje `Bar` będą dzieliły tą samą własność `value`. + +> **Uwaga:** **Nie** należy używać konstrukcji `Bar.prototype = Foo`, +> ponieważ nie spowoduje ona przypisania prototypu `Foo` a raczej obiektu +> funckji `Foo`. Zatem łańcuch prototypów nie bedzie zawierał `Foo.prototype`, +> ale `Function.prototype`, więc metoda `method` nie będzie w łańcuchu prototypów. + +### Wyszukiwanie własności + +Podczas dostępu do właściwości obiektu, JavaScript przejdzie w górę łańcucha +prototypów dopóki nie znajdzie właściwości z żądaną nazwą. + +Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha mianowicie `Object.prototype` +i nadal nie znajdzie określonej właściwości, to zwróci wartość +[undefined](#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 powoduje zawsze 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 używanych niedoskonałej częsci języka JavaScript. + +Technika ta nazywana jest [monkey patching][1] i łamie zasady *enkapsulacji*. +Jednak jest szeroko rozpowszechniona w frameworkach takich jak [Prototype][2]. +Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez dodawanie do nich +*niestandardowych* funkcjonalności. + +**Jedynym** dobrym powodem do rozszerzania wbudowanych prototypów jest portowanie +funkcjonalności znajdujących sie w nowszych silnikach JavaScript np. [`Array.forEach`][3] + +### Wnioski + +Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczanie +należy **całkowicie** rozumieć prototypowy model dziedziczenia. Ponadto należy uważać +na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń +aby uniknąć problemów z wydajnością. Natywne prototypy nie powinny **nigdy** być +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 From f3352aa1ed818a31c32f3e0b0ffc1d71e3eb4eeb Mon Sep 17 00:00:00 2001 From: oozcitak Date: Thu, 7 Apr 2011 01:15:09 +0300 Subject: [PATCH 133/641] Translated index --- doc/tr/index.json | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 doc/tr/index.json diff --git a/doc/tr/index.json b/doc/tr/index.json new file mode 100644 index 00000000..36a94b8d --- /dev/null +++ b/doc/tr/index.json @@ -0,0 +1,72 @@ +{ + "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": [ + "authors", + "contributors", + "license" + ] + }, + { + "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" + ] + }, + { + "title": "Diğer", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} From 73924e356c62a09a6d44b0e62c77c416649b617f Mon Sep 17 00:00:00 2001 From: oozcitak Date: Thu, 7 Apr 2011 01:15:32 +0300 Subject: [PATCH 134/641] Translated arrays --- doc/tr/array/constructor.md | 36 +++++++++++++++++++++++ doc/tr/array/general.md | 58 +++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 doc/tr/array/constructor.md create mode 100644 doc/tr/array/general.md diff --git a/doc/tr/array/constructor.md b/doc/tr/array/constructor.md new file mode 100644 index 00000000..2e10c1df --- /dev/null +++ b/doc/tr/array/constructor.md @@ -0,0 +1,36 @@ +## `Dizi` Oluşturucusu + +`Dizi` oluşturucusunun parametrelerini nasıl değerlendirdiği belirsiz olduğu +için, yeni diziler oluşturulurken her zaman dizi değişmezlerinin - `[]` +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'] + +`Dizi` 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ş olacaktır, dizi indisleri ilklenmemiş +olacaktır. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, indisler ilklenmedi + +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ç + +`Dizi` oluşturucusunun kullanılmasından mümkün olduğu kadar kaçınılmalıdır. +Bunun yerine her zaman dizi değişmezleri tercih edilmelidir. Hem daha kısadırlar +hem de daha net bir sentaksa sahiptirler; bu nedenle programın okunabilirliğini +de artırırlar. + diff --git a/doc/tr/array/general.md b/doc/tr/array/general.md new file mode 100644 index 00000000..5b6767d2 --- /dev/null +++ b/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 saydığı 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 için en iyi yol 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. + From f35738b4b4a6b068517851049c430f160ac1c341 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Thu, 7 Apr 2011 01:21:55 +0300 Subject: [PATCH 135/641] Wrong branch... --- doc/tr/array/constructor.md | 36 ------------------- doc/tr/array/general.md | 58 ------------------------------ doc/tr/index.json | 72 ------------------------------------- 3 files changed, 166 deletions(-) delete mode 100644 doc/tr/array/constructor.md delete mode 100644 doc/tr/array/general.md delete mode 100644 doc/tr/index.json diff --git a/doc/tr/array/constructor.md b/doc/tr/array/constructor.md deleted file mode 100644 index 2e10c1df..00000000 --- a/doc/tr/array/constructor.md +++ /dev/null @@ -1,36 +0,0 @@ -## `Dizi` Oluşturucusu - -`Dizi` oluşturucusunun parametrelerini nasıl değerlendirdiği belirsiz olduğu -için, yeni diziler oluşturulurken her zaman dizi değişmezlerinin - `[]` -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'] - -`Dizi` 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ş olacaktır, dizi indisleri ilklenmemiş -olacaktır. - - var arr = new Array(3); - arr[1]; // undefined - 1 in arr; // false, indisler ilklenmedi - -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ç - -`Dizi` oluşturucusunun kullanılmasından mümkün olduğu kadar kaçınılmalıdır. -Bunun yerine her zaman dizi değişmezleri tercih edilmelidir. Hem daha kısadırlar -hem de daha net bir sentaksa sahiptirler; bu nedenle programın okunabilirliğini -de artırırlar. - diff --git a/doc/tr/array/general.md b/doc/tr/array/general.md deleted file mode 100644 index 5b6767d2..00000000 --- a/doc/tr/array/general.md +++ /dev/null @@ -1,58 +0,0 @@ -## 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 saydığı 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 için en iyi yol 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/doc/tr/index.json b/doc/tr/index.json deleted file mode 100644 index 36a94b8d..00000000 --- a/doc/tr/index.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "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": [ - "authors", - "contributors", - "license" - ] - }, - { - "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" - ] - }, - { - "title": "Diğer", - "dir": "other", - "articles": [ - "timeouts" - ] - } - ] -} From ec3b64a82ae830fb3fdefabc835a04d9918028cf Mon Sep 17 00:00:00 2001 From: oozcitak Date: Thu, 7 Apr 2011 01:23:46 +0300 Subject: [PATCH 136/641] translated index --- doc/tr/index.json | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 doc/tr/index.json diff --git a/doc/tr/index.json b/doc/tr/index.json new file mode 100644 index 00000000..36a94b8d --- /dev/null +++ b/doc/tr/index.json @@ -0,0 +1,72 @@ +{ + "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": [ + "authors", + "contributors", + "license" + ] + }, + { + "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" + ] + }, + { + "title": "Diğer", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} From 247ded7b7b3fe3bd0cb21f34f954bd8da2e71eb8 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Thu, 7 Apr 2011 01:24:12 +0300 Subject: [PATCH 137/641] translated array --- doc/tr/array/constructor.md | 36 +++++++++++++++++++++++ doc/tr/array/general.md | 58 +++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 doc/tr/array/constructor.md create mode 100644 doc/tr/array/general.md diff --git a/doc/tr/array/constructor.md b/doc/tr/array/constructor.md new file mode 100644 index 00000000..2e10c1df --- /dev/null +++ b/doc/tr/array/constructor.md @@ -0,0 +1,36 @@ +## `Dizi` Oluşturucusu + +`Dizi` oluşturucusunun parametrelerini nasıl değerlendirdiği belirsiz olduğu +için, yeni diziler oluşturulurken her zaman dizi değişmezlerinin - `[]` +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'] + +`Dizi` 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ş olacaktır, dizi indisleri ilklenmemiş +olacaktır. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, indisler ilklenmedi + +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ç + +`Dizi` oluşturucusunun kullanılmasından mümkün olduğu kadar kaçınılmalıdır. +Bunun yerine her zaman dizi değişmezleri tercih edilmelidir. Hem daha kısadırlar +hem de daha net bir sentaksa sahiptirler; bu nedenle programın okunabilirliğini +de artırırlar. + diff --git a/doc/tr/array/general.md b/doc/tr/array/general.md new file mode 100644 index 00000000..5b6767d2 --- /dev/null +++ b/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 saydığı 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 için en iyi yol 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. + From 62fd141a5b94e9663be06672d66bf908336f76a3 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Thu, 7 Apr 2011 01:30:53 +0300 Subject: [PATCH 138/641] changed wording a litte --- doc/tr/array/constructor.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/tr/array/constructor.md b/doc/tr/array/constructor.md index 2e10c1df..b007c23b 100644 --- a/doc/tr/array/constructor.md +++ b/doc/tr/array/constructor.md @@ -14,8 +14,7 @@ notasyonu - kullanılması tavsiye olunur. `Dizi` 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ş olacaktır, dizi indisleri ilklenmemiş -olacaktır. +`length` özelliği belirlenmiş olup dizi indisleri tanımsız olacaktır. var arr = new Array(3); arr[1]; // undefined From f59fac3c5ef17d4ceadb917b0cfa5a1a133c1b10 Mon Sep 17 00:00:00 2001 From: "sanshi.email" Date: Thu, 7 Apr 2011 14:44:57 +0800 Subject: [PATCH 139/641] Typos in intro section --- doc/zh/intro/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/zh/intro/index.md b/doc/zh/intro/index.md index 93a4c73c..f141e173 100755 --- a/doc/zh/intro/index.md +++ b/doc/zh/intro/index.md @@ -7,7 +7,7 @@ JavaScript 秘密花园**不是**用来教你 JavaScript。为了更好的理解这篇文章的内容, 你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习[向导][1]。 -> **ES5 注意:** **译者注:** ES5 指的是ECMAScript 5,是 ECMAScript 标准语言的下一版本,正在开发中。 +> **译者注:** 文中提到的 ES5 是 ECMAScript 5 的简写,是 ECMAScript 标准语言的下一版本,正在开发中。 JavaScript 是此标准语言的一个方言。 [1]: https://developer.mozilla.org/en/JavaScript/Guide From af582320344aff5172a60223b1610304e2cdf253 Mon Sep 17 00:00:00 2001 From: "sanshi.email" Date: Thu, 7 Apr 2011 15:03:03 +0800 Subject: [PATCH 140/641] Update the prototype section, word optimization --- doc/zh/object/prototype.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/zh/object/prototype.md b/doc/zh/object/prototype.md index a02c4442..a9e09841 100755 --- a/doc/zh/object/prototype.md +++ b/doc/zh/object/prototype.md @@ -40,11 +40,11 @@ other way around is a far more difficult task.) {method: ...}; Object.prototype {toString: ... /* etc. */}; -* + 上面的例子中,`test` 对象从 `Bar.prototype` 和 `Foo.prototype` 继承下来;因此, -它能访问 `Foo` 的原型方法 `method`。它也同时能够访问那**一个**作为它原型的 `Foo` 实例 -的属性 `value`。需要注意的是 `new Bar()` **不会**创造出一个新的 `Foo` 实例,而是 -重新使用设定为它的原型的实例;也就是说,所有的 `Bar` 实例都将会有**同样**的 `value` 属性。 +它能访问 `Foo` 的原型方法 `method`。同时,它也能够访问**那个**定义在原型上的 `Foo` 实例属性 `value`。 +需要注意的是 `new Bar()` **不会**创造出一个新的 `Foo` 实例,而是 +重复使用它原型上的那个实例;因此,所有的 `Bar` 实例都会共享**相同**的 `value` 属性。 > **注意:** **不要**使用 `Bar.prototype = Foo`,因为这不会执行 `Foo` 的原型,而是指向函数 `Foo`。 > 因此原型链将会回溯到 `Function.prototype` 而不是 `Foo.prototype`,因此 `method` 将不会在 Bar 的原型链上。 From d0f7c68fabbae4893b58ac5b0986c4d66cc0cd25 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Thu, 7 Apr 2011 18:33:13 +0300 Subject: [PATCH 141/641] translated core --- doc/tr/core/eval.md | 48 ++++++++++++++++ doc/tr/core/semicolon.md | 118 +++++++++++++++++++++++++++++++++++++++ doc/tr/core/undefined.md | 71 +++++++++++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 doc/tr/core/eval.md create mode 100644 doc/tr/core/semicolon.md create mode 100644 doc/tr/core/undefined.md diff --git a/doc/tr/core/eval.md b/doc/tr/core/eval.md new file mode 100644 index 00000000..74b444e9 --- /dev/null +++ b/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/doc/tr/core/semicolon.md b/doc/tr/core/semicolon.md new file mode 100644 index 00000000..9ba7efd7 --- /dev/null +++ b/doc/tr/core/semicolon.md @@ -0,0 +1,118 @@ +## Otomatik noktalı virgül ilavesi + +JavaScript sentaksı C'ye benzese de, noktalı virgül kullanılması +zurunlu **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 heme 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 olunur, 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/doc/tr/core/undefined.md b/doc/tr/core/undefined.md new file mode 100644 index 00000000..dfaa24a2 --- /dev/null +++ b/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çindeki 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. + From 2198e3d8b1636b36d14b8a92a41b86f26e4ccb8c Mon Sep 17 00:00:00 2001 From: oozcitak Date: Fri, 8 Apr 2011 03:25:20 +0300 Subject: [PATCH 142/641] translated function/arguments --- doc/tr/function/arguments.md | 124 +++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 doc/tr/function/arguments.md diff --git a/doc/tr/function/arguments.md b/doc/tr/function/arguments.md new file mode 100644 index 00000000..1eb016ea --- /dev/null +++ b/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 + + From 8c30f11225e92ff0fea5c30e734a52cf7d606c3a Mon Sep 17 00:00:00 2001 From: oozcitak Date: Fri, 8 Apr 2011 03:40:49 +0300 Subject: [PATCH 143/641] translated intro --- doc/tr/intro/authors.md | 8 ++++++++ doc/tr/intro/contributors.md | 8 ++++++++ doc/tr/intro/index.md | 14 ++++++++++++++ doc/tr/intro/license.md | 13 +++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 doc/tr/intro/authors.md create mode 100644 doc/tr/intro/contributors.md create mode 100644 doc/tr/intro/index.md create mode 100644 doc/tr/intro/license.md diff --git a/doc/tr/intro/authors.md b/doc/tr/intro/authors.md new file mode 100644 index 00000000..0425d089 --- /dev/null +++ b/doc/tr/intro/authors.md @@ -0,0 +1,8 @@ +## Yazarlar + +Bu rehber, hoş birer Stack Overflow kullanıcısı olan [Ivo Wetzel][1] (Yazım) ve +[Zhang Yi Jiang][2] (Tasarım) tarafından hazırlanmıştır. + +[1]: http://stackoverflow.com/users/170224/ivo-wetzel +[2]: http://stackoverflow.com/users/313758/yi-jiang + diff --git a/doc/tr/intro/contributors.md b/doc/tr/intro/contributors.md new file mode 100644 index 00000000..c8d1a96a --- /dev/null +++ b/doc/tr/intro/contributors.md @@ -0,0 +1,8 @@ +## Katkıda bulunanlar + + - [Caio Romão][1] (Yazım düzeltmeleri) + - [Andreas Blixt][2] (Dil düzeltmeleri) + +[1]: https://github.com/caio +[2]: https://github.com/blixt + diff --git a/doc/tr/intro/index.md b/doc/tr/intro/index.md new file mode 100644 index 00000000..6d07b21e --- /dev/null +++ b/doc/tr/intro/index.md @@ -0,0 +1,14 @@ +## Giriş + +**JavaScript Garden** JavaScript programlama dilinin acayiplik üzerine derlenmiş +dökümanlardır. Henüz ustalaşmamış JavaScript programcılarının sıkça yaptığı +yanlışlar, dile has ince hatalar ve performans sorunlarına karşı tavsiyeler +içerir. + +JavaScript Garden'ın amacı JavaScript öğretmek **değildir**. Bu rehberde +anlatılan konuları anlamak için JavaScript dilini önceden biliyor olmak şiddetle +tavsiye olunur. Eğer JavaScript dilinin temellerini öğrenmek istiyorsanız, +lütfen Mozilla Programcı Ağı'nda bulunan mükemmel [rehbere][1] başvurun. + +[1]: https://developer.mozilla.org/en/JavaScript/Guide + diff --git a/doc/tr/intro/license.md b/doc/tr/intro/license.md new file mode 100644 index 00000000..a0484d74 --- /dev/null +++ b/doc/tr/intro/license.md @@ -0,0 +1,13 @@ +## Lisans + +JavaScript Garden [MIT lisansı][1] altında yayınlanmıştır ve [GitHub][2] +üzerinde bulunmaktadır. Eğer rehberde yanlışlıklar veya yazım hatalarına +rastlarsanız lütfen [sorunu bildirin][3] veya bir `pull request` gönderin. +Bizi ayrıca Stack Overflow'da [JavaScript sohbet kanalında][4] da +bulabilirsiniz. + +[1]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE +[2]: https://github.com/BonsaiDen/JavaScript-Garden +[3]: https://github.com/BonsaiDen/JavaScript-Garden/issues +[4]: http://chat.stackoverflow.com/rooms/17/javascript + From 2fce77cbe52e63b87fae49773b42ed69f5bcbc20 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Fri, 8 Apr 2011 05:04:39 -0700 Subject: [PATCH 144/641] few wording changes --- doc/tr/array/constructor.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/tr/array/constructor.md b/doc/tr/array/constructor.md index b007c23b..e613a738 100644 --- a/doc/tr/array/constructor.md +++ b/doc/tr/array/constructor.md @@ -1,8 +1,8 @@ ## `Dizi` Oluşturucusu `Dizi` oluşturucusunun parametrelerini nasıl değerlendirdiği belirsiz olduğu -için, yeni diziler oluşturulurken her zaman dizi değişmezlerinin - `[]` -notasyonu - kullanılması tavsiye olunur. +için, yeni diziler oluşturulurken her zaman dizi değişmezlerinin (`[]` +notasyonu) kullanılması tavsiye olunur. [1, 2, 3]; // Sonuç: [1, 2, 3] new Array(1, 2, 3); // Sonuç: [1, 2, 3] @@ -30,6 +30,6 @@ katarını tekrarlamaktır. `Dizi` oluşturucusunun kullanılmasından mümkün olduğu kadar kaçınılmalıdır. Bunun yerine her zaman dizi değişmezleri tercih edilmelidir. Hem daha kısadırlar -hem de daha net bir sentaksa sahiptirler; bu nedenle programın okunabilirliğini +hem de daha anlaşılır bir sentaksa sahiptirler; bu nedenle programın okunabilirliğini de artırırlar. From 15e3f47f75f725d9dbcb91b0347696e4fe8403ae Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Fri, 8 Apr 2011 17:24:20 +0300 Subject: [PATCH 145/641] translated function/closures --- doc/tr/function/closures.md | 100 ++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 doc/tr/function/closures.md diff --git a/doc/tr/function/closures.md b/doc/tr/function/closures.md new file mode 100644 index 00000000..84cfa4d0 --- /dev/null +++ b/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) + } + From e3f2422923f59fff60347c20869393255f750f68 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Fri, 8 Apr 2011 18:08:00 +0300 Subject: [PATCH 146/641] translated function/constructors --- doc/tr/function/constructors.md | 124 ++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 doc/tr/function/constructors.md diff --git a/doc/tr/function/constructors.md b/doc/tr/function/constructors.md new file mode 100644 index 00000000..1b330966 --- /dev/null +++ b/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 +JavaeScript'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 kullanamamak arasında +hiçbir bir fark yoktur. + +### Fabrikalar ile yeni nesneler oluşturmak + +`new` anahtar kelimesinin **kullanılmaması** tavsiye olunur, çü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**. + From 7dd496dee546fcea8fce43f462d39fe8e47ffccf Mon Sep 17 00:00:00 2001 From: Maxim Kazantsev Date: Tue, 12 Apr 2011 03:04:56 +0700 Subject: [PATCH 147/641] =?UTF-8?q?Minor=20grammar=20fixes=20(=D1=82=D0=B0?= =?UTF-8?q?=D0=BA=20=D0=B6=D0=B5=20->=20=D1=82=D0=B0=D0=BA=D0=B6=D0=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/ru/core/semicolon.md | 2 +- doc/ru/function/constructors.md | 2 +- doc/ru/function/this.md | 4 ++-- doc/ru/types/equality.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/ru/core/semicolon.md b/doc/ru/core/semicolon.md index 0bcb7f01..18c5749b 100644 --- a/doc/ru/core/semicolon.md +++ b/doc/ru/core/semicolon.md @@ -97,5 +97,5 @@ ### Заключение -Настоятельно рекомендуем **никогда** не забывать ставить точку с запятой; так же рекомендуется оставлять скобки на одной строке с соответствующим оператором и никогда не опускать их для выражений с использованием `if` / `else`. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят от изменения поведения кода, произведённого парсером втихую. +Настоятельно рекомендуем **никогда** не забывать ставить точку с запятой; также рекомендуется оставлять скобки на одной строке с соответствующим оператором и никогда не опускать их для выражений с использованием `if` / `else`. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят от изменения поведения кода, произведённого парсером втихую. diff --git a/doc/ru/function/constructors.md b/doc/ru/function/constructors.md index 4d60226c..20b09d4f 100644 --- a/doc/ru/function/constructors.md +++ b/doc/ru/function/constructors.md @@ -64,7 +64,7 @@ В обоих случаях при вызове `Bar` мы получим один и тот же результат — новый объект со свойством `method` (спасибо [замыканию](#function.closures) за это). -Так же следует заметить, что вызов `new Bar()` никак **не** связан с прототипом возвращаемого объекта. Хоть прототип и назначается всем новосозданным объектам, но `Bar` никогда не возвращает этот новый объект. +Также следует заметить, что вызов `new Bar()` никак **не** связан с прототипом возвращаемого объекта. Хоть прототип и назначается всем новосозданным объектам, но `Bar` никогда не возвращает этот новый объект. В предыдущем примере нет функциональных отличий между вызовом конструктора с оператором `new` или без него. diff --git a/doc/ru/function/this.md b/doc/ru/function/this.md index b01f76b0..9fd6390c 100644 --- a/doc/ru/function/this.md +++ b/doc/ru/function/this.md @@ -12,7 +12,7 @@ foo(); -Тут `this` так же ссылается на *глобальный* объект. +Тут `this` также ссылается на *глобальный* объект. > **ES5 Замечание:** В strict-режиме **теряется** понятие глобальности, поэтому в этом случае `this` будет иметь значение `undefined`. @@ -67,7 +67,7 @@ Подходящее имя для переменной - `that`, его часто используют для ссылки на внешний `this`. В комбинации с [замыканиями](#function.closures) `this` можно пробрасывать в глобальную область, или в любой другой объект. -> **Замечание** от перев. Кроме `that` так же часто встречаются `this_`, `self_` и другие варианты, но лучше принять для себя `that` как стандарт и тогда, возможно, все вокруг тоже будут им пользоваться. +> **Замечание** от перев. Кроме `that` также часто встречаются `this_`, `self_` и другие варианты, но лучше принять для себя `that` как стандарт и тогда, возможно, все вокруг тоже будут им пользоваться. ### Назначение методов diff --git a/doc/ru/types/equality.md b/doc/ru/types/equality.md index 25cbae31..03c916ce 100644 --- a/doc/ru/types/equality.md +++ b/doc/ru/types/equality.md @@ -50,7 +50,7 @@ JavaScript имеет 2 различных способа сравнения з var foo = {}; foo === foo; // true -Здесь оба операнда сравниваются на **идентичность**, а **не** равенство; то есть, будет проверяться, являются ли операнды одним **экземпляром** объекта, так же как делает `is` в Python и сравниваются указатели в С. +Здесь оба операнда сравниваются на **идентичность**, а **не** на равенство; то есть, будет проверяться, являются ли операнды одним **экземпляром** объекта, так же как делает `is` в Python и сравниваются указатели в С. ### Заключение From 8f83c9a9f39d08f214819199ba50aa6ce93e973e Mon Sep 17 00:00:00 2001 From: Maxim Kazantsev Date: Tue, 12 Apr 2011 03:11:45 +0700 Subject: [PATCH 148/641] =?UTF-8?q?Minor=20grammar=20fixes=20(=D1=82=D0=B0?= =?UTF-8?q?=D0=BA=D0=B6=D0=B5=20->=20=D1=82=D0=B0=D0=BA=20=D0=B6=D0=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/ru/core/undefined.md | 2 +- doc/ru/function/closures.md | 2 +- doc/ru/object/general.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ru/core/undefined.md b/doc/ru/core/undefined.md index dd1d16b2..6b490bb5 100644 --- a/doc/ru/core/undefined.md +++ b/doc/ru/core/undefined.md @@ -8,7 +8,7 @@ Кроме этого, в языке определена глобальная переменная со значением `undefined`, и эта переменная так и называется — `undefined`. Не являясь константой, она не является и ключевым словом. Из этого следует, что её значение можно с лёгкостью переопределить. -> **ES5 Замечание:** в ECMAScript 5 переменная `undefined` **больше не** *доступна на запись* в strict-режиме, однако она всё также может быть перегружена по имени, например - функцией с именем `undefined`. +> **ES5 Замечание:** в ECMAScript 5 переменная `undefined` **больше не** *доступна на запись* в strict-режиме, однако она всё так же может быть перегружена по имени, например - функцией с именем `undefined`. Несколько случаев, когда возвращается `undefined`: diff --git a/doc/ru/function/closures.md b/doc/ru/function/closures.md index 82835fcb..a6e62997 100644 --- a/doc/ru/function/closures.md +++ b/doc/ru/function/closures.md @@ -76,5 +76,5 @@ })(i), 1000) } -> **Замечание** от перев. Переменную `e` можно тоже назвать `i`, если вы хотите: это не поменяет поведения кода — внутренняя переменная `i` всё также будет *копией* внешней переменной +> **Замечание** от перев. Переменную `e` можно тоже назвать `i`, если вы хотите: это не поменяет поведения кода — внутренняя переменная `i` всё так же будет *копией* внешней переменной diff --git a/doc/ru/object/general.md b/doc/ru/object/general.md index 804bf1e3..d5b65e7a 100644 --- a/doc/ru/object/general.md +++ b/doc/ru/object/general.md @@ -50,7 +50,7 @@ Единственный способ удалить свойство у объекта — использовать оператор `delete`; устанавливая свойство в `undefined` или `null`, вы только заменяете связанное с ним *значение*, но не удаляете *ключ*. -> **Замечание** от перев.: Если ссылок на значение больше нет, то сборщиком мусора удаляется и само значение, но ключ объекта при этом всё также имеет новое значение. +> **Замечание** от перев.: Если ссылок на значение больше нет, то сборщиком мусора удаляется и само значение, но ключ объекта при этом всё так же имеет новое значение. var obj = { bar: 1, From 800ff5cb2ef4916709e6e4e57ccbea7d730cb827 Mon Sep 17 00:00:00 2001 From: Maxim Kazantsev Date: Tue, 12 Apr 2011 03:13:39 +0700 Subject: [PATCH 149/641] Minor grammar fixes --- doc/ru/appendix/fromtranslators.md | 2 +- doc/ru/core/undefined.md | 2 +- doc/ru/function/general.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ru/appendix/fromtranslators.md b/doc/ru/appendix/fromtranslators.md index 4e69ca5f..37372aa8 100644 --- a/doc/ru/appendix/fromtranslators.md +++ b/doc/ru/appendix/fromtranslators.md @@ -10,5 +10,5 @@ Относитесь с **мудростью** к тому, что вы пишете — *важно* знать как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. *Важно* знать как работает [прототипное наследование](#object.prototype), но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код — ну, если у вас есть талант. -Гонка за оптимизацией — это драматично и правильно, но лучше написать работающий и понятный вам код, а потом уже его оптимизировать и искать узкие места, при необходимости. Оптимизацию необходимо делать, если вы видите явные неудобства для пользователя в тех или иных браузерах или у вас есть супер-крупный проект, которым никогда мешает оптимизация, или вы работаете с какой-либо сверхтребовательной технологией типа WebGL. Данная документация очень поможет вам в определении этих узких мест. +Гонка за оптимизацией — это драматично и правильно, но лучше написать работающий и понятный вам код, а потом уже его оптимизировать и искать узкие места, при необходимости. Оптимизацию необходимо делать, если вы видите явные неудобства для пользователя в тех или иных браузерах или у вас есть супер-крупный проект, которым никогда не мешает оптимизация, или вы работаете с какой-либо сверхтребовательной технологией типа WebGL. Данная документация очень поможет вам в определении этих узких мест. diff --git a/doc/ru/core/undefined.md b/doc/ru/core/undefined.md index 6b490bb5..326f397b 100644 --- a/doc/ru/core/undefined.md +++ b/doc/ru/core/undefined.md @@ -13,7 +13,7 @@ Несколько случаев, когда возвращается `undefined`: - При попытке доступа к глобальной переменной `undefined` (если она не изменена). - - Неявный возврат из функции при отсутствия в ней оператора `return`. + - Неявный возврат из функции при отсутствии в ней оператора `return`. - Из операторов `return`, которые ничего не возвращают. - В результате поиска несуществующего свойства у объекта (и доступа к нему). - Параметры, которые не были переданы в функцию явно. diff --git a/doc/ru/function/general.md b/doc/ru/function/general.md index f8012d1a..c4a47e64 100644 --- a/doc/ru/function/general.md +++ b/doc/ru/function/general.md @@ -1,6 +1,6 @@ ## Выражения и объявление функций -Функции в JavaScript тоже являются объектами (шок, сенсация) — следовательно, их можно передавать и присваивать точно так же, как и любой другой объект. Одним из вариантов использования такой возможности является передача *анонимной функции* как функции обратного вызова в другую функцию — к примеру, для ассинхронных вызовов. +Функции в JavaScript тоже являются объектами (шок, сенсация) — следовательно, их можно передавать и присваивать точно так же, как и любой другой объект. Одним из вариантов использования такой возможности является передача *анонимной функции* как функции обратного вызова в другую функцию — к примеру, для асинхронных вызовов. ### Объявление `function` From 237a18e74e0d086a79a75c055c8721f663299832 Mon Sep 17 00:00:00 2001 From: Maxim Kazantsev Date: Tue, 12 Apr 2011 03:16:44 +0700 Subject: [PATCH 150/641] Minor grammar and stylistic fixes --- doc/ru/other/timeouts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ru/other/timeouts.md b/doc/ru/other/timeouts.md index 4453493d..7e2a6647 100644 --- a/doc/ru/other/timeouts.md +++ b/doc/ru/other/timeouts.md @@ -9,7 +9,7 @@ Функция `setTimeout` возвращает идентификатор таймаута и планирует вызвать `foo` через, **примерно**, тысячу миллисекунд. Фунция `foo` при этом будет вызвана ровно **один** раз. -В зависимости от разрешения таймера в используемом для запуска кода движке JavaScript, а также с учётом факта, что JavaScript является однопоточным языком и посторонний код может заблокировать выполнение потока, нет **никакой** гарантии, что переданный код будет выполнен ровно через указанное в вызове `setTimeout` время. +В зависимости от разрешения таймера в используемом для запуска кода движке JavaScript, а также с учётом того, что JavaScript является однопоточным языком и посторонний код может заблокировать выполнение потока, нет **никакой** гарантии, что переданный код будет выполнен ровно через указанное в вызове `setTimeout` время. Переданная первым параметром функция будет вызвана как *глобальный объект* — это значит, что оператор [`this`](#function.this) в вызываемой функции будет ссылаться на этот самый объект. @@ -75,7 +75,7 @@ `setTimeout` и `setInterval` могут принимать строку в качестве первого параметра. Эту возможность не следует использовать **никогда**, поскольку изнутри при этом производится скрытый вызов `eval`. -> **Замечание**: Поскольку функции работы с таймаутами **не** определены в стандарте ECMAScript, точная внутренняя механика их работы может различаться от движка к движку. Известен факт, то Microsoft JScript использует конструктор `Function` вместо `eval`. +> **Замечание**: Поскольку функции работы с таймаутами **не** определены в стандарте ECMAScript, точная внутренняя механика их работы может различаться от движка к движку. Известно, что Microsoft JScript использует конструктор `Function` вместо `eval`. function foo() { // будет вызвана From 57df1d87ad52529f8e501d2e2b9b4e44b3beca65 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Tue, 12 Apr 2011 17:01:20 +0300 Subject: [PATCH 151/641] Translated function/general. --- doc/tr/function/general.md | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 doc/tr/function/general.md diff --git a/doc/tr/function/general.md b/doc/tr/function/general.md new file mode 100644 index 00000000..8c6967d9 --- /dev/null +++ b/doc/tr/function/general.md @@ -0,0 +1,49 @@ +## Fonksiyon Tanımlası 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ı alı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 ataması program çalışırken gerçekleşeceği için, ilgili satır +çalıştığında, `foo` değişkeninin değeri varsayılan olarak +[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. + From 94b16abdddb26ca8f03a7e27a466d94270f3cd2d Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Tue, 12 Apr 2011 18:05:40 +0300 Subject: [PATCH 152/641] Translated function/scopes. --- doc/tr/function/general.md | 2 +- doc/tr/function/scopes.md | 232 +++++++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 doc/tr/function/scopes.md diff --git a/doc/tr/function/general.md b/doc/tr/function/general.md index 8c6967d9..4c60a166 100644 --- a/doc/tr/function/general.md +++ b/doc/tr/function/general.md @@ -10,7 +10,7 @@ geçirmekte kullanılır. function foo() {} Yukarıdaki fonksiyon tanımlaması program çalışmadan önce -[yukarı alınır](#function.scopes) ve böylece *tanımlandığı* kapsam içinde +[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 diff --git a/doc/tr/function/scopes.md b/doc/tr/function/scopes.md new file mode 100644 index 00000000..229fa9fe --- /dev/null +++ b/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 +> edilmesizor hatalara neden olabilir. + +JavaScript'te isim uzayları kavramı da bulunmaz, tanımlanan herşey tek bir +*genel olarak paylaşılmış* 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 olacaktor. + + // 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 olarak 'undefined' değerini alırlar + + // 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 + +İsim siz 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 vadı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 olunur. 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. + From 6ea878442e41c19335b498eb3f64e1c20b0a34c2 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Wed, 13 Apr 2011 12:31:15 +0300 Subject: [PATCH 153/641] Translated function/this. --- doc/tr/function/this.md | 116 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 doc/tr/function/this.md diff --git a/doc/tr/function/this.md b/doc/tr/function/this.md new file mode 100644 index 00000000..bdf97641 --- /dev/null +++ b/doc/tr/function/this.md @@ -0,0 +1,116 @@ +## `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. +Here `this` will again refer to the *global* object. + +### 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` değişmezi 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. + + From cb3caaadb44eae36b1f09a8967eb4b5d3f7e32a3 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Wed, 13 Apr 2011 13:27:02 +0300 Subject: [PATCH 154/641] Translated object/forinloop. --- doc/tr/object/forinloop.md | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 doc/tr/object/forinloop.md diff --git a/doc/tr/object/forinloop.md b/doc/tr/object/forinloop.md new file mode 100644 index 00000000..0965202c --- /dev/null +++ b/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` kullarak 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.protype` 'ı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/ + From fd4c53208b6117f82d134a6e7c0d6c170796621d Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Wed, 13 Apr 2011 13:51:41 +0300 Subject: [PATCH 155/641] Translated object/general. --- doc/tr/object/general.md | 102 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 doc/tr/object/general.md diff --git a/doc/tr/object/general.md b/doc/tr/object/general.md new file mode 100644 index 00000000..f9457629 --- /dev/null +++ b/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 solundki 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 değişmezi (`{}` 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ışi olacak ve çıktıda görünmeyecektir. + +### Anahtar notasyonu + + var test = { + 'case': 'bir anahtar kelime olduğu için katar notasyonu ile girildi', + delete: 'aynı şekilde katar notasyonu gerekiyordu' // SyntaxError hatası verir + }; + +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ına +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 + From 89866a9683956d679da3d87d661b35f281166954 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Wed, 13 Apr 2011 14:11:22 +0300 Subject: [PATCH 156/641] Translated object/hasownproperty. --- doc/tr/object/hasownproperty.md | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 doc/tr/object/hasownproperty.md diff --git a/doc/tr/object/hasownproperty.md b/doc/tr/object/hasownproperty.md new file mode 100644 index 00000000..f0fe0e0a --- /dev/null +++ b/doc/tr/object/hasownproperty.md @@ -0,0 +1,55 @@ +## `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 haricen kullanıldığında 'this' foo olarak atanır + {}.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. + From 06c4e33c7706f2fc3eb77f3ef7742b83902fe91f Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Wed, 13 Apr 2011 14:55:50 +0300 Subject: [PATCH 157/641] Translated object/prototype. --- doc/tr/object/prototype.md | 116 +++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 doc/tr/object/prototype.md diff --git a/doc/tr/object/prototype.md b/doc/tr/object/prototype.md new file mode 100644 index 00000000..3a4d6bcd --- /dev/null +++ b/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' } + 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 zincirinden 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.protype`) 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.protype` 'ı 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 + From 4f347b07f48194212f70177acd63e4ba43b6dac1 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Wed, 13 Apr 2011 14:59:52 +0300 Subject: [PATCH 158/641] Fixed typo. --- doc/tr/object/forinloop.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tr/object/forinloop.md b/doc/tr/object/forinloop.md index 0965202c..be20e072 100644 --- a/doc/tr/object/forinloop.md +++ b/doc/tr/object/forinloop.md @@ -21,7 +21,7 @@ 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` kullarak filtrelemek +### `hasOwnProperty` kullanarak filtrelemek // yukarıdaki örnekteki foo nesnesi for(var i in foo) { From bd3b4d1c858d3f5d7ae95fb417ed4044ff42cf0d Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Wed, 13 Apr 2011 15:59:50 +0300 Subject: [PATCH 159/641] Translated other/timeouts. --- doc/tr/other/timeouts.md | 161 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 doc/tr/other/timeouts.md diff --git a/doc/tr/other/timeouts.md b/doc/tr/other/timeouts.md new file mode 100644 index 00000000..e603a5a7 --- /dev/null +++ b/doc/tr/other/timeouts.md @@ -0,0 +1,161 @@ +### `setTimeout` ve `setInterval` + +JavaScript asenkron olduğu için bir `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ılmatı 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 zamanlatı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 metod 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 +olunur. + +### `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. Gerçekten de, 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(a, b, c); + }, 1000) + +> **Not:** `setTimeout(foo, 1000, a, b, c)` 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 + From a2658a42da4b4a99934334eacf2a250f1b4a9cea Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Wed, 13 Apr 2011 18:08:01 +0300 Subject: [PATCH 160/641] Translated types/casting. --- doc/tr/types/casting.md | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 doc/tr/types/casting.md diff --git a/doc/tr/types/casting.md b/doc/tr/types/casting.md new file mode 100644 index 00000000..798b8768 --- /dev/null +++ b/doc/tr/types/casting.md @@ -0,0 +1,72 @@ +## Tür Dönüşümleri + +JavaScript *weakly typed* bir dildir, bu yüzden **mümkün olan yerlerde** +*tür 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, [mutlak 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 türlerin nesne oluşturucuları + +`Number` ve `String` gibi temel türlerin 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, tür dönüşümü nedeniyle + +`Number` gibi bir temel türün 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üşümleri de söz konusu olacaktır. + +En iyi seçenek üç olası türden 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 türüne dönüştürmek + +**not** operatörü iki kez üst üste kullanılarak bir değer mantıksal değişken +türüne dönüştürülebilir. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + + From 8ef8ae994a7dcc3130c395a380de7214239f4358 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Thu, 14 Apr 2011 16:53:35 +0300 Subject: [PATCH 161/641] =?UTF-8?q?Changed=20wording:=20t=C3=BCr=20->=20ti?= =?UTF-8?q?p?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/tr/types/casting.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/tr/types/casting.md b/doc/tr/types/casting.md index 798b8768..2ffe0c39 100644 --- a/doc/tr/types/casting.md +++ b/doc/tr/types/casting.md @@ -1,7 +1,7 @@ -## Tür Dönüşümleri +## Tip Dönüşümleri JavaScript *weakly typed* bir dildir, bu yüzden **mümkün olan yerlerde** -*tür dönüşümü* uygular. +*tip dönüşümü* uygular. // Bunlar true verir new Number(10) == 10; // Number.toString() tekrar sayıya @@ -26,23 +26,23 @@ kullanılması **şiddetle** tavsiye edilir. Böylece yaygın hataların çoğun kaçınılabilir, yine de JavaScript'in *weak typing* sisteminden kaynaklanan başka sorunlar da vadır. -### Temel türlerin nesne oluşturucuları +### Temel tiplerin nesne oluşturucuları -`Number` ve `String` gibi temel türlerin nesne oluşturucuları `new` anahtar +`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, tür dönüşümü nedeniyle + new Number(10) + 0 === 10; // True, tip dönüşümü nedeniyle -`Number` gibi bir temel türün nesne oluşturucusunu kullanmak yeni bir `Number` +`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üşümleri de söz konusu olacaktır. +dönüşümler de söz konusu olacaktır. -En iyi seçenek üç olası türden birine **açıkça** dönüşüm yapılmasıdı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 @@ -56,10 +56,10 @@ Bir değerin başına boş bir katar eklenerek kolayca katara dönüştürülebi **Tek terimli** toplama operatörü kullanılarak bir değer sayıya dönüştürülebilir. -### Mantıksal değişken türüne dönüştürmek +### Mantıksal değişken tipine dönüştürmek **not** operatörü iki kez üst üste kullanılarak bir değer mantıksal değişken -türüne dönüştürülebilir. +tipine dönüştürülebilir. !!'foo'; // true !!''; // false From f3e5637deea3e51540f374c7c4bb55e415434e7f Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Thu, 14 Apr 2011 16:53:50 +0300 Subject: [PATCH 162/641] Translated types/equality. --- doc/tr/types/equality.md | 75 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 doc/tr/types/equality.md diff --git a/doc/tr/types/equality.md b/doc/tr/types/equality.md new file mode 100644 index 00000000..a0d5d296 --- /dev/null +++ b/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şittir operatörü + +Eşittir operatörü iki adet eşittir işaretinden oluşur: `==` + +JavaScript *weakly typed* bir dildir, bu nedenle, eşittir 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. + From 0acc4472f4b69ac916d67d2361c1a02ce85da340 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Thu, 14 Apr 2011 17:05:03 +0300 Subject: [PATCH 163/641] =?UTF-8?q?T=C3=BCr=20->=20Tip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/tr/types/casting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tr/types/casting.md b/doc/tr/types/casting.md index 2ffe0c39..ce7c240f 100644 --- a/doc/tr/types/casting.md +++ b/doc/tr/types/casting.md @@ -21,7 +21,7 @@ JavaScript *weakly typed* bir dildir, bu yüzden **mümkün olan yerlerde** > değerlendirilir. Oktal sayı desteği ECMAScript 5 mutlak modda > **kaldırılmıştır**. -Yukarıdakilerden kaçınmak için, [mutlak eşitlik operatörünün](#types.equality) +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. From 10076c3ce5036923feee265666cec2876f4b15a2 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Thu, 14 Apr 2011 17:05:14 +0300 Subject: [PATCH 164/641] Translated types/instanceof. --- doc/tr/types/instanceof.md | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 doc/tr/types/instanceof.md diff --git a/doc/tr/types/instanceof.md b/doc/tr/types/instanceof.md new file mode 100644 index 00000000..4dea0b25 --- /dev/null +++ b/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ıpkp [`typeof`](#types.typeof) operatörü gibi, +bunun dışındaki tüm kullanımlarından **kaçınılmalıdır**. + From 835694625d91d8b5a16edfbac72f97f668f38e03 Mon Sep 17 00:00:00 2001 From: Ozgur Ozcitak Date: Thu, 14 Apr 2011 17:30:20 +0300 Subject: [PATCH 165/641] Translated types/typeof. --- doc/tr/types/typeof.md | 87 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 doc/tr/types/typeof.md diff --git a/doc/tr/types/typeof.md b/doc/tr/types/typeof.md new file mode 100644 index 00000000..110c863d --- /dev/null +++ b/doc/tr/types/typeof.md @@ -0,0 +1,87 @@ +## `typeof` operatörü + +The `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)`), 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öre 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ı spesifikasdyonda 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. + From d3a31b89e785ce11685a0b857500babe2f153371 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Thu, 14 Apr 2011 21:51:20 +0300 Subject: [PATCH 166/641] Done proof-reading. --- doc/tr/array/constructor.md | 18 +++++++++--------- doc/tr/array/general.md | 6 +++--- doc/tr/core/eval.md | 2 +- doc/tr/core/semicolon.md | 8 ++++---- doc/tr/core/undefined.md | 2 +- doc/tr/function/arguments.md | 2 +- doc/tr/function/closures.md | 2 +- doc/tr/function/constructors.md | 10 +++++----- doc/tr/function/general.md | 4 ++-- doc/tr/function/scopes.md | 14 +++++++------- doc/tr/function/this.md | 5 ++--- doc/tr/intro/authors.md | 4 ++-- doc/tr/intro/contributors.md | 2 +- doc/tr/intro/index.md | 19 ++++++++++--------- doc/tr/object/general.md | 12 ++++++------ doc/tr/object/hasownproperty.md | 2 +- doc/tr/object/prototype.md | 6 +++--- doc/tr/other/timeouts.md | 10 +++++----- doc/tr/types/casting.md | 4 ++-- doc/tr/types/instanceof.md | 4 ++-- doc/tr/types/typeof.md | 10 +++++----- 21 files changed, 73 insertions(+), 73 deletions(-) diff --git a/doc/tr/array/constructor.md b/doc/tr/array/constructor.md index e613a738..8e16990f 100644 --- a/doc/tr/array/constructor.md +++ b/doc/tr/array/constructor.md @@ -1,7 +1,7 @@ -## `Dizi` Oluşturucusu +## `Array` Oluşturucusu -`Dizi` oluşturucusunun parametrelerini nasıl değerlendirdiği belirsiz olduğu -için, yeni diziler oluşturulurken her zaman dizi değişmezlerinin (`[]` +`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] @@ -11,14 +11,14 @@ notasyonu) kullanılması tavsiye olunur. new Array(3); // Sonuç: [] new Array('3') // Sonuç: ['3'] -`Dizi` oluşturucusuna tek bir argüman verildiğinde, ve bu argümanın türü +`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 ilklenmedi + 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 @@ -28,8 +28,8 @@ katarını tekrarlamaktır. ### Sonuç -`Dizi` oluşturucusunun kullanılmasından mümkün olduğu kadar kaçınılmalıdır. -Bunun yerine her zaman dizi değişmezleri 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. +`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/doc/tr/array/general.md b/doc/tr/array/general.md index 5b6767d2..6925a410 100644 --- a/doc/tr/array/general.md +++ b/doc/tr/array/general.md @@ -10,15 +10,15 @@ vardır. > Ve *associative* diziler eleman sıralamasını **korurlar** ama, nesneler > **korumazlar**. -`for in` döngüsü prototip zincirindeki tüm özellikleri saydığı için ve bunu +`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 için en iyi yol klasik `for` -döngüsünü kullanmaktır. +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++) { diff --git a/doc/tr/core/eval.md b/doc/tr/core/eval.md index 74b444e9..1fa6416a 100644 --- a/doc/tr/core/eval.md +++ b/doc/tr/core/eval.md @@ -1,4 +1,4 @@ -## Neden `eval` kullanılmamalı +## Neden `eval` Kullanılmamalı `eval` fonksiyonu bir JavaScript kodunu lokal kapsamda yürütür. diff --git a/doc/tr/core/semicolon.md b/doc/tr/core/semicolon.md index 9ba7efd7..89f5e3a4 100644 --- a/doc/tr/core/semicolon.md +++ b/doc/tr/core/semicolon.md @@ -1,7 +1,7 @@ -## Otomatik noktalı virgül ilavesi +## Otomatik Noktalı Virgül İlavesi JavaScript sentaksı C'ye benzese de, noktalı virgül kullanılması -zurunlu **değildir**. +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 @@ -85,7 +85,7 @@ eklenmesi gerektiğini gramer çözümleyicinin karar vermesi gerekiyor. })(window); //<- eklendi -> **Not:** JavaScript çözümleyicisi `return` ifadesinden heme sonra satır sonu +> **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. @@ -110,7 +110,7 @@ bu nedenle, yukarıdaki satır `undefined is not a function` hata mesajı ile bi ### Sonuç -Noktalı virgüllerin **hiç bir zaman** ihmal edilmemesi tavsiye olunur, ayrıca +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 diff --git a/doc/tr/core/undefined.md b/doc/tr/core/undefined.md index dfaa24a2..6ad70c09 100644 --- a/doc/tr/core/undefined.md +++ b/doc/tr/core/undefined.md @@ -65,7 +65,7 @@ JavaScript dilinde `undefined` geleneksel *null* yerine kullanılmaktadır, ası `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çindeki kapalı olarak kullanılır (mesela prototip zincirinin +`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/doc/tr/function/arguments.md b/doc/tr/function/arguments.md index 1eb016ea..dbd129e9 100644 --- a/doc/tr/function/arguments.md +++ b/doc/tr/function/arguments.md @@ -1,4 +1,4 @@ -## `arguments` nesnesi +## `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 diff --git a/doc/tr/function/closures.md b/doc/tr/function/closures.md index 84cfa4d0..efd1feb2 100644 --- a/doc/tr/function/closures.md +++ b/doc/tr/function/closures.md @@ -1,6 +1,6 @@ ## Closure ve Referanslar -JavaScript'in en güçlü özelliklerinden biri de *closure* 'lara sahip olmasıdır. +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. diff --git a/doc/tr/function/constructors.md b/doc/tr/function/constructors.md index 1b330966..e4913abf 100644 --- a/doc/tr/function/constructors.md +++ b/doc/tr/function/constructors.md @@ -3,7 +3,7 @@ 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 +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. @@ -48,7 +48,7 @@ bu değer bir `Object` ise oluşturucu fonksiyon verilen değeri döndürür. Foo(); // undefined Yukarıdaki örnek bazı durumlarda doğru çalışıyor gibi görünebilir, ama -JavaeScript'te [`this`](#function.this) 'in çalışma şeklinden dolayı `this` +JavaScript'te [`this`](#function.this) 'in çalışma şeklinden dolayı `this` 'in değeri *global nesne* olacaktır. ### Nesne fabrikaları @@ -72,19 +72,19 @@ döndürmesi gerekir. 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ış +`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 kullanamamak arasında +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 olunur, çünkü unutulması +`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. diff --git a/doc/tr/function/general.md b/doc/tr/function/general.md index 4c60a166..e45949c1 100644 --- a/doc/tr/function/general.md +++ b/doc/tr/function/general.md @@ -1,4 +1,4 @@ -## Fonksiyon Tanımlası ve Fonksiyon İfadesi +## 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 @@ -11,7 +11,7 @@ geçirmekte kullanılır. 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. +*her yerde* (hatta tanımlanmadan önce bile) kullanılabilir. foo(); // foo bu satır çalışmadan önce oluşturuldu function foo() {} diff --git a/doc/tr/function/scopes.md b/doc/tr/function/scopes.md index 229fa9fe..7b9d4a24 100644 --- a/doc/tr/function/scopes.md +++ b/doc/tr/function/scopes.md @@ -16,8 +16,8 @@ kapsamı **bulunmaz**; bu nedenle, dilde sadece *fonksiyon kapsamı* mevcuttur. > [otomatik noktalı virgül ilavesi](#core.semicolon) ile birleştiğinde fark > edilmesizor hatalara neden olabilir. -JavaScript'te isim uzayları kavramı da bulunmaz, tanımlanan herşey tek bir -*genel olarak paylaşılmış* bir isim uzayının içindedir. +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 @@ -50,7 +50,7 @@ kelimesini kullanmamanın önemli sonuçları olabilir. `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 olacaktor. +hatalara neden olacaktır. // genel kapsam var items = [/* bir dizi */]; @@ -121,7 +121,7 @@ ifadelerini ve `function` bildirimlerini içinde bulundukları kapsamın en üst taşır. // var ifadeleri buraya taşınır - var bar, someValue; // varsayılan olarak 'undefined' değerini alırlar + var bar, someValue; // varsayılan değerleri 'undefined' olur // function bildirimi de yukarı taşınır function test(data) { @@ -204,7 +204,7 @@ Tek bir genel isim uzayının bulunmasının yol açtığı yaygın sonuç isim })(); // fonksiyonu hemen çalıştır -İsim siz fonksiyonlar [ifade](#function.general) olarak değerlendirilir; +İ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 @@ -212,7 +212,7 @@ bu nedenle çağrılabilmeleri için önce değerlendirilmeleri gerekir. ) // 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 vadır ve +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 @@ -222,7 +222,7 @@ yukarıdaki ile aynı sonucu verirler. ### Sonuç Programı kendi isim uzayı ile kapsamak için her zaman *isimsiz fonksiyonların* -kullanılması tavsiye olunur. Böylece hem isim çakışmalarından korunulmuş olunur, +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 diff --git a/doc/tr/function/this.md b/doc/tr/function/this.md index bdf97641..de24190a 100644 --- a/doc/tr/function/this.md +++ b/doc/tr/function/this.md @@ -19,7 +19,6 @@ 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. -Here `this` will again refer to the *global* object. ### Bir metod çağırma @@ -49,7 +48,7 @@ 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` değişmezi içinde nesnenin kendisine +> **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. @@ -83,7 +82,7 @@ değişken oluşturmaktır. } `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) +`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. diff --git a/doc/tr/intro/authors.md b/doc/tr/intro/authors.md index 0425d089..6e53d523 100644 --- a/doc/tr/intro/authors.md +++ b/doc/tr/intro/authors.md @@ -1,7 +1,7 @@ ## Yazarlar -Bu rehber, hoş birer Stack Overflow kullanıcısı olan [Ivo Wetzel][1] (Yazım) ve -[Zhang Yi Jiang][2] (Tasarım) tarafından hazırlanmıştır. +Bu rehber, sevimli birer Stack Overflow kullanıcısı olan [Ivo Wetzel][1] (Yazım) +ve [Zhang Yi Jiang][2] (Tasarım) tarafından hazırlanmıştır. [1]: http://stackoverflow.com/users/170224/ivo-wetzel [2]: http://stackoverflow.com/users/313758/yi-jiang diff --git a/doc/tr/intro/contributors.md b/doc/tr/intro/contributors.md index c8d1a96a..ba517abc 100644 --- a/doc/tr/intro/contributors.md +++ b/doc/tr/intro/contributors.md @@ -1,4 +1,4 @@ -## Katkıda bulunanlar +## Katkıda Bulunanlar - [Caio Romão][1] (Yazım düzeltmeleri) - [Andreas Blixt][2] (Dil düzeltmeleri) diff --git a/doc/tr/intro/index.md b/doc/tr/intro/index.md index 6d07b21e..303ef891 100644 --- a/doc/tr/intro/index.md +++ b/doc/tr/intro/index.md @@ -1,14 +1,15 @@ ## Giriş -**JavaScript Garden** JavaScript programlama dilinin acayiplik üzerine derlenmiş -dökümanlardır. Henüz ustalaşmamış JavaScript programcılarının sıkça yaptığı -yanlışlar, dile has ince hatalar ve performans sorunlarına karşı tavsiyeler -içerir. - -JavaScript Garden'ın amacı JavaScript öğretmek **değildir**. Bu rehberde -anlatılan konuları anlamak için JavaScript dilini önceden biliyor olmak şiddetle -tavsiye olunur. Eğer JavaScript dilinin temellerini öğrenmek istiyorsanız, -lütfen Mozilla Programcı Ağı'nda bulunan mükemmel [rehbere][1] başvurun. +**JavaScript Garden** JavaScript programlama dilinin acayiplikleri üzerine +derlenmiş bir döküman koleksiyonudur. Henüz ustalaşmamış JavaScript +programcılarının sıkça yaptığı yanlışlar, dile has ince hatalar ve performans +sorunlarına karşı tavsiyeler içerir. + +JavaScript Garden'ın amacı size JavaScript öğretmek **değildir**. Bu rehberde +anlatılan konuları anlamak için JavaScript dilini önceden biliyor olmanız +şiddetle tavsiye edilir. Eğer JavaScript dilinin temellerini öğrenmek +istiyorsanız, lütfen Mozilla Programcı Ağı'nda bulunan mükemmel [rehbere][1] +başvurun. [1]: https://developer.mozilla.org/en/JavaScript/Guide diff --git a/doc/tr/object/general.md b/doc/tr/object/general.md index f9457629..b112a582 100644 --- a/doc/tr/object/general.md +++ b/doc/tr/object/general.md @@ -30,7 +30,7 @@ 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 değişmezi (`{}` notasyonu) ile düz bir nesne yaratmak mümkündür. Bu yeni +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. @@ -80,18 +80,18 @@ kullanmaktır; özelliğe `undefined` veya `null` değerlerini atamak **sadece** } Yukarıdaki örnek sonuç olarak hem `bar undefined` hem de `foo null` yazacaktır. -Sadece `baz` özelliği kaldırılmışi olacak ve çıktıda görünmeyecektir. +Sadece `baz` özelliği kaldırılmış olacak ve çıktıda görünmeyecektir. ### Anahtar notasyonu var test = { - 'case': 'bir anahtar kelime olduğu için katar notasyonu ile girildi', - delete: 'aynı şekilde katar notasyonu gerekiyordu' // SyntaxError hatası verir + '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ına +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 diff --git a/doc/tr/object/hasownproperty.md b/doc/tr/object/hasownproperty.md index f0fe0e0a..e5eb5df0 100644 --- a/doc/tr/object/hasownproperty.md +++ b/doc/tr/object/hasownproperty.md @@ -42,7 +42,7 @@ doğru sonuç alabilmek için `hasOwnProperty `*haricen* kullanılmalıdır. foo.hasOwnProperty('bar'); // her zaman false verir - // hasOwnProperty haricen kullanıldığında 'this' foo olarak atanır + // hasOwnProperty haricen kullanıldığında 'this' foo olur {}.hasOwnProperty.call(foo, 'bar'); // true ### Sonuç diff --git a/doc/tr/object/prototype.md b/doc/tr/object/prototype.md index 3a4d6bcd..a158176a 100644 --- a/doc/tr/object/prototype.md +++ b/doc/tr/object/prototype.md @@ -56,7 +56,7 @@ nesneyi kullanmasıdır; bu nedenle, tüm `Bar` nesneleri **aynı** `value` > **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 zincirinden bulunmayacaktır. +> gidecektir; ve bu yüzden, `method` prototip zincirinde bulunmayacaktır. ### Özelliklere bulmak @@ -64,7 +64,7 @@ Bir nesnenin özelliklerine erişildiğinde, JavaScript, istenen isimdeki özell bulana kadar prototip zincirinde **yukarı** doğru dolaşır. Zincirin en üstüne ulaştığında (yani `Object.protype`) ve hala istenen özelliği -bulamamışsa sonuç olarak [undefined](#core.undefined) verecektir. +bulamamışsa sonuç olarak [`undefined`](#core.undefined) verecektir. ### prototype özelliği @@ -93,7 +93,7 @@ yapıldığında da prototip zinciri üzerindeki **tüm** özelliklere bakılaca Sıklıkla yapılan bir hata `Object.protype` 'ı veya diğer baz prototipleri genişletmektir. -Bu tekniğe [monkey patching][1] denir ve *kapsüllemeyi* bozar. Bu teknik +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. diff --git a/doc/tr/other/timeouts.md b/doc/tr/other/timeouts.md index e603a5a7..594ba83c 100644 --- a/doc/tr/other/timeouts.md +++ b/doc/tr/other/timeouts.md @@ -1,6 +1,6 @@ ### `setTimeout` ve `setInterval` -JavaScript asenkron olduğu için bir `setTimeout` ve `setInterval` kullanarak bir +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**, @@ -61,7 +61,7 @@ Yukarıdaki örnekte `foo` fonksiyonu bir kez çağrılıp bir saniye boyunca bl edecektir. `foo` programı bloke etmişken, `setInterval` fonksiyon çağrılarını zamanlamaya -devam edecektir. `foo` tamamlandığında, çalıştırılmatı bekleyen **on** çağrı +devam edecektir. `foo` tamamlandığında, çalıştırılmayı bekleyen **on** çağrı daha olacaktır. ### Bloke eden programlarla başa çıkmak @@ -82,7 +82,7 @@ istemediğine karar verebilir. ### Zamanlayıcıları iptal etmek -Zamanlayıcıları iptal etmek için ilgili ID sayıları ile kullanılan zamanlatıcı +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. @@ -91,7 +91,7 @@ biri kullanılır. ### Tüm zamanlayıcıları iptal etmek -Tüm zamanlayıcıları iptal etmenin dahili bir metod olmadığı için, bu amaca +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 @@ -101,7 +101,7 @@ ancak kaba kuvvetle ulaşılabilir. 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 -olunur. +edilir. ### `eval` fonksiyonun gizli kullanımı diff --git a/doc/tr/types/casting.md b/doc/tr/types/casting.md index ce7c240f..b994c952 100644 --- a/doc/tr/types/casting.md +++ b/doc/tr/types/casting.md @@ -17,7 +17,7 @@ JavaScript *weakly typed* bir dildir, bu yüzden **mümkün olan yerlerde** 10 == 010; 10 == '-10'; -> **ES5 Notu:** 0 ile başlayan sayı sabitleri oktal (sekizlik) sayı sisteminde +> **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**. @@ -58,7 +58,7 @@ Bir değerin başına boş bir katar eklenerek kolayca katara dönüştürülebi ### Mantıksal değişken tipine dönüştürmek -**not** operatörü iki kez üst üste kullanılarak bir değer mantıksal değişken +**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 diff --git a/doc/tr/types/instanceof.md b/doc/tr/types/instanceof.md index 4dea0b25..09f19238 100644 --- a/doc/tr/types/instanceof.md +++ b/doc/tr/types/instanceof.md @@ -35,6 +35,6 @@ oluşturucuları aynı olmayacaktır. ### Sonuç `instanceof` operatörü **sadece** aynı JavaScript kaynağından gelen özel -nesneler ile kullanılmalıdır. Tıpkp [`typeof`](#types.typeof) operatörü gibi, -bunun dışındaki tüm kullanımlarından **kaçınılmalıdır**. +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/doc/tr/types/typeof.md b/doc/tr/types/typeof.md index 110c863d..62a9a723 100644 --- a/doc/tr/types/typeof.md +++ b/doc/tr/types/typeof.md @@ -1,6 +1,6 @@ -## `typeof` operatörü +## `typeof` Operatörü -The `typeof` operatörü ([`instanceof`](#types.instanceof) ile birlikte) +`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**. @@ -8,9 +8,9 @@ herhalde JavaScript'in en büyük tasarım hatalarından biridir, çünkü nered 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)`), ile de +> **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öre verilir. `typeof` diye +> içindeki ifadenin döndürdüğü değer typeof operatörüne verilir. `typeof` diye > bir fonksiyon yoktur. ### JavaScript tip tablosu @@ -79,7 +79,7 @@ tanımlanmamış bir değişkene erişmek bir `ReferenceError` hatası oluştur. 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ı spesifikasdyonda tanımlanmamıştır; bu nedenle, çeşitli platformlarda +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 From 6253ddb88d5a3a7327994a947c7e797b17f5e7a6 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sun, 17 Apr 2011 19:42:15 +0200 Subject: [PATCH 167/641] Update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e1867192..fecb262f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,6 @@ /site/zh /site/en /site/fi +/site/tr *.md~ *.src.md From d1ceb5684a33699531a2dfc19b1610198bbb9045 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sun, 17 Apr 2011 20:09:56 +0200 Subject: [PATCH 168/641] Add turkish translation --- doc/language.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/language.json b/doc/language.json index 260c7e17..f654ed73 100644 --- a/doc/language.json +++ b/doc/language.json @@ -1,5 +1,5 @@ { "default": "en", - "listed": ["en", "fi", "ru", "zh"] + "listed": ["en", "fi", "ru", "zh", "tr"] } From b089bc821bf7ce94e3b5a6435028d2c30a50ec0d Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:11:24 +0300 Subject: [PATCH 169/641] Merged intro. --- doc/tr/index.json | 6 +----- doc/tr/intro/index.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/doc/tr/index.json b/doc/tr/index.json index 36a94b8d..e6654186 100644 --- a/doc/tr/index.json +++ b/doc/tr/index.json @@ -6,11 +6,7 @@ { "title": "Giriş", "dir": "intro", - "articles": [ - "authors", - "contributors", - "license" - ] + "articles": [] }, { "title": "Nesneler", diff --git a/doc/tr/intro/index.md b/doc/tr/intro/index.md index 303ef891..f747fd0f 100644 --- a/doc/tr/intro/index.md +++ b/doc/tr/intro/index.md @@ -11,5 +11,38 @@ anlatılan konuları anlamak için JavaScript dilini önceden biliyor olmanız istiyorsanız, lütfen Mozilla Programcı Ağı'nda bulunan mükemmel [rehbere][1] başvurun. +## Yazarlar + +Bu rehber, sevimli birer [Stack Overflow][2] kullanıcısı olan [Ivo Wetzel][3] (Yazım) +ve [Zhang Yi Jiang][4] (Tasarım) tarafından hazırlanmıştır. + +## Katkıda Bulunanlar + + - [Caio Romão][5] (Yazım düzeltmeleri) + - [Andreas Blixt][6] (Dil düzeltmeleri) + +## Hosting +JavaScript Garden'a GitHub üzerinden, ve ayrıca [Cramer Development][7] +tarafından desteklenen [JavaScriptGarden.info][8] adresinden ulaşılabilir. + +## Lisans + +JavaScript Garden [MIT lisansı][9] altında yayınlanmıştır ve [GitHub][10] +üzerinde bulunmaktadır. Eğer rehberde yanlışlıklar veya yazım hatalarına +rastlarsanız lütfen [sorunu bize bildirin][11] veya bir `pull request` gönderin. +Bizi ayrıca Stack Overflow'da [JavaScript sohbet odasında][12] da +bulabilirsiniz. + [1]: https://developer.mozilla.org/en/JavaScript/Guide +[2]: http://stackoverflow.com/ +[3]: http://stackoverflow.com/users/170224/ivo-wetzel +[4]: http://stackoverflow.com/users/313758/yi-jiang +[5]: https://github.com/caio +[6]: https://github.com/blixt +[7]: http://cramerdev.com/ +[8]: http://javascriptgarden.info/ +[9]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE +[10]: https://github.com/BonsaiDen/JavaScript-Garden +[11]: https://github.com/BonsaiDen/JavaScript-Garden/issues +[12]: http://chat.stackoverflow.com/rooms/17/javascript From 95397b5e192643d6971b7e8fe0e8bef312b9d31a Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:11:32 +0300 Subject: [PATCH 170/641] Merged intro. --- doc/tr/intro/authors.md | 8 -------- doc/tr/intro/contributors.md | 8 -------- doc/tr/intro/license.md | 13 ------------- 3 files changed, 29 deletions(-) delete mode 100644 doc/tr/intro/authors.md delete mode 100644 doc/tr/intro/contributors.md delete mode 100644 doc/tr/intro/license.md diff --git a/doc/tr/intro/authors.md b/doc/tr/intro/authors.md deleted file mode 100644 index 6e53d523..00000000 --- a/doc/tr/intro/authors.md +++ /dev/null @@ -1,8 +0,0 @@ -## Yazarlar - -Bu rehber, sevimli birer Stack Overflow kullanıcısı olan [Ivo Wetzel][1] (Yazım) -ve [Zhang Yi Jiang][2] (Tasarım) tarafından hazırlanmıştır. - -[1]: http://stackoverflow.com/users/170224/ivo-wetzel -[2]: http://stackoverflow.com/users/313758/yi-jiang - diff --git a/doc/tr/intro/contributors.md b/doc/tr/intro/contributors.md deleted file mode 100644 index ba517abc..00000000 --- a/doc/tr/intro/contributors.md +++ /dev/null @@ -1,8 +0,0 @@ -## Katkıda Bulunanlar - - - [Caio Romão][1] (Yazım düzeltmeleri) - - [Andreas Blixt][2] (Dil düzeltmeleri) - -[1]: https://github.com/caio -[2]: https://github.com/blixt - diff --git a/doc/tr/intro/license.md b/doc/tr/intro/license.md deleted file mode 100644 index a0484d74..00000000 --- a/doc/tr/intro/license.md +++ /dev/null @@ -1,13 +0,0 @@ -## Lisans - -JavaScript Garden [MIT lisansı][1] altında yayınlanmıştır ve [GitHub][2] -üzerinde bulunmaktadır. Eğer rehberde yanlışlıklar veya yazım hatalarına -rastlarsanız lütfen [sorunu bildirin][3] veya bir `pull request` gönderin. -Bizi ayrıca Stack Overflow'da [JavaScript sohbet kanalında][4] da -bulabilirsiniz. - -[1]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE -[2]: https://github.com/BonsaiDen/JavaScript-Garden -[3]: https://github.com/BonsaiDen/JavaScript-Garden/issues -[4]: http://chat.stackoverflow.com/rooms/17/javascript - From 69ab28a6fe8542bdca48c6d5fb113b3154749998 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:16:24 +0300 Subject: [PATCH 171/641] Fixes #56 in Turkish translation. --- doc/tr/object/hasownproperty.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/tr/object/hasownproperty.md b/doc/tr/object/hasownproperty.md index e5eb5df0..634b3695 100644 --- a/doc/tr/object/hasownproperty.md +++ b/doc/tr/object/hasownproperty.md @@ -42,8 +42,9 @@ doğru sonuç alabilmek için `hasOwnProperty `*haricen* kullanılmalıdır. foo.hasOwnProperty('bar'); // her zaman false verir - // hasOwnProperty haricen kullanıldığında 'this' foo olur - {}.hasOwnProperty.call(foo, 'bar'); // true + // hasOwnProperty başka bir nesne üzerinde + // kullanıldığında 'this' foo olur + ({}).hasOwnProperty.call(foo, 'bar'); // true ### Sonuç From 806c9a6ffe8b45e587fea2bce9e34ed9a1b69c56 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:21:29 +0300 Subject: [PATCH 172/641] Same as BonsaiDen/JavaScript-Garden@c9e3df32127b663bcbacb9b8cf1eda2d4aa791e5 for Turkish translation. --- doc/tr/function/general.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tr/function/general.md b/doc/tr/function/general.md index e45949c1..84d54801 100644 --- a/doc/tr/function/general.md +++ b/doc/tr/function/general.md @@ -29,8 +29,8 @@ Bu örnekte *isimsiz fonksiyon* `foo` değişkenine atanır. Yukarıdaki `var` anahtar kelimesi bir bildirim olduğu için `foo` değişkeni program çalışmadan önce yukarı alınır, program çalıştığında `foo` tanımlanmştır. -Fakat değer ataması program çalışırken gerçekleşeceği için, ilgili satır -çalıştığında, `foo` değişkeninin değeri varsayılan olarak +Fakat değer atamaları sadece program çalışırken gerçekleşeceği için, ilgili +satır çalıştığında, `foo` değişkeninin değeri varsayılan olarak [undefined](#core.undefined) olacaktır. ### İsimli fonksiyon ifadesi From 2c35f13507c92a6365dedb0bed273d4a4ff95206 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:23:36 +0300 Subject: [PATCH 173/641] Same as BonsaiDen/JavaScript-Garden@07f23c9de9a6245dd3d2986bf285d00a7072fdc8 for Turkish translation. --- doc/tr/object/general.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tr/object/general.md b/doc/tr/object/general.md index b112a582..7cc81d7c 100644 --- a/doc/tr/object/general.md +++ b/doc/tr/object/general.md @@ -61,8 +61,8 @@ açabilecek özellik isimlerinin kullanılmasına izin vermesidir. ### Özellikleri silmek Bir nesnenin özelliklerinden birini silmenin tek yolu `delete` operatörünü -kullanmaktır; özelliğe `undefined` veya `null` değerlerini atamak **sadece** -özelliğin değerini kaldırır, anahtarı değil. +kullanmaktır; özelliğe `undefined` veya `null` değerlerini atamak sadece +özelliğin *değerini* kaldırır, *anahtarı* değil. var obj = { bar: 1, From cc5b30f9251e52c80a69667840f14dbacf886e50 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:28:45 +0300 Subject: [PATCH 174/641] Same as BonsaiDen/JavaScript-Garden@1b3a48104122202bb02ca451b83c2e3abd8aeaf2 for Turkish translation. --- doc/tr/other/timeouts.md | 7 +++---- doc/tr/types/equality.md | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/tr/other/timeouts.md b/doc/tr/other/timeouts.md index 594ba83c..5454b8e8 100644 --- a/doc/tr/other/timeouts.md +++ b/doc/tr/other/timeouts.md @@ -111,8 +111,8 @@ olarak `eval` kullanılır. > **Not:** Zamanlama fonksiyonları ECMAScript Standartında bulunmadığı için, > bir katar argümanı almaları durumundaki çalışma şekilleri JavaScript motorları -> arasında farklılık gösterebilir. Gerçekten de, Microsoft'un JScript motoru -> `eval` yerine `Function` oluşturucusunu kullanır. +> arasında farklılık gösterebilir. Mesela, Microsoft'un JScript motoru `eval` +> yerine `Function` oluşturucusunu kullanır. function foo() { // setTimeOut ile bu fonksiyon çağrılacaktır @@ -157,5 +157,4 @@ bulunduran bir *isimsiz fonksiyon* kullanılmalıdır. Ayrıca, `setInterval` fonksiyonu çalışan JavaScript programı tarafından bloke olmadığı için tercih edilmemelidir. -[1]: http://en.wikipedia.org/wiki/Document_Object_Model - +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" diff --git a/doc/tr/types/equality.md b/doc/tr/types/equality.md index a0d5d296..3240a54e 100644 --- a/doc/tr/types/equality.md +++ b/doc/tr/types/equality.md @@ -3,11 +3,11 @@ JavaScript'de nesnelerin değerlerinin eşitliğini kontrol etmenin iki farklı yolu vardır. -### Eşittir operatörü +### Eşitlik operatörü -Eşittir operatörü iki adet eşittir işaretinden oluşur: `==` +Eşitlik operatörü iki adet eşittir işaretinden oluşur: `==` -JavaScript *weakly typed* bir dildir, bu nedenle, eşittir operatörü ile +JavaScript *weakly typed* bir dildir. Bu nedenle, eşitlik operatörü ile değişkenleri karşılaştırırken **tip dönüşümü** yapar. "" == "0" // false From 192a1df798d436ec5a7b245f410d14941ec5c73a Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 02:07:00 +0300 Subject: [PATCH 175/641] Translated hosting subsection. --- doc/tr/intro/index.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/tr/intro/index.md b/doc/tr/intro/index.md index f747fd0f..e6f1fce6 100644 --- a/doc/tr/intro/index.md +++ b/doc/tr/intro/index.md @@ -21,9 +21,10 @@ ve [Zhang Yi Jiang][4] (Tasarım) tarafından hazırlanmıştır. - [Caio Romão][5] (Yazım düzeltmeleri) - [Andreas Blixt][6] (Dil düzeltmeleri) -## Hosting -JavaScript Garden'a GitHub üzerinden, ve ayrıca [Cramer Development][7] -tarafından desteklenen [JavaScriptGarden.info][8] adresinden ulaşılabilir. +## Sunum + +JavaScript Garden GitHub üzerinden, ve ayrıca [Cramer Development][7] +tarafından desteklenen [JavaScriptGarden.info][8] adresinden sunulmaktadır. ## Lisans From 53decceb1391f2e68b38a95aaf144a5d6f7c1dba Mon Sep 17 00:00:00 2001 From: MONTILLET Xavier Date: Tue, 26 Apr 2011 11:33:00 -0700 Subject: [PATCH 176/641] Added another way to clear "all" timeouts and replaced the "is is" by "it is". --- doc/en/other/timeouts.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/en/other/timeouts.md b/doc/en/other/timeouts.md index d196a3b7..02d39ea6 100644 --- a/doc/en/other/timeouts.md +++ b/doc/en/other/timeouts.md @@ -94,8 +94,14 @@ it is necessary to use brute force in order to achieve this functionality. clearTimeout(i); } -There might still be timeouts that are unaffected by this arbitrary number; -therefore, is is instead recommended to keep track of all the timeout IDs, so +But there might still be timeouts that are unaffected by this arbitrary number. Another way of doing this is to consider that the ID given to a timeout is incremented by one everytime you call `setTimeout`. + + // clear "all" timeouts + for(var i = 1; i <= window.setTimeout(function(){}, 1); i++) { + clearTimeout(i); + } + +But even though this works on all main browsers nowadays, it isn't specified that the IDs should be ordered that way and it may change. Therefore, it is instead recommended to keep track of all the timeout IDs, so they can be cleared specifically. ### Hidden use of `eval` From 0a894c199ac7e9f77e6f3fb8463b9b2d5cd111b4 Mon Sep 17 00:00:00 2001 From: MONTILLET Xavier Date: Tue, 26 Apr 2011 14:12:47 -0700 Subject: [PATCH 177/641] Stored the biggest timeout ID to avoid infinite loop. --- doc/en/other/timeouts.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/en/other/timeouts.md b/doc/en/other/timeouts.md index 02d39ea6..655fcc35 100644 --- a/doc/en/other/timeouts.md +++ b/doc/en/other/timeouts.md @@ -97,7 +97,9 @@ it is necessary to use brute force in order to achieve this functionality. But there might still be timeouts that are unaffected by this arbitrary number. Another way of doing this is to consider that the ID given to a timeout is incremented by one everytime you call `setTimeout`. // clear "all" timeouts - for(var i = 1; i <= window.setTimeout(function(){}, 1); i++) { + var biggestTimeoutId = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= biggestTimeoutId; i++) { clearTimeout(i); } From 33efc0531ed2352070d7d95fb687fa7333686b24 Mon Sep 17 00:00:00 2001 From: Lukasz Kufel Date: Wed, 4 May 2011 15:07:08 +0200 Subject: [PATCH 178/641] Polish translation of function and array sections. --- doc/pl/array/constructor.md | 37 +++++++++++ doc/pl/array/general.md | 58 +++++++++++++++++ doc/pl/full_index.json | 2 +- doc/pl/function/arguments.md | 122 +++++++++++++++++++++++++++++++++++ doc/pl/function/closures.md | 101 +++++++++++++++++++++++++++++ doc/pl/function/general.md | 52 +++++++++++++++ doc/pl/function/this.md | 116 +++++++++++++++++++++++++++++++++ doc/pl/index.json | 20 ++++++ doc/pl/object/forinloop.md | 2 +- doc/pl/object/prototype.md | 2 +- 10 files changed, 509 insertions(+), 3 deletions(-) create mode 100644 doc/pl/array/constructor.md create mode 100644 doc/pl/array/general.md create mode 100644 doc/pl/function/arguments.md create mode 100644 doc/pl/function/closures.md create mode 100644 doc/pl/function/general.md create mode 100644 doc/pl/function/this.md diff --git a/doc/pl/array/constructor.md b/doc/pl/array/constructor.md new file mode 100644 index 00000000..79772f43 --- /dev/null +++ b/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 +parametry do niego przekazane. + + [1, 2, 3]; // Rezultat: [1, 2, 3] + new Array(1, 2, 3); // Rezultat: [1, 2, 3] + + [3]; // Rezultat: [3] + new Array(3); // Rezultat: [] + new Array('3') // Rezultat: ['3'] + +W przypadku gdy tylko jeden argument zostanie przekazany do kostruktora `Array` i +ten argument jest typu `Number`, konstruktor zwróci nową *dziwną* tablicę +z ustawioną właściwością `length` na wartość przekazaną jako argument. Należy +zauważyć, że **tylko** właściwość `length` zostanie ustawiona w ten sposób, +rzeczywiste indeksy w tej tablicy nie zostaną zainicjalizowane. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // zwraca false, indeks nie został ustawiony + +Możliwość ustanienia z góry długości tablicy jest użyteczna tylko w kilku +przypadkach, jak powtarzanie ciągu znaków, w którym unika się stosowania +pętli `for`. + + // count - ilosc powtorzen + // stringToRepeat - ciąg znaków do powtórzenia + new Array(count + 1).join(stringToRepeat); + +### Wnioski + +W miare możliwości należy unikać używania konstruktora `Array`. Literały są +zdecydowanie lepszym rozwiązaniem, są krótsze i mają bardziej precyzyjną składnię. +Zwiększają również czytelność kodu. + diff --git a/doc/pl/array/general.md b/doc/pl/array/general.md new file mode 100644 index 00000000..c30056d7 --- /dev/null +++ b/doc/pl/array/general.md @@ -0,0 +1,58 @@ +## Iterowanie po tablicach oraz właściwości tablic + +Mimo, że tablice w JavaScript są obiektami, nie ma dobrych powodów do używania +[`pętli for in`](#object.forinloop) do iteracji po nich. W rzeczywiści istnieje +wiele dobrych powodów **przeciwko** wykorzystania `for in` na tablicach. + +> **Uwaga:** Tablice JavaScriptowe **nie** są *tablicami asocjacyjnymi*. JavaScript +> posiada tylko [obiekty](#object.general) do mapowania kluczy do wartości. Jednakże +> tablice asocjacyjne **zachowują** porządek, natomiast obiekty **nie zachowują**. + +Ponieważ pętla `for in` wylicza wszystkie właściwości, które są wewnątrz +łańcucha prototypów i jedynym sposobem aby wykluczyć te właściwości to użycie +[`hasOwnProperty`](#object.hasownproperty), ale wówczas pętla staje się +**dwadzieście razy** wolniejsza od normalnej pętli `for`. + +### Iteracja + +W celu osiągnięcia najlepszej wydajności podczas iteracji po tablicach należy +użyć klasycznej pętli `for`. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +Jest tam jeszcze jeden dodatkowy haczyk w przykładzie powyżej. Jest to zbuforowanie +długości tablicy poprzez `l = list.length`. + +Mimo, że właściwość `length` jest zdefiniowana w wewnątrz tablicy, istnieje nadal +dodatkowy koszt na wyszukiwanie tej właściwości przy każdej iteracji w pętli. +Chociaż najnowsze silniki JavaScript **mogą** zastosować optymalizację w tym +przypadku. Nie ma jednak możliwość ustalenia czy kod będzie wykonywany w jednym +z tych nowych silników czy też nie. + +W rzeczywistości pomijając buforowanie długości tablicy może spowodować, że pętla +będzie tylko **w połowie tak szybka** jak ta z buforowaniem długości. + +### Właściwość `length` + +Mimo, że *getter* właściwości `length` po prostu zwraca liczbę elementów, które są +zawarte w tablicy, to *setter* może być użyta do **skracania** tablicy. + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo; // [1, 2, 3] + +Przypisanie mniejszej długości spowoduje skrócenie tablicy, ale zwiększenie wartości +`length` nie ma żadnego wpływu na tablicę. + +### Wnioski + +Aby uzyskać najlepszą wydajność zaleca się, aby zawsze używać zwykłej pętli `for` +i zbuforowanie właściwości `length`. Korzystanie z pętli `for in` na tablicy jest +znakiem źle napisanego kodu, który jest podatny na błędy i ma słabą wydajność. + diff --git a/doc/pl/full_index.json b/doc/pl/full_index.json index 469324be..16b65ffb 100644 --- a/doc/pl/full_index.json +++ b/doc/pl/full_index.json @@ -55,7 +55,7 @@ ] }, { - "title": "?Wnętrze?", + "title": "Jądro", "dir": "core", "articles": [ "eval", diff --git a/doc/pl/function/arguments.md b/doc/pl/function/arguments.md new file mode 100644 index 00000000..1b56e10a --- /dev/null +++ b/doc/pl/function/arguments.md @@ -0,0 +1,122 @@ +## obiekt `arguments` + +Każda zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej `arguments`. +Ta zmienna trzyma listę wszystkich argumentów przekazanych do funkcji. + +> **Uwaga:** W przypadku gdy `arguments` zostanie zadeklarowana wewnatrz funkcji +> poprzez `var` lub jako nazwa jednego z formalnych parametrów, obiekt `arguments` +> nie zostanie utworzony. + +Obiekt `arguments` **nie** jest typu `Array`. Mimo, że posiada pewne cechy +semantyki tablic - właściwość `length` - to nie dziedziczy on z `Array.prototype`, +ale w rzeczywistości z `Object`. + +Ze względu na to **nie** można używać standardowych dla tablic metod takich jak +`push`, `pop` czy `slice` na obiekcie `arguments`. Mimo, że iteracja przy pomocy +pętli `for` działa dobrze, to aby skorzystać ze standardowych metod tablicowych +należy skonwertować `arguments` do prawdziwego obiekt `Array`. + +### Konwersja do tablicy + +Poniższy kod zwróci nowy obiekt `Array` zawierający wszystkie elementy +obiektu `arguments`. + + Array.prototype.slice.call(arguments); + +Jednakże konwersja ta jest **wolna** i **nie jest zalecana** w sekcjach, +które mają duży wpływ na wydajność. + +### Przekazywanie argumentów + +Zalecany sposób przekazywania argumentów z jednej funkcji do następnej +wyglada następująco. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // do stuff here + } + +Kolejną sztuczką jest użycie razem `call` i `apply` w celu stworzenia +szybkich i nieograniczonych wrapperów. + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Stworzenie nieograniczoną wersję metody "method" + // która przyjmuje parametry: this, arg1, arg2...argN + Foo.method = function() { + + // Rezultat: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +### Parametry formalne i indeksy argumentów + +Obiekt `arguments` tworzy funckje *getter* i *setter* nie tylko dla swoich +właściwości, ale również dla parametrów formalnych funkcji. + +W rezultacie zmiana wartości parametru formalnego zmieni również wartość +odpowiadającemu mu wpisowi w obiekcie `arguments`, zachodzi to również w drugą stronę. + + function foo(a, b, c) { + arguments[0] = 2; + a; // 2 + + b = 4; + arguments[1]; // 4 + + var d = c; + d = 9; + c; // 3 + } + foo(1, 2, 3); + +### Mity i prawdy o wydajności + +Obiekt `arguments` jest zawsze tworzony z wyjątkiem dwóch przypadków, gdy +zmienna o takiej nazwie jest zdefiniowana wewnątrz funkcji lub jeden z parametrów +formalnych funkcji ma taką nazwę. Nie ma znaczenia czy obiekt `arguments` jest +używany czy nie. + +Zarówno *gettery* jak i *settery* są zawsze tworzone, zatem używanie ich nie ma +praktycznie żadnego wpływu na wydajność. Zwłaszcza w rzeczywistym kodzie, który +wykorzystuje coś więcej niż tylko prosty dostęp do właściwości obiektu `arguments`. + +> **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 niej wynikające, ale również stanowi złamanie +zasad enkapsulacji ponieważ ta funkcja jest zależna od kontekstu +w jakim została wywołana. + +**Mocno zalecane** jest aby **nigdy** nie korzystać z `arguments.callee` +i żadnej jej własności. + +> **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/doc/pl/function/closures.md b/doc/pl/function/closures.md new file mode 100644 index 00000000..c8e45b99 --- /dev/null +++ b/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 +[funckję](#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 funckję `get`. +Obie te funkcję trzymają **referencję** do zasięgu `Counter` a co za tym idzie +zawsze posiadają dostęp do zmiennej `count` tak, jakby ta zmienna była zdefiniowana +w zasięgu tych funkcji. + +### Dlaczego zmienne przywatne działają + +Ponieważ nie ma możliwości wskazania lub przypisania zasięgu w JavaScript, to +**nie** istnieje sposób aby uzyskać dostęp do zmiennej `count` z zewnątrz. +Wykorzystanie tych dwóch domkinęć jest jedynym sposobem na interakcję z tą zmienną. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + + +Powyższy kod **nie** zmieni wartości zmiennej `count` wewnątrz zasięgu `Counter`, +ponieważ `foo.hack` nie została zadeklarowana wewnątrz **tego konkretnego** zasięgu. +Zamiast tego funkcja utworzy lub nadpisze *globalną* zmienną `count`. + +### Domknięcia wewnątrz pętli + +Jednym z częstrzych błędów jest wykorzystywanie domknięć wewnątrz pętli, +aby wartość zmiennej po której odbywa się iteracja był kopiowana do +wewnętrznej funkcji. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +Powyższy kod **nie** wypisze numerów od `0` do `9`, ale wypisze +dziesięć razy liczbę `10`. + +*Anonimowa* funkcja trzyma **wskaźnik** do zmiennej `i` i podczas uruchomienia +`console.log`, pętla `for` już zakończyła działanie i wartość zmiennej `i` +została ustawiona na `10`. + +Aby otrzymać zamierzony efekt, niezbędne jest **skopiowanie** wartości +zmiennej `i`. + +### Unikanie problemu z referencją + +Aby skopiować wartość zmiennej, po której iterujemy w pętli, należy skorzystać +z [anonimowego wrappera](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +Zewnętrzna anonimowa funkcja zostaje wywołana od razu z parametrem `i` +jako pierwszym argumentem i otrzyma kopię **wartości** zmiennej `i` jako +zmienną `e`. + +Anonimowa funkcja która zostaje przekazana do `setTimeout` teraz posiada +referencję do zmiennej `e`, która nie zostanie zmieniona przez pętle `for`. + +Istnieje jeszcze jeden sposób na osiągnięcie tego samego efektu. Należy zwrócic +fukcję z anonimowego wrappera, wówczas kod będzie zachowywał się jak ten +wcześniejszy. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + diff --git a/doc/pl/function/general.md b/doc/pl/function/general.md new file mode 100644 index 00000000..821682ed --- /dev/null +++ b/doc/pl/function/general.md @@ -0,0 +1,52 @@ +## Deklaracje funkcji i wyrażenia funkcyjne + +Funcje w języku JavaScript są [typami pierwszoklasowymi][1]. Co oznacza, że mogą +być przekazywane jak każda inna wartość. Jednym z typowych zastosowań tej cechy +jest przekazywanie *anonimowej funkcji* jako callback do innej, prawdopodobnie +asynchronicznej funkcji. + +### Deklaracja funckcji + + 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 funkdyjne + +Kolejnym specjalnym przypadkiem jest przypisanie nazwanej funkcji. + + var foo = function bar() { + bar(); // Działa + } + bar(); // wyrzuca ReferenceError + +W zewnętrznym zakresie `bar` nie będzie dostępne, ponieważ funkcja zostaje +przypisana do `foo`, jednakże w wewnętrznym zakresie `bar` będzie dostępna. +Jest to spowodowane tym, jak działa [rozwiązywanie nazw](#function.scopes) +w języku JavaScript. Nazwa funkcji jest *zawsze* dostępna w lokalnym +zakresie tej funkcji. + +[1]: http://pl.wikipedia.org/wiki/Typ_pierwszoklasowy \ No newline at end of file diff --git a/doc/pl/function/this.md b/doc/pl/function/this.md new file mode 100644 index 00000000..8fc8564d --- /dev/null +++ b/doc/pl/function/this.md @@ -0,0 +1,116 @@ +## Jak działa `this` + +JavaScript posiada inną koncepcję odnośnie tego na co wskazuje specjalna +nazwa `this`, niż większość innych języków programowania. Istnieją dokładnie +**pięć** różnych sytuacji w których wartość `this` zostaje przypisana w języku JavaScript. + +JavaScript has a different concept of what the special name `this` refers to +than most other programming languages do. There are exactly **five** different +ways in which the value of `this` can be bound in the language. + +### Zasięg globalny + + this; + +Używanie `this` w globalnym zasięgu, zwróci po prostu referencje do obiektu *global*. + + +### Wywołanie funkcji + + foo(); + +Tutaj `this` również będzie wkazywało na obiekt *global* + +> **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ć +traktorany jako błąd podczas projektowania języka i **nigdy** nie wykorzystywany +w praktyce. + + Foo.method = function() { + function test() { + // wewnątrz tej funkcji this wskazuje na obiekt global + } + test(); + } + +Powszechnym nieporozumieniem jest, że `this` wewnątrz `test` wskazuje na `Foo`, +podczas gdy w rzeczywistości tak **nie jest**. + +Aby uzyskać dostęp do `Foo` wewnątrz `test` niezbędne jest stworzenie wewnątrz +metody lokalnej zmiennej, która będzie wskazywała na `Foo`. + + Foo.method = function() { + var that = this; + function test() { + // Należy używać that zamiast this wewnątrz tej funkcji + } + test(); + } + +`that` jest zwykłą zmienną, ale jest to powszechnie stosowana konwencja, aby otrzymać +wartość zewnętrznego `this`. W połączeniu z [domknięciami(closures)](#function.closures) +jest to sposób na przekazywanie wartości `this` wokoło. + +### Metody przypisywania + +Kolejną rzeczą, która **nie** działa w języku JavaScript jest nadawanie aliasów +funkcjom, co oznacza **przypisanie** metody do zmiennej. + + var test = someObject.methodTest; + test(); + +Podobnie jak w pierwszym przypadku `test` zachowuje się jak wywołanie zwykłej +funkcji, a zatem wewnątrz funkcji `this` już nie będzie wskazywało `someObject`. + +Podczas gdy późne wiązanie `this` może się na początku wydawać złym pomysłem, +to w rzeczywistości jest to rzecz, która powoduje że +[dziedziczenie prototypowe](#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/doc/pl/index.json b/doc/pl/index.json index 9d904471..96131148 100644 --- a/doc/pl/index.json +++ b/doc/pl/index.json @@ -23,6 +23,26 @@ "hasownproperty", "forinloop" ] + }, + { + "title": "Funkcje", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Tablice", + "dir": "array", + "articles": [ + "general", + "constructor" + ] } ] } diff --git a/doc/pl/object/forinloop.md b/doc/pl/object/forinloop.md index 218b0117..a2e2b5fe 100644 --- a/doc/pl/object/forinloop.md +++ b/doc/pl/object/forinloop.md @@ -37,7 +37,7 @@ To jest jedyna poprawna wersja, którą należy używać. Ze względu na użycie kod będzie podatny na błędy, gdy natywne prototypy np. `Object.prototype` zostanie rozszerzony. -[Prototype][1] jest jednym z szeroko rozpowszechniony framework, który dokonuje +[Prototype][1] jest jednym z szeroko rozpowszechniony frameworków, który dokonuje takiego rozszerzenia. Używanie tego frameworku oraz nie używanie w pętle `for in` metody `hasOwnProperty` gwarantuje błędy w wykonaniu. diff --git a/doc/pl/object/prototype.md b/doc/pl/object/prototype.md index c25e1df1..e79a45cb 100644 --- a/doc/pl/object/prototype.md +++ b/doc/pl/object/prototype.md @@ -47,7 +47,7 @@ tak zwanych *łańcuchów prototypów*. Object.prototype { toString: ... /* etc. */ } -W powyższym przykładzie obiekt `test będzie dziedziczył z obydwu tj. +W powyższym przykładzie obiekt `test` będzie dziedziczył z obydwu tj. `Bar.prototyp` i `Foo.prototyp`, stąd będzie miał dostęp do funkcji `method`, która była zdefiniowana w `Foo`. Ponadto obiekt będzie miał dostęp do właściwości `value`, która jest jednyną instancją `Foo` i stała się jego prototypem. From 95f57a73541b8207bbfdd404f76b3c3a0f28ca24 Mon Sep 17 00:00:00 2001 From: Lukasz Kufel Date: Thu, 5 May 2011 13:00:14 +0200 Subject: [PATCH 179/641] Polish translation of types section. --- doc/pl/index.json | 10 +++++ doc/pl/intro/translators.md | 5 +-- doc/pl/types/casting.md | 72 ++++++++++++++++++++++++++++++ doc/pl/types/equality.md | 71 ++++++++++++++++++++++++++++++ doc/pl/types/instanceof.md | 41 +++++++++++++++++ doc/pl/types/typeof.md | 87 +++++++++++++++++++++++++++++++++++++ 6 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 doc/pl/types/casting.md create mode 100644 doc/pl/types/equality.md create mode 100644 doc/pl/types/instanceof.md create mode 100644 doc/pl/types/typeof.md diff --git a/doc/pl/index.json b/doc/pl/index.json index 96131148..5e26527b 100644 --- a/doc/pl/index.json +++ b/doc/pl/index.json @@ -43,6 +43,16 @@ "general", "constructor" ] + }, + { + "title": "Typy", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] } ] } diff --git a/doc/pl/intro/translators.md b/doc/pl/intro/translators.md index b827667b..60b3626e 100644 --- a/doc/pl/intro/translators.md +++ b/doc/pl/intro/translators.md @@ -1,6 +1,5 @@ ## Tłumaczenie - - [Łukasz Kufel][1] ([qfel13.pl][2]) + - [Łukasz Kufel][1] -[1]: https://github.com/qfel13 -[2]: http://qfel13.pl \ No newline at end of file +[1]: http://qfel13.pl \ No newline at end of file diff --git a/doc/pl/types/casting.md b/doc/pl/types/casting.md new file mode 100644 index 00000000..ae0247f4 --- /dev/null +++ b/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. + + // These are true + new Number(10) == 10; // Number.toString() zostanie przekształcone + // z powrotem do liczby + + 10 == '10'; // Stringi zostaną przekształcone do typu Number + 10 == '+10 '; // Kolejne wariacje + 10 == '010'; // i następne + isNaN(null) == false; // null zostanie przekształcony do 0 + // który oczywiście nie jest NaN + + // Poniższe zwracają false + 10 == 010; + 10 == '-10'; + +> **Uwaga ES5: Literały licznowe zaczynające sie od `0` są interpretowane jako +> liczby w systemie ósemkowym. W trybie strict mode w ECMAScript 5 wsparcie dla +> liczb ósemkowych zostało porzucone. + +Aby uniknąć powyższych problemów, należy **koniecznie** skorzystać ze +[ściełego operatora równości](#types.equality). Mimo, że pozwala to uniknąć wiele +typowych problemów to nadal istnieje wiele innych, które powstają na bazie słabego +typowania języka JavaScript. + +### Konstruktory typów wbudowanych + +Konstruktory typów wbudowanych takich, jak `Number` lub `String` zachowują się +inaczej jeżeli są poprzedzone słowem kluczowym `new` a inaczej jeżeli nie są. + + new Number(10) === 10; // False, Object i Number + Number(10) === 10; // True, Number i Number + new Number(10) + 0 === 10; // True, ponieważ dokonana została jawna konwersja + +Korzystanie z wbudowanych typów jak `Number` jako konstruktor utworzy nowy obiekt +typu `Number`, natomiast opuszczenie słowa kluczowego `new` spowoduje, że funkcja +`Number` zachowa się jak konwerter. + +Ponadto, użycie literałów lub wartości nieobiektowych zaowocuje jeszcze większą +ilością rzutowań (koercją) typów. + +Najlepszym rozwiązaniem jest **jawne** rzutowanie do jednego z trzech typów. + +### Rzutowanie do typu String + + '' + 10 === '10'; // true + +Konkatenacja pustego stringu i wartości powoduje rzutowanie do typu String. + +### Rzutowanie do typu Number + + +'10' === 10; // true + +Zastosowanie **unarnego** operatora + spowoduje rzutowanie do typu Number. + +### Rzutowanie do typu Boolean + +Używając dwukrotnie operatora **negacji** dowolna wartość może zostać zrzutowana +do typu Boolean + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + + diff --git a/doc/pl/types/equality.md b/doc/pl/types/equality.md new file mode 100644 index 00000000..e79b952f --- /dev/null +++ b/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łędy. + +Ponadto koercja ma również wpływ na wydajność na przykład gdy typ String musi zostać +przekształcony na typ Number przed porównaniem z drugą liczbą. + +### Operator ścisłej równości + +Operator ścisłej równości składa się z **trzech** znaków "równa się": `===` + +Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem nie +dokonuje koercji typów przed porównaniem. + + "" === "0" // false + 0 === "" // false + 0 === "0" // false + false === "false" // false + false === "0" // false + false === undefined // false + false === null // false + null === undefined // false + " \t\r\n" === 0 // false + +Powyższe rezultaty są o wiele bardziej przejrzyste. Powoduje to "ustatycznienie" +języka do pewnego stopnia oraz pozwala na wprowadzenie optymalizacji porównań +obiektów o różnych typach. + +### Porównywanie obiektów + +Mimo, że oba operatory `==` i `===` nazywane są operatorami **równościowymi**, +to zachowują się różnie gdy jednym z operandów jest obiekt typu `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Oba operatory porównują **toższmość** a **nie** równość, czyli będą porównywać czy +jeden i drugi operand jest tą samą **instancją** obiektu, podobnie jak operator +`is` w Pythonie i porównanie wskaźników w C. + +### Wnioski + +Zaleca się aby używać tylko operatora **ścisłej równości**. W sytuacjach gdy +potrzebna jest koercja (porównanie obiektów różnych typów), konwersja powinna +być dokonana [jawnie](#types.casting) a nie pozostawiona trudnym regułom koercji +obowiązującym w języku. + diff --git a/doc/pl/types/instanceof.md b/doc/pl/types/instanceof.md new file mode 100644 index 00000000..73ae4ebd --- /dev/null +++ b/doc/pl/types/instanceof.md @@ -0,0 +1,41 @@ +## Operator `instanceof` + +Operator `instanceof` porównuje konstruktory obiektów przekazanych jako operendy. +Jest on jedynie użyteczny do porównywania obiektów utworzonych klas. Stosowanie +go na wbudowanych typach jest praktycznie tak samo bezużyteczne jak operatora +[typeof](#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ć **tylko** używany podczas korzystania z obiektów +klas utworzonych, które były zdefiniowane w tym samym kontekscie JavaScriptowym. +Podobnie jak operator [`typeof`](#types.typeof), należy **unikać** korzystania +z tego operatora w innych sytuacjach. + diff --git a/doc/pl/types/typeof.md b/doc/pl/types/typeof.md new file mode 100644 index 00000000..186e241f --- /dev/null +++ b/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, jest on praktycznie +**całkowicie wadliwy**. + +Mimo, że `instanceof` ma swoje wady to nadal ma ograniczone zastosowanie w praktyce, +natomiast `typeof` ma tylko jeden praktyczny przypadek użycia, który na dodatek +**nie** jest związany z sprawdzaniem typu obiektu. + +> **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 sie 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 **o ile to tylko możliwe**. + From 403b6f3f005385d5e3bbb3bb11f643ec9516d37d Mon Sep 17 00:00:00 2001 From: Lukasz Kufel Date: Thu, 5 May 2011 14:05:07 +0200 Subject: [PATCH 180/641] PL forgot to add some files. --- doc/pl/function/constructors.md | 133 ++++++++++++++++++ doc/pl/function/scopes.md | 234 ++++++++++++++++++++++++++++++++ 2 files changed, 367 insertions(+) create mode 100644 doc/pl/function/constructors.md create mode 100644 doc/pl/function/scopes.md diff --git a/doc/pl/function/constructors.md b/doc/pl/function/constructors.md new file mode 100644 index 00000000..1654f4bb --- /dev/null +++ b/doc/pl/function/constructors.md @@ -0,0 +1,133 @@ +## Konstruktory + +Konstruktory w JavaScript również wyglądają inaczej niż innych języka. Każde +wywołanie funkcji, które jest poprzedone słowem kluczowym `new` zachowuje się +jak konstruktor. + +Wewnątrz konstruktora - wywoływanej fukcji - wartość `this` wskazuje na +nowo utworzony obiekt `Object`. Prototyp [`prototype`](#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łanya została funkcja `Foo` jako konstruktor oraz ustawia +nowo utworzonemu obiektowi właściwość `prototype` na `Foo.prototype`. + +W tym przypadku jawna deklaracja `return` w funkcji zwraca wartość +ustawioną w deklaracji, **ale tylko** jeżeli zwracaną wartością jest +obiekt `Object`. + + function Bar() { + return 2; + } + new Bar(); // nowy obiekt + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // zwrócony obiekt + +Jeżeli słowo kluczowe `new` zostanie pominięte funkcja **nie** zwróci nowego +obiektu. + + function Foo() { + this.bla = 1; // zostanie ustawiona w obiekcie global + } + Foo(); // undefined + +Mimo, że powyższy kod może zadziałać w pewnych przypadkach, w związku +z działaniem [`this`](#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` w sobie, dla którego `Bar` jest [Domknięciem](#function.closures). + +Należy również pamiętać, że wywołanie `new Bar()` **nie** ma wpływu na +prototyp zwróconego obiektu (prototypem będzie `object.prototype` a nie `Bar.prototype`). +Podczas gdy prototyp zostanie przypisany do nowo utworzonego obiektu, to jednak `Bar` +nidgy nie zwróci tego nowego obiektu `Bar`, ale literał obiektu, który jest po +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 używając fabryk + +Często zaleca się **nie** korzystać z operatora `new` ponieważ zapominając +go zastosować może prowadzić do błędów. + +W celu stworzenia nowego obiektu, powinno się używać fabryki i konstruować +nowy obiekt wewnątrz tej fabryki. + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + + +Mimo, że powyższy kod jest odporny na brak słowa kluczowego `new` i ułatwia +korzystanie ze [zmiennych prywatnych](#function.closures), to posiada +pewne wady. +While the above is robust against a missing `new` keyword and certainly makes +the use of [private variables](#function.closures) easier, it comes with some +downsides. + 1. Zużywa więcej pamięci, ponieważ tworzony obiekt **nie** współdzieli metod + poprzez prototyp + 2. Aby móc dziedziczyć fabryka musi skopiować wszystkie metody z dziedziczonego + obiektu lub przypisać ten obiekt, z którego się dziedziczy, jako prototyp + do nowo utworzonego obiektu. + 3. Porzucenie łańcucha prototypów tylko ze względu na opuszczone słowo kluczowe + `new` jest sprzeczne z duchem języka. + +### Wnioski + +Pominięcie słowa kluczowego `new` może prowadzić do błędów, ale na pewno nie +powinno to być powodem odrzucenia używania prototypów w ogóle. Sprowadza się to +do wyboru rozwiązania, które bardziej pasuje do potrzeb aplikacji. Szczególnie +ważne jest aby wybrać określony styl tworzenia obiektów i się go **trzymać**. + diff --git a/doc/pl/function/scopes.md b/doc/pl/function/scopes.md new file mode 100644 index 00000000..a06eba05 --- /dev/null +++ b/doc/pl/function/scopes.md @@ -0,0 +1,234 @@ +## 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. + +Za każdym razem gdy zmienna jest +Z każdym odwołaniem do zmiennej JavaScript przeszukuje w górę wszystkie zasięgi +dopóki nie znajdzie tej zmiennej. W przypadku gdy przeszukiwanie dotrze do globalnego +zasięgu i nadal nie znajdzie żądanej nazwy to wyrzuca błąd `ReferenceError`. + +### Zmora globalnych zmiennych + + // script A + foo = '42'; + + // script B + var foo = '42' + +Powyższe dwa skrypty **nie** dają tego samego efektu. Skrypt A definiuje zmienna +nazwaną `foo` w *globalnym* zasięgu natomiast skrypt B definiuje `foo` +w *aktualnym* zasięgu. + +Jeszcze raz, to wcale nie daje *tego samego efektu*, nie użycie `var` może mieć +poważne konsekwencje. + + // globalny zasięg + var foo = 42; + function test() { + // lokalny zasięg + foo = 21; + } + test(); + foo; // 21 + +Pominięcie słowa `var` w deklaracji wewnątrz funkcji `test` nadpisze wartość +zmiennej globalnej `foo`. Mimo, że nie wygląda to na początku jak duży problem, +to posiadając wiele tysięcy linii kodu w JavaScript i nie korzystanie z `var` +wprowadzi straszne i trudne do wyśledzenia błędy. + + // globalny zasięg + var items = [/* jakaś lista */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // scope of subLoop + for(i = 0; i < 10; i++) { // brakuje słowa var w deklaracji + // do amazing stuff! + } + } + +Zewnętrzna pętla zakończy działanie po pierwszym wywołaniu `subLoop`, ponieważ +`subLoop` nadpisuje wartość globalnej zmiennej `i`. Użycie `var` w drugiej pętli +`for` pozwoliło by łatwo uniknąć problemu. Słowo kluczowe `var` nie powinno być +**nigdy** pominięte w deklaracji, chyba że *pożądanym skutkiem* jest wpłynięcie na +zewnętrzny zasięg. + +### Lokalne zmienne + +Jedynym źródłem zmiennych lokalnych w JavaScripcie są parametry [funkcji](#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 zasiegu funkcji `test`, +natomiast przypisanie wartości do `bar` nadpisze zmienną globalną o tej samej nazwie. + +### "Hoisting" - wywindowanie, podnoszenie + +JavaScript **winduje** deklaracje. Oznacza to, że zarówno deklaracja ze słowem +kluczowym `var` jak i deklaracje funkcji `function` zostaną przeniesione na +początek otaczającego zasięgu. + + bar(); + var bar = function() {}; + var someValue = 42; + + test(); + function test(data) { + if (false) { + goo = 1; + + } else { + var goo = 2; + } + for(var i = 0; i < 100; i++) { + var e = data[i]; + } + } + +Powyższy kod zostanie przekształcony przed rozpoczęciem wykonania. JavaScript +przeniesie deklarację zmiennej `var` oraz deklarację funkcji `function` na szczyt +najbliższego zasięgu. + + // deklaracje var zostaną przeniesione tutaj + var bar, someValue; // ustawione domyślnie na 'undefined' + + // deklaracje funkcji zostaną również przeniesione na górę + function test(data) { + var goo, i, e; // brak blokowego zasięgu spowoduje przeniesienie tutaj + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // powoduje błąd TypeError ponieważ bar jest nadal 'undefined' + someValue = 42; // przypisania nie zostają zmienione przez 'hoisting' + bar = function() {}; + + test(); + +Brak blokowego zasięgu nie tylko przeniesie deklaracje `var` poza ciało pętle, +ale również spowoduje, że niektóre porównania `if` staną się nieintuicyjne. + +W oryginalnym kodzie wewnątrz deklaracja `if` zdaje się modyfikować *zmienną +globalną* `goo`, podczas gdy faktycznie modyfikuje ona *zmienną lokalną* - po tym +jak zostało zastosowane windowanie (hoisting). + +Bez wiedzy na temat podnoszenia (hoistingu), poniższy kod może wydawać się +wyrzucać błąd `ReferenceError`. + + // sprawdz czy SomeImportantThing zostało zainicjalizowane + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +Oczywiście powyższy kod działa ze względu na fakt, że deklaracja `var` zostanie +przeniesiona na początek *globalnego zasięgu*. + + var SomeImportantThing; + + // inny kod który może ale nie musi zainicjalizować SomeImportantThing + + // upewnienie sie że SomeImportantThing zostało zainicjalizowane + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Kolejność rozwiązywania nazw + +Wszystkie zasięgi w JavaScripcie, włączając *globalny zasięg*, posiadają +zdefiniowana wewnątrz specjalną nazwę [`this`](#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 fukcja została nazwana `foo` skorzystaj z tego. + 4. Przejdz do zewnętrznego zasięgu i przejdz do kroku **#1**. + +> **Uwaga:** Jeżeli jeden z parametrów fukcji został nazwany `arguments` zapobiegnie +> to utworzenia domyślnego obiektu `arguments`. + +### Przestrzenie nazw + +Powszechnym problemem posiadania tylko jednej globalnej przestrzeni nazw jest +prawdopodobieństwo wystąpienia kolizji nazw. W JavaScripcie, można łatwo uniknąć +tego problemu korzystając z *anonimowych wrapperów*. + + (function() { + // autonomiczna "przestrzeń nazw" + + window.foo = function() { + // wyeksponowane domkniecie (closure) + }; + + })(); // natychmiastowe wykonanie funkcji + +Nienazwane funkcje są rozpoznane jako [wyrażenia](#function.general), więc +aby mogły zostać wywołane muszą zostać zewaluowane. + + ( // zewaluowanie fukcji znajdującej się wewnątrz nawiasów + function() {} + ) // zwrócenie obiektu funkcji + () // wywołanie rezultatu ewaluacji + +Istnieją inne sposoby aby zewaluować i wykonać wyrażenie funkcyjne. Mimo, że +mają inną składnie, zachowują się dokładnie tak samo. + + // Trzy inne sposoby + !function(){}(); + +function(){}(); + (function(){}()); + +### Wnioski + +Zaleca się aby zawsze używać *anonimowych wrapperów* do hermetyzacji kodu wewnątrz +jego własnej przestrzeni nazw. To nie tylko chroni kod przed kolizją nazw, ale +również wprowadza lepszą modularyzację programów. + +Ponadto, stosowanie zmiennych globalnych jest uznawane za złą praktykę. +Jakiekolwiek wykorzystanie zmiennych globalnych wskazuje na źle napisany kod, +który jest podatny na błędy i trudny do utrzymania. + From ffba8be017bbf218680f9bd6048eb1579f1b3a13 Mon Sep 17 00:00:00 2001 From: Lukasz Kufel Date: Thu, 5 May 2011 15:51:55 +0200 Subject: [PATCH 181/641] Polish translation of core. --- doc/pl/core/eval.md | 49 ++++++++++++++++ doc/pl/core/semicolon.md | 117 +++++++++++++++++++++++++++++++++++++++ doc/pl/core/undefined.md | 70 +++++++++++++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 doc/pl/core/eval.md create mode 100644 doc/pl/core/semicolon.md create mode 100644 doc/pl/core/undefined.md diff --git a/doc/pl/core/eval.md b/doc/pl/core/eval.md new file mode 100644 index 00000000..6b66b37c --- /dev/null +++ b/doc/pl/core/eval.md @@ -0,0 +1,49 @@ +## Dlaczego nie należy używać `eval` + +Funkcja `eval` uruchomi podany string jako kod JavaScript w lokalnym zasięgu (scopie). + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +Niestaty `eval` zostanie wykonana w lokalnym zasięgu tylko jeżeli została wywołana +**bezpośrednio** *i* nazwa wołanej funkcji równa sie `eval`. + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +Należy unikać stosowania `eval` **o ile to tylko możliwe**. W 99.9% przypadków można +osiągnąć ten sam efekt **nie** używając `eval`. + +### `eval` w przebraniu + +[Funkcje wykonywane po upływie czasu](#other.timeouts) `setTimeout` i `setInterval` +mogą przyjąć string jako pierwszy argument. String ten zostanie **zawsze** wykonany +w globalnym zasięgu, ponieważ funkcja `eval` zostanie wywołana niebezpośrednio w tym +przypadku. + +### Problemy z bezpieczeństwem + +Funkcja `eval` jest również problematyczna od strony bezpieczeństwa, ponieważ +wykonuje **każdy** kod, który zostanie do niej przekazany i nie należy **nigdy** +używać jej na stringach nieznanego lub niezaufanego pochodzenia. + +### Wnioski + +Funkcja `eval` nie powinna być w ogole używana, każdy kod, który ją wykorzystuje +powinien zostać sprawdzony pod względem działania, wydajności i bezpieczeństwa. +W przypadku gdy użycie `eval` jest niezbędne do działania, wówczas taki kod +należy przemyśleć raz jeszcze i *ulepszyć* kod aby nie wymagał użycia `eval`. + diff --git a/doc/pl/core/semicolon.md b/doc/pl/core/semicolon.md new file mode 100644 index 00000000..6dcaf9cb --- /dev/null +++ b/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. + +Lecz JavaScript nie jest językiem bez średników, tak na prawdę potrzebuje +średników aby zinterpretować kod źródłowy. Jednakże parser JavaScript +**automatycznie** wstawia średniki o ile napotka błąd parsowania związany z +brakiem średnika. + + var foo = function() { + } // błąd parsowania, oczekiwany był w tym miejscu średnik + test() + +Parser dodaje średnik, i próbuje jeszcze raz sparsować skrypt. + + var foo = function() { + }; // bez błędu parser kontynuuje + test() + +Automatyczne wstawianie średników jest uważane za jeden z **największych** błędów +konstrukcji języka, ponieważ *może* ono zachowanie kodu. + +### Jak działa wstawianie + +Kod poniżej nie ma żadnych średników, więc parser zdecyduje, w których miejscach +je wstawi. + + (function(window, undefined) { + function test(options) { + log('testing!') + + (options.list || []).forEach(function(i) { + + }) + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ) + + return + { + foo: function() {} + } + } + window.test = test + + })(window) + + (function(window) { + window.someLibrary = {} + + })(window) + +Poniżej znajduje się rezultat "zgadywania" parsera. + + (function(window, undefined) { + function test(options) { + + // Nie wstaniony średnik, linie zostały połączone + log('testing!')(options.list || []).forEach(function(i) { + + }); // <- wstawiony + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ); // <- wstawiony + + return; // <- wstawiony, psując deklarację return + { // potraktowane jako definicja bloku + + // etykieta oraz pojedyncze wyrażenie + foo: function() {} + }; // <- wstawiony + } + window.test = test; // <- wstawiony + + // Kolejna połączona linia + })(window)(function(window) { + window.someLibrary = {}; // <- wstawiony + + })(window); //<- wstawiony + +> **Uwaga:** Parser JavaScript nie potrafił "odpowiednio" zinterpretować +> deklaracji return, po którje 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) {}) + +Ten kod zostanie zmieniony w poniższą jedną linię. + + log('testing!')(options.list || []).forEach(function(i) {}) + +Jest **bardzo** prawdopodobne, że `log` **nie** zwróci fukcji, co za tym idzie +powyższy kod wyrzuci błąd `TypeError` oznajmując, że `undefined is not a +function` - `undefined` nie jest funkcją. + +### Wnioski + +Zaleca się aby **nigdy** nie pomijać średników, pozostawiać nawias otwierający +w tej samej linii co odpowiadająca mu definicja i nigdy nie pozostawiać deklaracji +`if` / `else` bez nawiasów nawet jeżeli są jednolinijkowe. Wszystkie te uwagi nie +tylko pomagają poprawić spójność kodu, ale również zapobiegają parser JavaScript +przed zmianą działania kod. + diff --git a/doc/pl/core/undefined.md b/doc/pl/core/undefined.md new file mode 100644 index 00000000..b938ead3 --- /dev/null +++ b/doc/pl/core/undefined.md @@ -0,0 +1,70 @@ +## `undefined` i `null` + +JavaScript ma dwie różne wartości dla `pustych` wartości, bardziej użyteczną +z tych dwóch jest `undefined`. + +### Wartość `undefined` + +`undefined` jest typem z dokładnie jedną wartością: `undefined`. + +Język również definiuje globalną zmienną, która ma wartość `undefined`, zmienna +ta jest nazwana `undefined`. Jednakże jest to zmienna a **nie** stała czy słowo +kluczowe w języku. Oznacza to że możliwe jest nadpisanie *wartości* tej zmiennej. + +> Uwaga ES55: `undefined` w ECMAScript 5 **nie będzie już** *nadpisywalna* w trybie +> strict mode, ale jej nazwa może zostać przysłonona przez na przykład funkcję o +> nazwie `undefined`. + +Kilka przykładów kiedy wartość `undefined` jest zwracana: + + - Dostęp do (niemodyfikowalnej) zmiennej globalnej `undefined`. + - Wyjście z funkcji, która nie ma deklaracji `return`. + - Deklaracja `return`, która nic jawnie nie zwraca. + - Poszukiwanie nieistniejącej właściwości. + - Parametr funkcji, który nie został jawnie przekazany podczas wywołania funkcji + - Wszystko co zostało ustawione na wartość `undefined` + +### Obsługa przypadku zmiany wartości `undefined` + +Ponieważ globalna zmienna `undeined` tylko zawiera kopię prawdziwej *wartości* typu +`undefined`, przypisanie nowej wartości do tej zmiennej **nie** zmienia wartości +*typu* `undefined`. + +Jednak, aby porównać coś do wartości `undefined` potrzebne jest odczytanie wartości +`undefined`. + +Aby uchronić swój kod przeciwko możliwemu nadpisaniu zmiennej `undefined`, korzysta +się z powszechnej techniki dodania dodatkowego parametru do +[anonimowego wrappera](#function.scopes), do którego nie zostanie przekazany +argument. + + var undefined = 123; + (function(something, foo, undefined) { + // undefined lokalnym zasięgu znowu + // odnosi się do poprawnej wartości + + })('Hello World', 42); + +Kolejnym sposobem aby osiągnąć ten sam efekt jest użycie deklaracji zmiennej +wewnątrz wrappera. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +Jedyną różnicą pomięcy tymi sposobami są dodatkowe 4 bajty przeznaczone na słowo +kluczowe `var` i spację po nim. + +### Zastosowanie `null` + +Podczas gdy `undefined` w kontekście języka jest używany jak *null* w sensie +tradycyjnych języków, to `null` w JavaScript (jako literał i jako typ) jest po +prostu kolejnym typem danych. + +Jest wykorzystywany we wnętrzu JavaScript (np. deklaracji końca łańcucha prototypów +poprzez ustawienie `Foo.prototype = null`), ale prawie w każdym przypadku można go +zastąpić przez `undefined`. + From e531742c68b989e31fbe7f31ffd313c387c212d1 Mon Sep 17 00:00:00 2001 From: Adeel Ahmad Khan Date: Thu, 5 May 2011 18:59:41 -0700 Subject: [PATCH 182/641] minor --- doc/ru/intro/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ru/intro/index.md b/doc/ru/intro/index.md index fa7c6636..9debe341 100644 --- a/doc/ru/intro/index.md +++ b/doc/ru/intro/index.md @@ -1,6 +1,6 @@ ## Вступление -**JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам языка JavaScript. В ней вы найдёте советы о том, как избежать распространённых ошибок и предсказать появление тех или иных багов. В документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми, продираясь к глубинам языка, могут столкнуться даже просвещённые JavaScript-программисты. +**JavaScript Гарден** — это постоянно обновляющаяся и растущая документация по самым заковыристым темам языка JavaScript. В ней вы найдёте советы о том, как избежать распространённых ошибок и предсказать появление тех или иных багов. В документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми, продираясь к глубинам языка, могут столкнуться даже просвещённые JavaScript-программисты. JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Вам понадобится реальный опыт работы с языком чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста внимательно ознакомьтесь с замечательным [руководством][1] на сайте Mozilla Developer Network. From 357897bd21cdc1ea6b4b595ca10d6a3557574dc3 Mon Sep 17 00:00:00 2001 From: Lukasz Kufel Date: Fri, 6 May 2011 10:37:39 +0200 Subject: [PATCH 183/641] Polish translation of other section. --- doc/pl/full_index.json | 74 ------------------ doc/pl/index.json | 16 ++++ doc/pl/other/timeouts.md | 160 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 74 deletions(-) delete mode 100644 doc/pl/full_index.json create mode 100644 doc/pl/other/timeouts.md diff --git a/doc/pl/full_index.json b/doc/pl/full_index.json deleted file mode 100644 index 16b65ffb..00000000 --- a/doc/pl/full_index.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "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/doc/pl/index.json b/doc/pl/index.json index 5e26527b..16b65ffb 100644 --- a/doc/pl/index.json +++ b/doc/pl/index.json @@ -53,6 +53,22 @@ "instanceof", "casting" ] + }, + { + "title": "Jądro", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon" + ] + }, + { + "title": "Inne", + "dir": "other", + "articles": [ + "timeouts" + ] } ] } diff --git a/doc/pl/other/timeouts.md b/doc/pl/other/timeouts.md new file mode 100644 index 00000000..bf2ad512 --- /dev/null +++ b/doc/pl/other/timeouts.md @@ -0,0 +1,160 @@ +### `setTimeout` i `setInterval` + +Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania +funkcji korzystając z funkcji `setTimeout` i `setInterval`. +Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the `setTimeout` and `setInterval` functions. + +> **Note:** Funkcje czasowe nie są częścią standardu ECMAScript. Jest to część +> standardu [DOM][1]. + + function foo() {} + var id = setTimeout(foo, 1000); // zwraca licznę typu Number > 0 + +Powyższe wywołanie `setTimeout` zwraca ID budzika i planuje wywołanie `foo` za +**około** tysiąc milisekund. `foo` zostanie wykonana dokładnie **jeden raz**. + +**Nie ma pewności**, że kod zaplanowany do wykonania wykona się dokładnie po +upłynięciu zadanego czasu podanego jako parametr do `setTimeout`, ponieważ zależy +to od dokładności zegara w silniku JavaScript, który wykonuje kod oraz od tego, +że inny kawałek kodu może zablokować wątek, ponieważ JavaScript jest tylko +jedno wątkowy. + +Funkcja, która została przekazana jako pierwszy parametr zostanie wykonana w +globalnym zasięgu, co oznacza, że [`this`](#function.this) wewnątrz tej funkcji +będzie wkazywać na obiekt *global*. + + function Foo() { + this.value = 42; + this.method = function() { + // this wskazuje na obiekt global + console.log(this.value); // wypisze undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + +> **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 kilkakrotnie. W szczególności może się +to wydarzyć przy krótkim interwale. + + function foo(){ + // coś co blokuje wykonanie na 1 sekundę + } + setInterval(foo, 100); + +W powyższym kodzie kod `foo` zostanie wywołany tylko raz i zablokuje wywołanie na +jedną sekundę. + +Podczas, gdy funkcja `foo` blokuje wykonanie `setInterval` będzie planować kolejne +wywołania `foo`. W momencie, gdy pierwsze wywołanie `foo` się zakończy, już +w kolejce do wywołania będą czekały kolejne **dziesięć** wywołań tej funkcji. + +### Radzenie sobie z możliwymi blokadami + +Najprostrzym jak również najbardziej kontrolowaną sytuacją jest użycie `setTimeout` +wewnątrz wywoływanej funkcji. + + function foo(){ + // coś co blokuje wykonanie na 1 sekundę + setTimeout(foo, 100); + } + foo(); + +Powyższy kod nie tylko hermetyzuje wywołanie `setTimeout` ale również zapobiega +kolejkowaniu wywołań fukcji i daje dodatkową kontrolę. W tym przypadku funkcja +`foo` może zdecydować czy powinna się wywołać ponownie czy też nie. + +### Ręczne usuwanie budzików + +Usuwanie budzików i interwałów dokonywane jest przez przekazanie odpowiedniego ID +do `clearTimeout` lub `clearInterval`, w zależności z jakiej funkcji zostało +zwrócone ID. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Usuwanie wszystkich budzików + +Ponieważ nie istnieje wbudowana metoda na usunięcie wszystkich budzików i/lub +interwałów, konieczne jest użycie metody brute force aby osiągnąć ten efekt. + + // usunięcie "wszystkich" budzików + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +Nadal może istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała, +ponieważ ID było z innego przedziału, dlatego zamiast korzystania z metody brute +force, zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć. + +### Ukryte wykorzystanie `eval` + +Do `setTimeout` i `setInterval` można również przekazać string jako pierwszy +parametr zamiast obiektu funkcji, jednakże **nigdy** nie należy korzystać z tej +możliwości, ponieważ wewnętrznie `setTimeout` i `setInterval` wykorzystują `eval`. + +> **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 [wprost](#core.eval) w tym przypadku, to +string przekazany do `setTimeout` zostanie uruchomiony w *zasięgu globalnym*, +co za tym idzie lokalna zmienna `foo` z zasięgu `bar` nie zostanie użyta. + +Kolejnym zaleceniem jest aby **nie** stosować stringów do przekazywania argumentów +do funkcji, która ma zostać wywołana przez budzik. + + function foo(a, b, c) {} + + // NIGDY nie należy tak robić + setTimeout('foo(1,2, 3)', 1000) + + // Zamiast tego należy skorzystać z anonimowej funkcji + setTimeout(function() { + foo(a, b, c); + }, 1000) + +>**Uwaga:** Mimo, że możliwe jest wykorzystanie składni +> `setTimeout(foo, 1000, a, b, c)`, to nie zaleca się korzystania z niej, ponieważ +> może to prowadzić do subtelnych błędów podczas wykorzystania [metod](#function.this). + +### Wnioski + +Nie należy **nigdy** przekazywać stringu jako parametru do `setTimeout` lub +`setInterval`. Jest to wyraźną oznaką **bardzo** złego kodu, jeżeli potrzebne jest +przekazanie argumentów do funkcji należy skorzystać z *anonimowej funkcji* i +wewnątrz niej dokonać przekazania argumentów. + +Ponadto, należy unikać korzystanie z `setInterval`, ponieważ planista może +zablokować wykonanie JavaScriptu. + +[1]: http://pl.wikipedia.org/wiki/Obiektowy_model_dokumentu "Document Object Model" + From d2c4c0f76f8b0deb36cb3ed139c1dc15d2eb946b Mon Sep 17 00:00:00 2001 From: Lukasz Kufel Date: Fri, 6 May 2011 11:18:08 +0200 Subject: [PATCH 184/641] Small layout fixes. --- doc/pl/function/arguments.md | 2 +- doc/pl/function/constructors.md | 2 +- doc/pl/function/general.md | 3 ++- doc/pl/object/hasownproperty.md | 3 ++- doc/pl/types/casting.md | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/pl/function/arguments.md b/doc/pl/function/arguments.md index 1b56e10a..9f5d7476 100644 --- a/doc/pl/function/arguments.md +++ b/doc/pl/function/arguments.md @@ -1,4 +1,4 @@ -## obiekt `arguments` +## Obiekt `arguments` Każda zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej `arguments`. Ta zmienna trzyma listę wszystkich argumentów przekazanych do funkcji. diff --git a/doc/pl/function/constructors.md b/doc/pl/function/constructors.md index 1654f4bb..4cb7bd12 100644 --- a/doc/pl/function/constructors.md +++ b/doc/pl/function/constructors.md @@ -86,7 +86,7 @@ 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 używając fabryk +### Tworzenie nowych obiektów korzystając z fabryk Często zaleca się **nie** korzystać z operatora `new` ponieważ zapominając go zastosować może prowadzić do błędów. diff --git a/doc/pl/function/general.md b/doc/pl/function/general.md index 821682ed..d55153c3 100644 --- a/doc/pl/function/general.md +++ b/doc/pl/function/general.md @@ -13,7 +13,8 @@ Powyższa funkcja zostaje [wyniesiona](#function.scopes) zanim program wystartuj 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 + foo(); // Działa ponieważ definicja funkcji została wyniesiona + // na początek zasięgu przed uruchomieniem kodu function foo() {} ### Wyrażenie funkcyjne diff --git a/doc/pl/object/hasownproperty.md b/doc/pl/object/hasownproperty.md index 9c1775c0..5c3429e6 100644 --- a/doc/pl/object/hasownproperty.md +++ b/doc/pl/object/hasownproperty.md @@ -40,7 +40,8 @@ możliwość, że obiekt może posiadać tak nazwaną właściwość. Konieczne foo.hasOwnProperty('bar'); // zawsze zwraca false - // Została użyta metoda innego obiektu i wywołana z konkekstem `this` ustawionym na foo + // Została użyta metoda innego obiektu i wywołana z konkekstem + // `this` ustawionym na foo ({}).hasOwnProperty.call(foo, 'bar'); // true ### Wnioski diff --git a/doc/pl/types/casting.md b/doc/pl/types/casting.md index ae0247f4..5e358c63 100644 --- a/doc/pl/types/casting.md +++ b/doc/pl/types/casting.md @@ -33,7 +33,7 @@ inaczej jeżeli są poprzedzone słowem kluczowym `new` a inaczej jeżeli nie s new Number(10) === 10; // False, Object i Number Number(10) === 10; // True, Number i Number - new Number(10) + 0 === 10; // True, ponieważ dokonana została jawna konwersja + new Number(10) + 0 === 10; // True, ponieważ dokonano jawnej konwersji Korzystanie z wbudowanych typów jak `Number` jako konstruktor utworzy nowy obiekt typu `Number`, natomiast opuszczenie słowa kluczowego `new` spowoduje, że funkcja From 5ef7ea516b63e405895f45f2e97f101a3a200901 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 17 May 2011 01:17:16 +0300 Subject: [PATCH 185/641] Fixed a little typo. --- doc/tr/intro/index.md | 7 +++---- doc/tr/object/general.md | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/tr/intro/index.md b/doc/tr/intro/index.md index e6f1fce6..5c39210a 100644 --- a/doc/tr/intro/index.md +++ b/doc/tr/intro/index.md @@ -2,14 +2,13 @@ **JavaScript Garden** JavaScript programlama dilinin acayiplikleri üzerine derlenmiş bir döküman koleksiyonudur. Henüz ustalaşmamış JavaScript -programcılarının sıkça yaptığı yanlışlar, dile has ince hatalar ve performans +programcılarının sıkça yaptığı yanlışlar, dile has incelikler ve performans sorunlarına karşı tavsiyeler içerir. JavaScript Garden'ın amacı size JavaScript öğretmek **değildir**. Bu rehberde anlatılan konuları anlamak için JavaScript dilini önceden biliyor olmanız -şiddetle tavsiye edilir. Eğer JavaScript dilinin temellerini öğrenmek -istiyorsanız, lütfen Mozilla Programcı Ağı'nda bulunan mükemmel [rehbere][1] -başvurun. +gerekir. Eğer JavaScript dilinin temellerini öğrenmek istiyorsanız, lütfen +Mozilla Programcı Ağı'nda bulunan mükemmel [rehbere][1] başvurun. ## Yazarlar diff --git a/doc/tr/object/general.md b/doc/tr/object/general.md index 7cc81d7c..6107ca66 100644 --- a/doc/tr/object/general.md +++ b/doc/tr/object/general.md @@ -21,7 +21,7 @@ Bu hatayı aşıp sayı sabitlerinin de nesne olarak davranmasını sağlamak i uygulanabilecek bazı çözümler vardır. 2..toString(); // ikinci nokta doğru şekilde algılanır - 2 .toString(); // noktanın solundki boşluğa dikkat edin + 2 .toString(); // noktanın solundaki boşluğa dikkat edin (2).toString(); // ilk önce 2 değerlendirilir ### Bir veri türü olarak nesneler From 9d166aa72e08dc1cdeba8f5866c61ca976652597 Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 21:49:10 +0900 Subject: [PATCH 186/641] cp en --- doc/ja/array/constructor.md | 35 +++++ doc/ja/array/general.md | 58 ++++++++ doc/ja/core/eval.md | 48 +++++++ doc/ja/core/semicolon.md | 114 ++++++++++++++++ doc/ja/core/undefined.md | 72 ++++++++++ doc/ja/function/arguments.md | 119 ++++++++++++++++ doc/ja/function/closures.md | 98 ++++++++++++++ doc/ja/function/constructors.md | 128 ++++++++++++++++++ doc/ja/function/general.md | 48 +++++++ doc/ja/function/scopes.md | 231 ++++++++++++++++++++++++++++++++ doc/ja/function/this.md | 111 +++++++++++++++ doc/ja/index.json | 68 ++++++++++ doc/ja/intro/index.md | 47 +++++++ doc/ja/object/forinloop.md | 51 +++++++ doc/ja/object/general.md | 99 ++++++++++++++ doc/ja/object/hasownproperty.md | 53 ++++++++ doc/ja/object/prototype.md | 116 ++++++++++++++++ doc/ja/other/timeouts.md | 155 +++++++++++++++++++++ doc/ja/types/casting.md | 70 ++++++++++ doc/ja/types/equality.md | 71 ++++++++++ doc/ja/types/instanceof.md | 38 ++++++ doc/ja/types/typeof.md | 87 ++++++++++++ 22 files changed, 1917 insertions(+) create mode 100644 doc/ja/array/constructor.md create mode 100644 doc/ja/array/general.md create mode 100644 doc/ja/core/eval.md create mode 100644 doc/ja/core/semicolon.md create mode 100644 doc/ja/core/undefined.md create mode 100644 doc/ja/function/arguments.md create mode 100644 doc/ja/function/closures.md create mode 100644 doc/ja/function/constructors.md create mode 100644 doc/ja/function/general.md create mode 100644 doc/ja/function/scopes.md create mode 100644 doc/ja/function/this.md create mode 100644 doc/ja/index.json create mode 100644 doc/ja/intro/index.md create mode 100644 doc/ja/object/forinloop.md create mode 100644 doc/ja/object/general.md create mode 100644 doc/ja/object/hasownproperty.md create mode 100644 doc/ja/object/prototype.md create mode 100644 doc/ja/other/timeouts.md create mode 100644 doc/ja/types/casting.md create mode 100644 doc/ja/types/equality.md create mode 100644 doc/ja/types/instanceof.md create mode 100644 doc/ja/types/typeof.md diff --git a/doc/ja/array/constructor.md b/doc/ja/array/constructor.md new file mode 100644 index 00000000..763f55fb --- /dev/null +++ b/doc/ja/array/constructor.md @@ -0,0 +1,35 @@ +## The `Array` Constructor + +Since the `Array` constructor is ambiguous in how it deals with its parameters, +it is highly recommended to always use the array literals - `[]` notation - +when creating new arrays. + + [1, 2, 3]; // Result: [1, 2, 3] + new Array(1, 2, 3); // Result: [1, 2, 3] + + [3]; // Result: [3] + new Array(3); // Result: [] + new Array('3') // Result: ['3'] + +In cases when there is only one argument passed to the `Array` constructor, +and that argument is a `Number`, the constructor will return a new *sparse* +array with the `length` property set to the value of the argument. It should be +noted that **only** the `length` property of the new array will be set this way, +the actual indexes of the array will not be initialized. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, the index was not set + +The behavior of being able to set the length of the array upfront only comes in +handy in a few cases, like repeating a string, in which it avoids the use of a +`for loop` code. + + new Array(count + 1).join(stringToRepeat); + +### In Conclusion + +The use of the `Array` constructor should be avoided as much as possible. +Literals are definitely preferred. They are shorter and have a clearer syntax; +therefore, they also increase the readability of the code. + diff --git a/doc/ja/array/general.md b/doc/ja/array/general.md new file mode 100644 index 00000000..7c8f07ab --- /dev/null +++ b/doc/ja/array/general.md @@ -0,0 +1,58 @@ +## Array Iteration and Properties + +Although arrays in JavaScript are objects, there are no good reasons to use +the [`for in loop`](#object.forinloop) in for iteration on them. In fact there +are a number of good reasons **against** the use of `for in` on arrays. + +> **Note:** JavaScript arrays are **not** *associative arrays*. JavaScript only +> has [objects](#object.general) for mapping keys to values. And while associative +> arrays **preserve** order, objects **do not**. + +Since the `for in` loop enumerates all the properties that are on the prototype +chain and the only way to exclude those properties is to use +[`hasOwnProperty`](#object.hasownproperty), it is already up to **twenty times** +slower than a normal `for` loop. + +### Iteration + +In order to achieve the best performance when iterating over arrays, it is best +to use the classic `for` loop. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +There is one extra catch in the above example, that is the caching of the +length of the array via `l = list.length`. + +Although the `length` property is defined on the array itself, there is still an +overhead for doing the lookup on each iteration of the loop. And while recent +JavaScript engines **may** apply optimization in this case, there is no way of +telling whether the code will run on one of these newer engines or not. + +In fact, leaving out the caching may result in the loop being only **half as +fast** as with the cached length. + +### The `length` Property + +While the *getter* of the `length` property simply returns the number of +elements that are contained in the array, the *setter* can be used to +**truncate** the array. + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo; // [1, 2, 3] + +Assigning a smaller length does truncate the array, but increasing the length +does not have any effect on the array. + +### In Conclusion + +For the best performance it is recommended to always use the plain `for` loop +and cache the `length` property. The use of `for in` on an array is a sign of +badly written code that is prone to bugs and bad performance. + diff --git a/doc/ja/core/eval.md b/doc/ja/core/eval.md new file mode 100644 index 00000000..ecf7ba90 --- /dev/null +++ b/doc/ja/core/eval.md @@ -0,0 +1,48 @@ +## Why Not to Use `eval` + +The `eval` function will execute a string of JavaScript code in the local scope. + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +But `eval` only executes in local scope when it is being called **directly** *and* +the name of the called function is actually `eval`. + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +The use of `eval` should be avoided at **all costs**. 99.9% of its "uses" can be +achieved **without** it. + +### `eval` in Disguise + +The [timeout functions](#other.timeouts) `setTimeout` and `setInterval` can both +take a string as their first argument. This string will **always** get executed +in the global scope since `eval` is not being called directly in that case. + +### Security Issues + +`eval` also is a security problem as it executes **any** code given to it, +it should **never** be used with strings of unknown or untrusted origins. + +### In Conclusion + +`eval` should never be used, any code that makes use of it is to be questioned in +its workings, performance and security. In case something requires `eval` in +order to work, its design is to be questioned and should **not** be used in the +first place, a *better design* should be used, that does not require the use of +`eval`. + diff --git a/doc/ja/core/semicolon.md b/doc/ja/core/semicolon.md new file mode 100644 index 00000000..61d74fd9 --- /dev/null +++ b/doc/ja/core/semicolon.md @@ -0,0 +1,114 @@ +## Automatic Semicolon Insertion + +Although JavaScript has C style syntax, it does **not** enforce the use of +semicolons in the source code, it is possible to omit them. + +But JavaScript is not a semicolon-less language, it in fact needs the +semicolons in order to understand the source code. Therefore the JavaScript +parser **automatically** inserts them whenever it encounters a parse +error due to a missing semicolon. + + var foo = function() { + } // parse error, semicolon expected + test() + +Insertion happens, and the parser tries again. + + var foo = function() { + }; // no error, parser continues + test() + +The automatic insertion of semicolon is considered to be one of **biggest** +design flaws in the language, as it *can* change the behavior of code. + +### How it Works + +The code below has no semicolons in it, so it is up to the parser to decide where +to insert them. + + (function(window, undefined) { + function test(options) { + log('testing!') + + (options.list || []).forEach(function(i) { + + }) + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ) + + return + { + foo: function() {} + } + } + window.test = test + + })(window) + + (function(window) { + window.someLibrary = {} + + })(window) + +Below is the result of the parser's "guessing" game. + + (function(window, undefined) { + function test(options) { + + // Not inserted, lines got merged + log('testing!')(options.list || []).forEach(function(i) { + + }); // <- inserted + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ); // <- inserted + + return; // <- inserted, breaks the return statement + { // treated as a block + + // a label and a single expression statement + foo: function() {} + }; // <- inserted + } + window.test = test; // <- inserted + + // The lines got merged again + })(window)(function(window) { + window.someLibrary = {}; // <- inserted + + })(window); //<- inserted + +> **Note:** The JavaScript parser does not "correctly" handle return statements +> which are followed by a new line, while this is not neccessarily the fault of +> the automatic semicolon insertion, it can still be an unwanted side-effect. + +The parser drastically changed the behavior of the code above, in certain cases +it does the **wrong thing**. + +### Leading Parenthesis + +In case of a leading parenthesis, the parser will **not** insert a semicolon. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +This code gets transformed into one line. + + log('testing!')(options.list || []).forEach(function(i) {}) + +Chances are **very** high that `log` does **not** return a function; therefore, +the above will yield a `TypeError` stating that `undefined is not a function`. + +### In Conclusion + +It is highly recommended to **never** omit semicolons, it is also advocated to +keep braces on the same line with their corresponding statements and to never omit +them for one single-line `if` / `else` statements. Both of these measures will +not only improve the consistency of the code, they will also prevent the +JavaScript parser from changing its behavior. + diff --git a/doc/ja/core/undefined.md b/doc/ja/core/undefined.md new file mode 100644 index 00000000..b29da3b4 --- /dev/null +++ b/doc/ja/core/undefined.md @@ -0,0 +1,72 @@ +## `undefined` and `null` + +JavaScript has two distinct values for `nothing`, the more useful of these two +being `undefined`. + +### The Value `undefined` + +`undefined` is a type with exactly one value: `undefined`. + +The language also defines a global variable that has the value of `undefined`, +this variable is also called `undefined`. But this variable is **not** a constant, +nor is it a keyword of the language. This means that its *value* can be easily +overwritten. + +> **ES5 Note:** `undefined` in ECMAScript 5 is **no longer** *writable* in strict +> mode, but its name can still be shadowed by for example a function with the name +> `undefined`. + +Some examples for when the value `undefined` is returned: + + - Accessing the (unmodified) global variable `undefined`. + - Implicit returns of functions due to missing `return` statements. + - `return` statements which do not explicitly return anything. + - Lookups of non-existent properties. + - Function parameters which do not had any explicit value passed. + - Anything that has been set to the value of `undefined`. + +### Handling Changes to the Value of `undefined` + +Since the global variable `undefined` only holds a copy of the actual *value* of +`undefined`, assigning a new value to it does **not** change the value of the +*type* `undefined`. + +Still, in order to compare something against the value of `undefined` it is +necessary to retrieve the value of `undefined` first. + +In order to protect code against a possible overwritten `undefined` variable, a +common technique used is to add an additional parameter to an +[anonymous wrapper](#function.scopes), that gets no argument passed to it. + + var undefined = 123; + (function(something, foo, undefined) { + // undefined in the local scope does + // now again refer to the value + + })('Hello World', 42); + +Another way to achieve the same effect would be to use a declaration inside the +wrapper. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +The only difference being here, that this version results in 4 more bytes being +used in case it is minified and there is no other `var` statement inside the +anonymous wrapper. + +### Uses of `null` + +While `undefined` in the context of the JavaScript language is mostly used in +the sense of a traditional *null*, the actual `null` (both a literal and a type) +is more or less just another data type. + +It is used in some JavaScript internals (like declaring the end of the +prototype chain by setting `Foo.prototype = null`), but in almost all cases it +can be replaced by `undefined`. + + diff --git a/doc/ja/function/arguments.md b/doc/ja/function/arguments.md new file mode 100644 index 00000000..3d551ad5 --- /dev/null +++ b/doc/ja/function/arguments.md @@ -0,0 +1,119 @@ +## The `arguments` Object + +Every function scope in JavaScript can access the special variable `arguments`. +This variable holds a list of all the arguments that were passed to the function. + +> **Note:** In case `arguments` has already been defined inside the function's +> scope either via a `var` statement or being the name of a formal parameter, +> the `arguments` object will not be created. + +The `arguments` object is **not** an `Array`. While it has some of the +semantics of an array - namely the `length` property - it does not inherit from +`Array.prototype` and is in fact an `Object`. + +Due to this, it is **not** possible to use standard array methods like `push`, +`pop` or `slice` on `arguments`. While iteration with a plain `for` loop works +just fine, it is necessary to convert it to a real `Array` in order to use the +standard `Array` methods on it. + +### Converting to an Array + +The code below will return a new `Array` containing all the elements of the +`arguments` object. + + Array.prototype.slice.call(arguments); + +This conversion is **slow**, it is **not recommended** to use it in performance +critical sections of code. + +### Passing Arguments + +The following is the recommended way of passing arguments from one function to +another. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // do stuff here + } + +Another trick is to use both `call` and `apply` together to create fast, unbound +wrappers. + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Create an unbound version of "method" + // It takes the parameters: this, arg1, arg2...argN + Foo.method = function() { + + // Result: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +### Formal Parameters and Arguments Indices + +The `arguments` object creates *getter* and *setter* functions for both its +properties as well as the function's formal parameters. + +As a result, changing the value of a formal parameter will also change the value +of the corresponding property on the `arguments` object, and the other way around. + + function foo(a, b, c) { + arguments[0] = 2; + a; // 2 + + b = 4; + arguments[1]; // 4 + + var d = c; + d = 9; + c; // 3 + } + foo(1, 2, 3); + +### Performance Myths and Truths + +The `arguments` object is always created with the only two exceptions being the +cases where it is declared as a name inside of a function or one of its formal +parameters. It does not matter whether it is used or not. + +Both *getters* and *setters* are **always** created; thus, using it has nearly +no performance impact at all, especially not in real world code where there is +more than a simple access to the `arguments` object's properties. + +> **ES5 Note:** These *getters* and *setters* are not created in strict mode. + +However, there is one case which will drastically reduce the performance in +modern JavaScript engines. That case is the use of `arguments.callee`. + + function foo() { + arguments.callee; // do something with this function object + arguments.callee.caller; // and the calling function object + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // Would normally be inlined... + } + } + +In the above code, `foo` can no longer be a subject to [inlining][1] since it +needs to know about both itself and its caller. This not only defeats possible +performance gains that would arise from inlining, it also breaks encapsulation +since the function may now be dependent on a specific calling context. + +It is **highly recommended** to **never** make use of `arguments.callee` or any of +its properties. + +> **ES5 Note:** In strict mode, `arguments.callee` will throw a `TypeError` since +> its use has been deprecated. + +[1]: http://en.wikipedia.org/wiki/Inlining + + diff --git a/doc/ja/function/closures.md b/doc/ja/function/closures.md new file mode 100644 index 00000000..17554dcb --- /dev/null +++ b/doc/ja/function/closures.md @@ -0,0 +1,98 @@ +## Closures and References + +One of JavaScript's most powerful features is the availability of *closures*, +this means that scopes **always** keep access to the outer scope they were +defined in. Since the only scoping that JavaScript has is +[function scope](#function.scopes), all functions, by default, act as closures. + +### Emulating private variables + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Here, `Counter` returns **two** closures. The function `increment` as well as +the function `get`. Both of these functions keep a **reference** to the scope of +`Counter` and, therefore, always keep access to the `count` variable that was +defined in that very scope. + +### Why Private Variables Work + +Since it is not possible to reference or assign scopes in JavaScript, there is +**no** way of accessing the variable `count` from the outside. The only way to +interact with it is via the two closures. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +The above code will **not** change the variable `count` in the scope of `Counter`, +since `foo.hack` was not defined in **that** scope. It will instead create - or +override - the *global* variable `count`. + +### Closures Inside Loops + +One often made mistake is to use closures inside of loops, as if they were +copying the value of the loops index variable. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +The above will **not** output the numbers `0` through `9`, but will simply print +the number `10` ten times. + +The *anonymous* function keeps a **reference** to `i` and at the time +`console.log` gets called, the `for loop` has already finished and the value of +`i` as been set to `10`. + +In order to get the desired behavior, it is necessary to create a **copy** of +the value of `i`. + +### Avoiding the Reference Problem + +In order to copy the value of the loop's index variable, it is best to use an +[anonymous wrapper](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +The anonymous outer function gets called immediately with `i` as its first +argument and will receive a copy of the **value** of `i` as its parameter `e`. + +The anonymous function that gets passed to `setTimeout` now has a reference to +`e`, whose value does **not** get changed by the loop. + +There is another possible way of achieving this; that is to return a function +from the anonymous wrapper, that will then have the same behavior as the code +above. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + diff --git a/doc/ja/function/constructors.md b/doc/ja/function/constructors.md new file mode 100644 index 00000000..ad90b028 --- /dev/null +++ b/doc/ja/function/constructors.md @@ -0,0 +1,128 @@ +## Constructors + +Constructors in JavaScript are yet again different from many other languages. Any +function call that is preceded by the `new` keyword acts as a constructor. + +Inside the constructor - the called function - the value of `this` refers to a +newly created `Object`. The [`prototype`](#object.prototype) of this **new** +object is set to the `prototype` of the function object that was invoked as the +constructor. + +If the function that was called has no explicit `return` statement, then it +implicitly returns the value of `this` - the new object. + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +The above calls `Foo` as constructor and sets the `prototype` of the newly +created object to `Foo.prototype`. + +In case of an explicit `return` statement the function returns the value +specified that statement, **but only** if the return value is an `Object`. + + function Bar() { + return 2; + } + new Bar(); // a new object + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // the returned object + +When the `new` keyword is omitted, the function will **not** return a new object. + + function Foo() { + this.bla = 1; // gets set on the global object + } + Foo(); // undefined + +While the above example might still appear to work in some cases, due to the +workings of [`this`](#function.this) in JavaScript, it will use the +*global object* as the value of `this`. + +### Factories + +In order to be able to omit the `new` keyword, the constructor function has to +explicitly return a value. + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +Both calls to `Bar` return the exact same thing, a newly create object which +has a property called `method` on it, that is a +[Closure](#function.closures). + +It is also to note that the call `new Bar()` does **not** affect the prototype +of the returned object. While the prototype will be set on the newly created +object, `Bar` never returns that new object. + +In the above example, there is no functional difference between using and +not using the `new` keyword. + + +### Creating New Objects via Factories + +An often made recommendation is to **not** use `new` since forgetting its use +may lead to bugs. + +In order to create new object, one should rather use a factory and construct a +new object inside of that factory. + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +While the above is robust against a missing `new` keyword and certainly makes +the use of [private variables](#function.closures) easier, it comes with some +downsides. + + 1. It uses more memory since the created objects do **not** share the methods + on a prototype. + 2. In order to inherit the factory needs to copy all the methods from another + object or put that object on the prototype of the new object. + 3. Dropping the prototype chain just because of a left out `new` keyword + somehow goes against the spirit of the language. + +### In Conclusion + +While omitting the `new` keyword might lead to bugs, it is certainly **not** a +reason to drop the use of prototypes altogether. In the end it comes down to +which solution is better suited for the needs of the application, it is +especially important to choose a specific style of object creation **and stick** +with it. + diff --git a/doc/ja/function/general.md b/doc/ja/function/general.md new file mode 100644 index 00000000..64c9e0c9 --- /dev/null +++ b/doc/ja/function/general.md @@ -0,0 +1,48 @@ +## Function Declarations and Expressions + +Functions in JavaScript are first class objects. That means they can be +passed around like any other value. One common use of this feature is to pass +an *anonymous function* as a callback to another, possibly asynchronous function. + +### The `function` Declaration + + function foo() {} + +The above function gets [hoisted](#function.scopes) before the execution of the +program starts; thus, it is available *everywhere* in the scope it was *defined* +in, even if called before the actual definition in the source. + + foo(); // Works because foo was created before this code runs + function foo() {} + +### The `function` Expression + + var foo = function() {}; + +This example assigns the unnamed and *anonymous* function to the variable `foo`. + + foo; // 'undefined' + foo(); // this raises a TypeError + var foo = function() {}; + +Due to the fact that `var` is a declaration, that hoists the variable name `foo` +before the actual execution of the code starts, `foo` is already defined when +the script gets executed. + +But since assignments only happen at runtime, the value of `foo` will default +to [undefined](#core.undefined) before the corresponding code is executed. + +### Named Function Expression + +Another special case is the assignment of named functions. + + var foo = function bar() { + bar(); // Works + } + bar(); // ReferenceError + +Here `bar` is not available in the outer scope, since the function only gets +assigned to `foo`; however, inside of `bar` it is available. This is due to +how [name resolution](#function.scopes) in JavaScript works, the name of the +function is *always* made available in the local scope of the function itself. + diff --git a/doc/ja/function/scopes.md b/doc/ja/function/scopes.md new file mode 100644 index 00000000..7ae5e4f5 --- /dev/null +++ b/doc/ja/function/scopes.md @@ -0,0 +1,231 @@ +## Scopes and Namespaces + +Although JavaScript deals fine with the syntax of two matching curly +braces for blocks, it does **not** support block scope; hence, all that is left +is in the language is *function scope*. + + function test() { // a scope + for(var i = 0; i < 10; i++) { // not a scope + // count + } + console.log(i); // 10 + } + +> **Note:** When not used in an assignment, return statement or as a function +> argument, the `{...}` notation will get interpreted as a block statement and +> **not** as an object literal. This, in conjunction with +> [automatic insertion of semicolons](#core.semicolon), can lead to subtle errors. + +There are also no distinct namespaces in JavaScript, that means that everything +gets defined in one *globally shared* namespace. + +Each time a variable is referenced, JavaScript will traverse upwards through all +the scopes until it finds it. In the case that it reaches the global scope and +still has not found the requested name, it will raise a `ReferenceError`. + +### The Bane of Global Variables + + // script A + foo = '42'; + + // script B + var foo = '42' + +The above two scripts do **not** have the same effect. Script A defines a +variable called `foo` in the *global* scope and script B defines a `foo` in the +*current* scope. + +Again, that is **not** at all the *same effect*, not using `var` can have major +implications. + + // global scope + var foo = 42; + function test() { + // local scope + foo = 21; + } + test(); + foo; // 21 + +Leaving out the `var` statement inside the function `test` will override the +value of `foo`. While this might not seem like a big deal at first, having +thousands of lines of JavaScript and not using `var` will introduce horrible and +hard to track down bugs. + + // global scope + var items = [/* some list */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // scope of subLoop + for(i = 0; i < 10; i++) { // missing var statement + // do amazing stuff! + } + } + +The outer loop will terminate after the first call to `subLoop`, since `subLoop` +overwrites the global value of `i`. Using a `var` for the second `for` loop would +have easily avoided this error. The `var` statement should **never** be left out +unless the *desired effect* is to affect the outer scope. + +### Local Variables + +The only source for local variables in JavaScript are +[function](#function.general) parameters and variables that were declared via the +`var` statement. + + // global scope + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // local scope of the function test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +While `foo` and `i` are local variables inside the scope of the function `test`, +the assignment of `bar` will override the global variable with the same name. + +### Hoisting + +JavaScript **hoists** declarations. This means that both `var` statements and +`function` declarations will be moved to the top of their enclosing scope. + + bar(); + var bar = function() {}; + var someValue = 42; + + test(); + function test(data) { + if (false) { + goo = 1; + + } else { + var goo = 2; + } + for(var i = 0; i < 100; i++) { + var e = data[i]; + } + } + +The above code gets transformed before any execution is started. JavaScript moves +the `var` statements as well as the `function` declarations to the top of the +nearest surrounding scope. + + // var statements got moved here + var bar, someValue; // default to 'undefined' + + // the function declartion got moved up too + function test(data) { + var goo, i, e; // missing block scope moves these here + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // fails with a TypeError since bar is still 'undefined' + someValue = 42; // assignments are not affected by hoisting + bar = function() {}; + + test(); + +Missing block scoping will not only move `var` statements out of loops and +their bodies, it will also make the results of certain `if` constructs +non-intuitive. + +In the original code the `if` statement seemed to modify the *global +variable* `goo`, while actually it modifies the *local variable* - after hoisting +has been applied. + +Without the knowledge about *hoisting*, below code might seem to raise a +`ReferenceError`. + + // check whether SomeImportantThing has been initiliazed + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +But of course, the above works due to the fact that the `var` statement is being +moved to the top of the *global scope*. + + var SomeImportantThing; + + // other code might initiliaze SomeImportantThing here, or not + + // make sure it's there + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Name Resolution Order + +All scopes in JavaScript, including the *global scope*, have the special name +[`this`](#function.this) defined in them, which refers to the *current object*. + +Function scopes also have the name [`arguments`](#function.arguments) defined in +them which contains the arguments that were passed to a function. + +For example, when trying to access a variable named `foo` inside the scope of a +function, JavaScript will lookup the name in the following order: + + 1. In case there is a `var foo` statement in the current scope use that. + 2. If one of the function parameters is named `foo` use that. + 3. If the function itself is called `foo` use that. + 4. Go to the next outer scope and start with **#1** again. + +> **Note:** Having a parameter called `arguments` will **prevent** the creation +> of the default `arguments` object. + +### Namespaces + +A common problem of having only one global namespace is the likeliness of running +into problems where variable names clash. In JavaScript, this problem can +easily be avoided with the help of *anonymous wrappers*. + + (function() { + // a self contained "namespace" + + window.foo = function() { + // an exposed closure + }; + + })(); // execute the function immediately + + +Unnamed functions are considered [expressions](#function.general); so in order to +being callable, they must first be evaluated. + + ( // evaluate the function inside the paranthesis + function() {} + ) // and return the function object + () // call the result of the evaluation + +There are other ways for evaluating and calling the function expression; which, +while different in syntax, do behave the exact same way. + + // Two other ways + +function(){}(); + (function(){}()); + +### In Conclusion + +It is recommended to always use an *anonymous wrapper* for encapsulating code in +its own namespace. This does not only protect code against name clashes, it +also allows for better modularization of programs. + +Additionally, the use of global variables is considered **bad practice**. **Any** +use of them indicates badly written code that is prone to errors and hard to maintain. + diff --git a/doc/ja/function/this.md b/doc/ja/function/this.md new file mode 100644 index 00000000..85b11f0f --- /dev/null +++ b/doc/ja/function/this.md @@ -0,0 +1,111 @@ +## How `this` Works + +JavaScript has a different concept of what the special name `this` refers to +than most other programming languages do. There are exactly **five** different +ways in which the value of `this` can be bound in the language. + +### The Global Scope + + this; + +When using `this` in global scope, it will simply refer to the *global* object. + + +### Calling a Function + + foo(); + +Here `this` will again refer to the *global* object. + +> **ES5 Note:** In strict mode, the global case **no longer** exists. +> `this` will instead have the value of `undefined` in that case. + +### Calling a Method + + test.foo(); + +In this example `this` will refer to `test`. + +### Calling a Constructor + + new foo(); + +A function call that is preceded by the `new` keyword acts as +a [constructor](#function.constructors). Inside the function `this` will refer +to a *newly created* `Object`. + +### Explicit Setting of `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // array will expand to the below + foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3 + +When using the `call` or `apply` methods of `Function.prototype`, the value of +`this` inside the called function gets **explicitly set** to the first argument +of the corresponding function call. + +As a result, the above example the *method case* does **not** apply, and `this` +inside of `foo` will be set to `bar`. + +> **Note:** `this` **cannot** be used to refer to the object inside of an `Object` +> literal. So `var obj = {me: this}` will **not** result in `me` referring to +> `obj`, since `this` only gets bound by one of the five listed cases. + +### Common Pitfalls + +While most of these cases make sense, the first one is to be considered another +mis-design of the language, as it **never** has any practical use. + + Foo.method = function() { + function test() { + // this is set to the global object + } + test(); + } + +A common misconception is that `this` inside of `test` refers to `Foo`, while in +fact it **does not**. + +In order to gain access to `Foo` from within `test` it is necessary to create a +local variable inside of `method` which refers to `Foo`. + + Foo.method = function() { + var that = this; + function test() { + // Use that instead of this here + } + test(); + } + +`that` is just a normal variable name, but it is commonly used for the reference to an +outer `this`. In combination with [closures](#function.closures), it can also +be used to pass `this` values around. + +### Assigning Methods + +Another thing that does **not** work in JavaScript is function aliasing, that is, +**assigning** a method to a variable. + + var test = someObject.methodTest; + test(); + +Due to the first case `test` now acts like a plain function call; therefore, +`this` inside it will no longer refer to `someObject`. + +While the late binding of `this` might seem like a bad idea at first, it is in +fact what makes [prototypal inheritance](#object.prototype) work. + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +When `method` gets called on a instance of `Bar`, `this` will now refer to that +very instance. + + diff --git a/doc/ja/index.json b/doc/ja/index.json new file mode 100644 index 00000000..d4012ed9 --- /dev/null +++ b/doc/ja/index.json @@ -0,0 +1,68 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden in English", + "description": "A Guide to JavaScript's Quirks and Flaws.", + "sections": [ + { + "title": "Intro", + "dir": "intro", + "articles": [] + }, + { + "title": "Objects", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "Functions", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Arrays", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "Types", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "Core", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon" + ] + }, + { + "title": "Other", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/doc/ja/intro/index.md b/doc/ja/intro/index.md new file mode 100644 index 00000000..6ddd7c71 --- /dev/null +++ b/doc/ja/intro/index.md @@ -0,0 +1,47 @@ +## Intro + +**JavaScript Garden** is a growing collection of documentation about the most +quirky parts of the JavaScript programming language. It gives advice to +avoid common mistakes, subtle bugs, as well as performance issues and bad +practices that non-expert JavaScript programmers may encounter on their +endeavours into the depths of the language. + +JavaScript Garden does **not** aim to teach you JavaScript. Former knowledge +of the language is strongly recommended in order to understand the topics covered +in this guide. In order to learn the basics of the language, please head over to +the excellent [guide][1] on the Mozilla Developer Network. + +## The Authors + +This guide is the work of two lovely [Stack Overflow][2] users, [Ivo Wetzel][3] +(Writing) and [Zhang Yi Jiang][4] (Design). + +## Contributors + + - [Caio Romão][5] (Spelling corrections) + - [Andreas Blixt][6] (Language corrections) + +## Hosting + +JavaScript Garden is hosted on GitHub, but [Cramer Development][7] supports us +with a mirror at [JavaScriptGarden.info][8]. + +## License + +JavaScript Garden is published under the [MIT license][9] and hosted on +[GitHub][10]. If you find errors or typos please [file an issue][11] or a pull +request on the repository. You can also find us in the [JavaScript room][12] on +Stack Overflow chat. + +[1]: https://developer.mozilla.org/en/JavaScript/Guide +[2]: http://stackoverflow.com/ +[3]: http://stackoverflow.com/users/170224/ivo-wetzel +[4]: http://stackoverflow.com/users/313758/yi-jiang +[5]: https://github.com/caio +[6]: https://github.com/blixt +[7]: http://cramerdev.com/ +[8]: http://javascriptgarden.info/ +[9]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE +[10]: https://github.com/BonsaiDen/JavaScript-Garden +[11]: https://github.com/BonsaiDen/JavaScript-Garden/issues +[12]: http://chat.stackoverflow.com/rooms/17/javascript diff --git a/doc/ja/object/forinloop.md b/doc/ja/object/forinloop.md new file mode 100644 index 00000000..30751ed9 --- /dev/null +++ b/doc/ja/object/forinloop.md @@ -0,0 +1,51 @@ +## The `for in` Loop + +Just like the `in` operator, the `for in` loop also traverses the prototype +chain when iterating over the properties of an object. + +> **Note:** The `for in` loop will **not** iterate over any properties that +> have their `enumerable` attribute set to `false`; for example, the `length` +> property of an array. + + // Poisoning Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // prints both bar and moo + } + +Since it is not possible to change the behavior of the `for in` loop itself, it +is necessary to filter out the unwanted properties inside the loop body , +this is done by using the [`hasOwnProperty`](#object.hasownproperty) method of +`Object.prototype`. + +> **Note:** Since the `for in` always traverses the complete prototype chain, it +> will get slower with each additional layer of inheritance added to an object. + +### Using `hasOwnProperty` for Filtering + + // still the foo from above + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +This version is the only correct one to use. Due to the use of `hasOwnProperty` it +will **only** print out `moo`. When `hasOwnProperty` is left out, the code is +prone to errors in cases where the native prototypes - e.g. `Object.prototype` - +have been extended. + +One widely used framework which does this is [Prototype][1]. When this +framework is included, `for in` loops that do not use `hasOwnProperty` are +guaranteed to break. + +### In Conclusion + +It is recommended to **always** use `hasOwnProperty`. Never should any +assumptions be made about the environment the code is running in, or whether the +native prototypes have been extended or not. + +[1]: http://www.prototypejs.org/ + diff --git a/doc/ja/object/general.md b/doc/ja/object/general.md new file mode 100644 index 00000000..cbad5f11 --- /dev/null +++ b/doc/ja/object/general.md @@ -0,0 +1,99 @@ +## Object Usage and Properties + +Everything in JavaScript acts like an object, with the only two exceptions being +[`null`](#core.undefined) and [`undefined`](#core.undefined). + + false.toString() // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +A common misconception is that number literals cannot be used as +objects. That is because a flaw in JavaScript's parser tries to parse the *dot +notation* on a number as a floating point literal. + + 2.toString(); // raises SyntaxError + +There are a couple of workarounds which can be used in order make number +literals act as objects too. + + 2..toString(); // the second point is correctly recognized + 2 .toString(); // note the space left to the dot + (2).toString(); // 2 is evaluated first + +### Objects as a Data Type + +Objects in JavaScript can also be used as a [*Hashmap*][1], they mainly consist +of named properties mapping to values. + +Using a object literal - `{}` notation - it is possible to create a +plain object. This new object [inherits](#object.prototype) from `Object.prototype` and +has no [own properties](#object.hasownproperty) defined on it. + + var foo = {}; // a new empty object + + // a new object with a property called 'test' with value 12 + var bar = {test: 12}; + +### Accessing Properties + +The properties of an object can be accessed in two ways, via either the dot +notation, or the square bracket notation. + + var foo = {name: 'Kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // works + +Both notations are identical in their workings, with the only difference being that +the square bracket notation allows for dynamic setting of properties, as well as +the use of property names that would otherwise lead to a syntax error. + +### Deleting Properties + +The only way to actually remove a property from an object is to use the `delete` +operator; setting the property to `undefined` or `null` only remove the +*value* associated with the property, but not the *key*. + + var obj = { + bar: 1, + foo: 2, + baz: 3 + }; + obj.bar = undefined; + obj.foo = null; + delete obj.baz; + + for(var i in obj) { + if (obj.hasOwnProperty(i)) { + console.log(i, '' + obj[i]); + } + } + +The above outputs both `bar undefined` and `foo null` - only `baz` was +removed and is therefore missing from the output. + +### Notation of Keys + + var test = { + 'case': 'I am a keyword so I must be notated as a string', + delete: 'I am a keyword too so me' // raises SyntaxError + }; + +Object properties can be both notated as plain characters and as strings. Due to +another mis-design in JavaScript's parser, the above will throw +a `SyntaxError` prior to ECMAScript 5. + +This error arises from the fact that `delete` is a *keyword*; therefore, it must be +notated as a *string literal* to ensure that it will be correctly interpreted by +older JavaScript engines. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/doc/ja/object/hasownproperty.md b/doc/ja/object/hasownproperty.md new file mode 100644 index 00000000..319a8801 --- /dev/null +++ b/doc/ja/object/hasownproperty.md @@ -0,0 +1,53 @@ +## `hasOwnProperty` + +In order to check whether a object has a property defined on *itself* and **not** +somewhere on its [prototype chain](#object.prototype), it is necessary to use the +`hasOwnProperty` method which all objects inherit from `Object.prototype`. + +> **Note:** It is **not** enough to check whether a property is `undefined`. The +> property might very well exist, but its value just happens to be set to +> `undefined`. + +`hasOwnProperty` is the only thing in JavaScript which deals with properties and +does **not** traverse the prototype chain. + + // Poisoning Object.prototype + Object.prototype.bar = 1; + var foo = {goo: undefined}; + + foo.bar; // 1 + 'bar' in foo; // true + + foo.hasOwnProperty('bar'); // false + foo.hasOwnProperty('goo'); // true + +Only `hasOwnProperty` will give the correct and expected result, this is +essential when iterating over the properties of any object. There is **no** other +way to exclude properties that are not defined on the object *itself*, but +somewhere on its prototype chain. + +### `hasOwnProperty` as a Property + +JavaScript does **not** protect the property name `hasOwnProperty`; thus, if the +possibility exists that an object might have a property with this name, it is +necessary to use an *external* `hasOwnProperty` in order to get correct results. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // always returns false + + // Use another Object's hasOwnProperty and call it with 'this' set to foo + ({}).hasOwnProperty.call(foo, 'bar'); // true + +### In Conclusion + +When checking for the existence of a property on a object, `hasOwnProperty` is +the **only** method of doing so. It is also recommended to make `hasOwnProperty` +part of **every** [`for in` loop](#object.forinloop), this will avoid errors from +extended native [prototypes](#object.prototype). + diff --git a/doc/ja/object/prototype.md b/doc/ja/object/prototype.md new file mode 100644 index 00000000..f780eba2 --- /dev/null +++ b/doc/ja/object/prototype.md @@ -0,0 +1,116 @@ +## The Prototype + +JavaScript does not feature a classical inheritance model, instead it uses a +*prototypal* one. + +While this is often considered to be one of JavaScript's weaknesses, the +prototypal inheritance model is in fact more powerful than the classic model. +It is for example fairly trivial to build a classic model on top of it, while the +other way around is a far more difficult task. + +Due to the fact that JavaScript is basically the only widely used language that +features prototypal inheritance, it takes some time to adjust to the +differences between the two models. + +The first major difference is that inheritance in JavaScript is done by using so +called *prototype chains*. + +> **Note:** Simply using `Bar.prototype = Foo.prototype` will result in both objects +> sharing the **same** prototype. Therefore, changes to either object's prototype +> will affect the prototype of the other as well, which in most cases is not the +> desired effect. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Set Bar's prototype to a new instance of Foo + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Make sure to list Bar as the actual constructor + Bar.prototype.constructor = Bar; + + var test = new Bar() // create a new bar instance + + // The resulting prototype chain + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World' } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +In the above, the object `test` will inherit from both `Bar.prototype` and +`Foo.prototype`; hence, it will have access to the function `method` that was +defined on `Foo`. It will also have access to the property `value` of the +**one** `Foo` instance that is its prototype. It is important to note that `new +Bar()` does **not** create a new `Foo` instance, but reuses the one assigned to +its prototype; thus, all `Bar` instances will share the **same** `value` property. + +> **Note:** Do **not** use `Bar.prototype = Foo`, since it will not point to +> the prototype of `Foo` but rather to the function object `Foo`. So the +> prototype chain will go over `Function.prototype` and not `Foo.prototype`; +> therefore, `method` will not be on the prototype chain. + +### Property Lookup + +When accessing the properties of an object, JavaScript will traverse the +prototype chain **upwards** until it finds a property with the requested name. + +When it reaches the top of the chain - namely `Object.prototype` - and still +hasn't found the specified property, it will return the value +[undefined](#core.undefined) instead. + +### The Prototype Property + +While the prototype property is used by the language to build the prototype +chains, it is still possible to assign **any** given value to it. Although +primitives will simply get ignored when assigned as a prototype. + + function Foo() {} + Foo.prototype = 1; // no effect + +Assigning objects, as shown in the example above, will work, and allows for dynamic +creation of prototype chains. + +### Performance + +The lookup time for properties that are high up on the prototype chain can have a +negative impact on performance critical sections of code. Additionally, trying to +access non-existent properties will always traverse the full prototype chain. + +Also, when [iterating](#object.forinloop) over the properties of an object +**every** property that is on the prototype chain will get enumerated. + +### Extension of Native Prototypes + +One mis-feature that is often used is to extend `Object.prototype` or one of the +other built in prototypes. + +This technique is called [monkey patching][1] and breaks *encapsulation*. While +used by widely spread frameworks such as [Prototype][2], there is still no good +reason for cluttering built-in types with additional *non-standard* functionality. + +The **only** good reason for extending a built-in prototype is to backport +the features of newer JavaScript engines; for example, +[`Array.forEach`][3]. + +### In Conclusion + +It is a **must** to understand the prototypal inheritance model completely +before writing complex code which makes use of it. Also, watch the length of +the prototype chains and break them up if necessary to avoid possible +performance issues. Further, the native prototypes should **never** be extended +unless it is for the sake of compatibility with newer JavaScript features. + +[1]: http://en.wikipedia.org/wiki/Monkey_patch +[2]: http://prototypejs.org/ +[3]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach + diff --git a/doc/ja/other/timeouts.md b/doc/ja/other/timeouts.md new file mode 100644 index 00000000..d196a3b7 --- /dev/null +++ b/doc/ja/other/timeouts.md @@ -0,0 +1,155 @@ +### `setTimeout` and `setInterval` + +Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the `setTimeout` and `setInterval` functions. + +> **Note:** Timeouts are **not** part of the ECMAScript Standard. They are +> implemented as part of the [DOM][1]. + + function foo() {} + var id = setTimeout(foo, 1000); // returns a Number > 0 + +When `setTimeout` gets called, it will return the ID of the timeout and schedule +`foo` to run in **approximately** one thousand milliseconds in the future. +`foo` will then get executed exactly **once**. + +Depending on the timer resolution of the JavaScript engine that is running the +code, as well as the fact that JavaScript is single threaded and other code that +gets executed might block the thread, it is by **no means** a safe bet that one +will get the exact delay that was specified in the `setTimeout` call. + +The function that was passed as the first parameter will get called by the +*global object*, that means, that [`this`](#function.this) inside the called function +refers to that very object. + + function Foo() { + this.value = 42; + this.method = function() { + // this refers to the global object + console.log(this.value); // will log undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **Note:** As `setTimeout` takes a **function object** as its first parameter, an +> often made mistake is to use `setTimeout(foo(), 1000)`, which will use the +> **return value** of the call `foo` and **not** `foo`. This is, most of the time, +> a silent error, since when the function returns `undefined` `setTimeout` will +> **not** raise any error. + +### Stacking Calls with `setInterval` + +While `setTimeout` only runs the function once, `setInterval` - as the name +suggests - will execute the function **every** `X` milliseconds. But its use is +discouraged. + +When code that is being executed blocks the timeout call, `setInterval` will +still issue more calls to the specified function. This can, especially with small +intervals, result in function calls stacking up. + + function foo(){ + // something that blocks for 1 second + } + setInterval(foo, 100); + +In the above code `foo` will get called once and will then block for one second. + +While `foo` blocks the code `setInterval` will still schedule further calls to +it. Now, when `foo` has finished, there will already be **ten** further calls to +it waiting for execution. + +### Dealing with Possible Blocking Code + +The easiest as well as most controllable solution, is to use `setTimeout` within +the function itself. + + function foo(){ + // something that blocks for 1 second + setTimeout(foo, 100); + } + foo(); + +Not only does this encapsulate the `setTimeout` call, but it also prevents the +stacking of calls and it gives additional control.`foo` itself can now decide +whether it wants to run again or not. + +### Manually Clearing Timeouts + +Clearing timeouts and intervals works by passing the respective ID to +`clearTimeout` or `clearInterval`, depending which `set` function was used in +the first place. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Clearing all timeouts + +As there is no built-in method for clearing all timeouts and/or intervals, +it is necessary to use brute force in order to achieve this functionality. + + // clear "all" timeouts + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +There might still be timeouts that are unaffected by this arbitrary number; +therefore, is is instead recommended to keep track of all the timeout IDs, so +they can be cleared specifically. + +### Hidden use of `eval` + +`setTimeout` and `setInterval` can also take a string as their first parameter. +This feature should **never** be used, since it internally makes use of `eval`. + +> **Note:** Since the timeout functions are **not** specified by the ECMAScript +> standard, the exact workings when a string is passed to them might differ in +> various JavaScript implementations. For example, Microsoft's JScript makes use of +> the `Function` constructor in place of `eval`. + + function foo() { + // will get called + } + + function bar() { + function foo() { + // never gets called + } + setTimeout('foo()', 1000); + } + bar(); + +Since `eval` is not getting called [directly](#core.eval) in this case, the string +passed to `setTimeout` will get executed in the *global scope*; thus, it will +not use the local variable `foo` from the scope of `bar`. + +It is further recommended to **not** use a string for passing arguments to the +function that will get called by either of the timeout functions. + + function foo(a, b, c) {} + + // NEVER use this + setTimeout('foo(1,2, 3)', 1000) + + // Instead use an anonymous function + setTimeout(function() { + foo(a, b, c); + }, 1000) + +> **Note:** While it is also possible to use the syntax +> `setTimeout(foo, 1000, a, b, c)`, it is not recommended, as its use may lead +> to subtle errors when used with [methods](#function.this). + +### In Conclusion + +**Never** should a string be used as the parameter of `setTimeout` or +`setInterval`. It is a clear sign of **really** bad code, when arguments need +to be supplied to the function that gets called. An *anonymous function* should +be passed that then takes care of the actual call. + +Further, the use of `setInterval` should be avoided since its scheduler is not +blocked by executing JavaScript. + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" + diff --git a/doc/ja/types/casting.md b/doc/ja/types/casting.md new file mode 100644 index 00000000..15d84e74 --- /dev/null +++ b/doc/ja/types/casting.md @@ -0,0 +1,70 @@ +## Type Casting + +JavaScript is a *weakly typed* language, so it will apply *type coercion* +**wherever** possible. + + // These are true + new Number(10) == 10; // Number.toString() is converted + // back to a number + + 10 == '10'; // Strings gets converted to Number + 10 == '+10 '; // More string madness + 10 == '010'; // And more + isNaN(null) == false; // null converts to 0 + // which of course is not NaN + + // These are false + 10 == 010; + 10 == '-10'; + +> **ES5 Note:** Number literals that start with a `0` are interpreted as octal +> (Base 8). Octal support for these has been **removed** in ECMAScript 5 strict +> mode. + +In order to avoid the above, use of the [strict equal operator](#types.equality) +is **highly** recommended. Although this avoids a lot of common pitfalls, there +are still many further issues that arise from JavaScript's weak typing system. + +### Constructors of Built-In Types + +The constructors of the built in types like `Number` and `String` behave +differently when being used with the `new` keyword and without it. + + new Number(10) === 10; // False, Object and Number + Number(10) === 10; // True, Number and Number + new Number(10) + 0 === 10; // True, due to implicit conversion + +Using a built-in type like `Number` as a constructor will create a new `Number` +object, but leaving out the `new` keyword will make the `Number` function behave +like a converter. + +In addition, having literals or non-object values in there will result in even +more type coercion. + +The best option is to cast to one of the three possible types **explicitly**. + +### Casting to a String + + '' + 10 === '10'; // true + +By prepending a empty string a value can easily be casted to a string. + +### Casting to a Number + + +'10' === 10; // true + +Using the **unary** plus operator it is possible to cast to a number. + +### Casting to a Boolean + +By using the **not** operator twice, a value can be converted a boolean. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + + diff --git a/doc/ja/types/equality.md b/doc/ja/types/equality.md new file mode 100644 index 00000000..a578b19c --- /dev/null +++ b/doc/ja/types/equality.md @@ -0,0 +1,71 @@ +## Equality and Comparisons + +JavaScript has two different ways of comparing the values of objects for equality. + +### The Equality Operator + +The equality operator consists of two equal signs: `==` + +JavaScript features *weak typing*. This means that the equality operator +**coerces** types in order to compare them. + + "" == "0" // false + 0 == "" // true + 0 == "0" // true + false == "false" // false + false == "0" // true + false == undefined // false + false == null // false + null == undefined // true + " \t\r\n" == 0 // true + +The above table shows the results of the type coercion and it is the main reason +why the use of `==` is widely regarded as bad practice, it introduces hard to +track down bugs due to its complicated conversion rules. + +Additionally there is also a performance impact when type coercion is in play; +for example, a string has to be converted to a number before it can be compared +to another number. + +### The Strict Equality Operator + +The strict equality operator consists of **three** equal signs: `===` + +It works exactly like the normal equality operator, except that strict equality +operator does **not** perform type coercion between its operands. + + "" === "0" // false + 0 === "" // false + 0 === "0" // false + false === "false" // false + false === "0" // false + false === undefined // false + false === null // false + null === undefined // false + " \t\r\n" === 0 // false + +The above results are a lot clearer and allow for early breakage of code. This +hardens code to a certain degree and also gives performance improvements in case +the operands are of different types. + +### Comparing Objects + +While both `==` and `===` are stated as **equality** operators, they behave +different when at least one of their operands happens to be an `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Here both operators compare for **identity** and **not** equality; that is, they +will compare for the same **instance** of the object, much like `is` in Python +and pointer comparison in C. + +### In Conclusion + +It is highly recommended to only use the **strict equality** operator. In cases +where types need to be coerced, it should be done [explicitly](#types.casting) +and not left to the language's complicated coercion rules. + diff --git a/doc/ja/types/instanceof.md b/doc/ja/types/instanceof.md new file mode 100644 index 00000000..8711331a --- /dev/null +++ b/doc/ja/types/instanceof.md @@ -0,0 +1,38 @@ +## The `instanceof` Operator + +The `instanceof` operator compares the constructors of its two operands. It is +only useful when comparing custom made objects. Used on built-in types, it is +nearly as useless as the [typeof operator](#types.typeof). + +### Comparing Custom Objects + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // This just sets Bar.prototype to the function object Foo + // But not to an actual instance of Foo + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### Using `instanceof` with Native Types + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +One important thing to note here is, that `instanceof` does not work on objects +that origin from different JavaScript contexts (e.g. different documents +in a web browser), since their constructors will not be the exact same object. + +### In Conclusion + +The `instanceof` operator should **only** be used when dealing with custom made +objects that origin from the same JavaScript context. Just like the +[`typeof`](#types.typeof) operator, every other use of it should be **avoided**. + diff --git a/doc/ja/types/typeof.md b/doc/ja/types/typeof.md new file mode 100644 index 00000000..e4b28d7f --- /dev/null +++ b/doc/ja/types/typeof.md @@ -0,0 +1,87 @@ +## The `typeof` Operator + +The `typeof` operator (together with +[`instanceof`](#types.instanceof)) is probably the biggest +design flaw of JavaScript, as it is near of being **completely broken**. + +Although `instanceof` still has its limited uses, `typeof` really has only one +practical use case, which does **not** happen to be checking the type of an +object. + +> **Note:** While `typeof` can also be called with a function like syntax +> i.e. `typeof(obj)`, this is not a function call. The two parenthesis will +> behave like normal and the return value will be used as the operand of the +> `typeof` operator. There is **no** `typeof` function. + +### The JavaScript Type Table + + Value Class Type + ------------------------------------- + "foo" String string + new String("foo") String object + 1.2 Number number + new Number(1.2) Number object + true Boolean boolean + new Boolean(true) Boolean object + new Date() Date object + new Error() Error object + [1,2,3] Array object + new Array(1, 2, 3) Array object + new Function("") Function function + /abc/g RegExp object (function in Nitro/V8) + new RegExp("meow") RegExp object (function in Nitro/V8) + {} Object object + new Object() Object object + +In the above table *Type* refers to the value, that the `typeof` operator returns. +As can be clearly seen, this value is anything but consistent. + +The *Class* refers to the value of the internal `[[Class]]` property of an object. + +> **From the Specification:** The value of `[[Class]]` can be one of the +> following strings. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +In order to retrieve the value of `[[Class]]` one has to make use of the +`toString` method of `Object.prototype`. + +### The Class of an Object + +The specification gives exactly one way of accessing the `[[Class]]` value, +with the use of `Object.prototype.toString`. + + function is(type, obj) { + var clas = Object.prototype.toString.call(obj).slice(8, -1); + return obj !== undefined && obj !== null && clas === type; + } + + is('String', 'test'); // true + is('String', new String('test')); // true + +In the above example, `Object.prototype.toString` gets called with the value of +[this](#function.this) being set to the object whose `[[Class]]` value should be +retrieved. + +> **ES5 Note:** For convenience the return value of `Object.prototype.toString` +> for both `null` and `undefined` was **changed** from `Object` to `Null` and +> `Undefined` in ECMAScript 5. + +### Testing for Undefined Variables + + typeof foo !== 'undefined' + +The above will check whether `foo` was actually declared or not; just +referencing it would result in a `ReferenceError`. This is the only thing +`typeof` is actually useful for. + +### In Conclusion + +In order to check the type of an object, it is highly recommended to use +`Object.prototype.toString`; as this is the only reliable way of doing so. +As shown in the above type table, some return values of `typeof` are not defined +in the specification; thus, they can differ across various implementations. + +Unless checking whether a variable is defined, `typeof` should be avoided at +**all costs**. + + From 2a24fa87affeb63234d274a7b6525c6eddc05fdd Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 22:12:15 +0900 Subject: [PATCH 187/641] translate index.json to Japanese --- doc/ja/index.json | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/doc/ja/index.json b/doc/ja/index.json index d4012ed9..4f263875 100644 --- a/doc/ja/index.json +++ b/doc/ja/index.json @@ -1,64 +1,64 @@ { "title": "JavaScript Garden", - "langTitle": "JavaScript Garden in English", - "description": "A Guide to JavaScript's Quirks and Flaws.", + "langTitle": "JavaScript Garden in Japanese", + "description": "JavaScriptの奇抜さと欠陥についてのガイドライン", "sections": [ { - "title": "Intro", + "title": "イントロ", "dir": "intro", "articles": [] }, { - "title": "Objects", + "title": "オブジェクト", "dir": "object", "articles": [ - "general", + "概論", "prototype", "hasownproperty", - "forinloop" + "forinループ" ] }, { - "title": "Functions", + "title": "関数", "dir": "function", "articles": [ - "general", + "概論", "this", - "closures", - "arguments", - "constructors", - "scopes" + "クロージャ", + "引数", + "コンストラクタ", + "スコープ" ] }, { - "title": "Arrays", + "title": "配列", "dir": "array", "articles": [ - "general", - "constructor" + "概論", + "コンストラクタ" ] }, { - "title": "Types", + "title": "型", "dir": "types", "articles": [ - "equality", + "同値・同価", "typeof", "instanceof", - "casting" + "型変換" ] }, { - "title": "Core", + "title": "コア", "dir": "core", "articles": [ "eval", "undefined", - "semicolon" + "セミコロン" ] }, { - "title": "Other", + "title": "その他", "dir": "other", "articles": [ "timeouts" From d51d1246640443ababb84c5ce94057465e96da0d Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 22:13:43 +0900 Subject: [PATCH 188/641] add ja to language.json --- doc/language.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/language.json b/doc/language.json index 1c1bdffc..56774f29 100644 --- a/doc/language.json +++ b/doc/language.json @@ -1,5 +1,5 @@ { "default": "en", - "listed": ["en", "fi", "ru", "zh", "tr", "pl"] + "listed": ["en", "fi", "ru", "zh", "tr", "pl", "ja"] } From 5f190cdb71b117540deaa9f4b3b32da68a30b52a Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 22:24:31 +0900 Subject: [PATCH 189/641] fix ariticles --- doc/ja/index.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/ja/index.json b/doc/ja/index.json index 4f263875..85e25a6b 100644 --- a/doc/ja/index.json +++ b/doc/ja/index.json @@ -12,40 +12,40 @@ "title": "オブジェクト", "dir": "object", "articles": [ - "概論", + "general", "prototype", "hasownproperty", - "forinループ" + "forinloop" ] }, { "title": "関数", "dir": "function", "articles": [ - "概論", + "general", "this", - "クロージャ", - "引数", - "コンストラクタ", - "スコープ" + "closures", + "arguments", + "constructors", + "scopes" ] }, { "title": "配列", "dir": "array", "articles": [ - "概論", - "コンストラクタ" + "general", + "constructor" ] }, { "title": "型", "dir": "types", "articles": [ - "同値・同価", + "equality", "typeof", "instanceof", - "型変換" + "casting" ] }, { @@ -54,7 +54,7 @@ "articles": [ "eval", "undefined", - "セミコロン" + "semicolon" ] }, { From 91aa9d74bc9dcf5a2c177420a575ffbaf4b816ff Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 22:28:29 +0900 Subject: [PATCH 190/641] build ja/index.html --- site/ja/index.html | 1878 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1878 insertions(+) create mode 100644 site/ja/index.html diff --git a/site/ja/index.html b/site/ja/index.html new file mode 100644 index 00000000..60e5d1c9 --- /dev/null +++ b/site/ja/index.html @@ -0,0 +1,1878 @@ +JavaScript Garden +

    イントロ

    JavaScript Garden is a growing collection of documentation about the most +quirky parts of the JavaScript programming language. It gives advice to +avoid common mistakes, subtle bugs, as well as performance issues and bad +practices that non-expert JavaScript programmers may encounter on their +endeavours into the depths of the language.

    + +

    JavaScript Garden does not aim to teach you JavaScript. Former knowledge +of the language is strongly recommended in order to understand the topics covered +in this guide. In order to learn the basics of the language, please head over to +the excellent guide on the Mozilla Developer Network.

    + +

    The Authors

    + +

    This guide is the work of two lovely Stack Overflow users, Ivo Wetzel +(Writing) and Zhang Yi Jiang (Design).

    + +

    Contributors

    + + + +

    Hosting

    + +

    JavaScript Garden is hosted on GitHub, but Cramer Development supports us +with a mirror at JavaScriptGarden.info.

    + +

    License

    + +

    JavaScript Garden is published under the MIT license and hosted on +GitHub. If you find errors or typos please file an issue or a pull +request on the repository. You can also find us in the JavaScript room on +Stack Overflow chat.

    オブジェクト

    Object Usage and Properties

    Everything in JavaScript acts like an object, with the only two exceptions being +null and undefined.

    + +
    false.toString() // 'false'
    +[1, 2, 3].toString(); // '1,2,3'
    +
    +function Foo(){}
    +Foo.bar = 1;
    +Foo.bar; // 1
    +
    + +

    A common misconception is that number literals cannot be used as +objects. That is because a flaw in JavaScript's parser tries to parse the dot +notation on a number as a floating point literal.

    + +
    2.toString(); // raises SyntaxError
    +
    + +

    There are a couple of workarounds which can be used in order make number +literals act as objects too.

    + +
    2..toString(); // the second point is correctly recognized
    +2 .toString(); // note the space left to the dot
    +(2).toString(); // 2 is evaluated first
    +
    + +

    Objects as a Data Type

    + +

    Objects in JavaScript can also be used as a Hashmap, they mainly consist +of named properties mapping to values.

    + +

    Using a object literal - {} notation - it is possible to create a +plain object. This new object inherits from Object.prototype and +has no own properties defined on it.

    + +
    var foo = {}; // a new empty object
    +
    +// a new object with a property called 'test' with value 12
    +var bar = {test: 12}; 
    +
    + +

    Accessing Properties

    + +

    The properties of an object can be accessed in two ways, via either the dot +notation, or the square bracket notation.

    + +
    var foo = {name: 'Kitten'}
    +foo.name; // kitten
    +foo['name']; // kitten
    +
    +var get = 'name';
    +foo[get]; // kitten
    +
    +foo.1234; // SyntaxError
    +foo['1234']; // works
    +
    + +

    Both notations are identical in their workings, with the only difference being that +the square bracket notation allows for dynamic setting of properties, as well as +the use of property names that would otherwise lead to a syntax error.

    + +

    Deleting Properties

    + +

    The only way to actually remove a property from an object is to use the delete +operator; setting the property to undefined or null only remove the +value associated with the property, but not the key.

    + +
    var obj = {
    +    bar: 1,
    +    foo: 2,
    +    baz: 3
    +};
    +obj.bar = undefined;
    +obj.foo = null;
    +delete obj.baz;
    +
    +for(var i in obj) {
    +    if (obj.hasOwnProperty(i)) {
    +        console.log(i, '' + obj[i]);
    +    }
    +}
    +
    + +

    The above outputs both bar undefined and foo null - only baz was +removed and is therefore missing from the output.

    + +

    Notation of Keys

    + +
    var test = {
    +    'case': 'I am a keyword so I must be notated as a string',
    +    delete: 'I am a keyword too so me' // raises SyntaxError
    +};
    +
    + +

    Object properties can be both notated as plain characters and as strings. Due to +another mis-design in JavaScript's parser, the above will throw +a SyntaxError prior to ECMAScript 5.

    + +

    This error arises from the fact that delete is a keyword; therefore, it must be +notated as a string literal to ensure that it will be correctly interpreted by +older JavaScript engines.

    The Prototype

    JavaScript does not feature a classical inheritance model, instead it uses a +prototypal one.

    + +

    While this is often considered to be one of JavaScript's weaknesses, the +prototypal inheritance model is in fact more powerful than the classic model. +It is for example fairly trivial to build a classic model on top of it, while the +other way around is a far more difficult task.

    + +

    Due to the fact that JavaScript is basically the only widely used language that +features prototypal inheritance, it takes some time to adjust to the +differences between the two models.

    + +

    The first major difference is that inheritance in JavaScript is done by using so +called prototype chains.

    + + + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Set Bar's prototype to a new instance of Foo
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// Make sure to list Bar as the actual constructor
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar() // create a new bar instance
    +
    +// The resulting prototype chain
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo] 
    +        { foo: 'Hello World' }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* etc. */ }
    +
    + +

    In the above, the object test will inherit from both Bar.prototype and +Foo.prototype; hence, it will have access to the function method that was +defined on Foo. It will also have access to the property value of the +one Foo instance that is its prototype. It is important to note that new +Bar() does not create a new Foo instance, but reuses the one assigned to +its prototype; thus, all Bar instances will share the same value property.

    + + + +

    Property Lookup

    + +

    When accessing the properties of an object, JavaScript will traverse the +prototype chain upwards until it finds a property with the requested name.

    + +

    When it reaches the top of the chain - namely Object.prototype - and still +hasn't found the specified property, it will return the value +undefined instead.

    + +

    The Prototype Property

    + +

    While the prototype property is used by the language to build the prototype +chains, it is still possible to assign any given value to it. Although +primitives will simply get ignored when assigned as a prototype.

    + +
    function Foo() {}
    +Foo.prototype = 1; // no effect
    +
    + +

    Assigning objects, as shown in the example above, will work, and allows for dynamic +creation of prototype chains.

    + +

    Performance

    + +

    The lookup time for properties that are high up on the prototype chain can have a +negative impact on performance critical sections of code. Additionally, trying to +access non-existent properties will always traverse the full prototype chain.

    + +

    Also, when iterating over the properties of an object +every property that is on the prototype chain will get enumerated.

    + +

    Extension of Native Prototypes

    + +

    One mis-feature that is often used is to extend Object.prototype or one of the +other built in prototypes.

    + +

    This technique is called monkey patching and breaks encapsulation. While +used by widely spread frameworks such as Prototype, there is still no good +reason for cluttering built-in types with additional non-standard functionality.

    + +

    The only good reason for extending a built-in prototype is to backport +the features of newer JavaScript engines; for example, +Array.forEach.

    + +

    In Conclusion

    + +

    It is a must to understand the prototypal inheritance model completely +before writing complex code which makes use of it. Also, watch the length of +the prototype chains and break them up if necessary to avoid possible +performance issues. Further, the native prototypes should never be extended +unless it is for the sake of compatibility with newer JavaScript features.

    hasOwnProperty

    In order to check whether a object has a property defined on itself and not +somewhere on its prototype chain, it is necessary to use the +hasOwnProperty method which all objects inherit from Object.prototype.

    + + + +

    hasOwnProperty is the only thing in JavaScript which deals with properties and +does not traverse the prototype chain.

    + +
    // Poisoning Object.prototype
    +Object.prototype.bar = 1; 
    +var foo = {goo: undefined};
    +
    +foo.bar; // 1
    +'bar' in foo; // true
    +
    +foo.hasOwnProperty('bar'); // false
    +foo.hasOwnProperty('goo'); // true
    +
    + +

    Only hasOwnProperty will give the correct and expected result, this is +essential when iterating over the properties of any object. There is no other +way to exclude properties that are not defined on the object itself, but +somewhere on its prototype chain.

    + +

    hasOwnProperty as a Property

    + +

    JavaScript does not protect the property name hasOwnProperty; thus, if the +possibility exists that an object might have a property with this name, it is +necessary to use an external hasOwnProperty in order to get correct results.

    + +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // always returns false
    +
    +// Use another Object's hasOwnProperty and call it with 'this' set to foo
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +
    + +

    In Conclusion

    + +

    When checking for the existence of a property on a object, hasOwnProperty is +the only method of doing so. It is also recommended to make hasOwnProperty +part of every for in loop, this will avoid errors from +extended native prototypes.

    The for in Loop

    Just like the in operator, the for in loop also traverses the prototype +chain when iterating over the properties of an object.

    + + + +
    // Poisoning Object.prototype
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // prints both bar and moo
    +}
    +
    + +

    Since it is not possible to change the behavior of the for in loop itself, it +is necessary to filter out the unwanted properties inside the loop body , +this is done by using the hasOwnProperty method of +Object.prototype.

    + + + +

    Using hasOwnProperty for Filtering

    + +
    // still the foo from above
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +
    + +

    This version is the only correct one to use. Due to the use of hasOwnProperty it +will only print out moo. When hasOwnProperty is left out, the code is +prone to errors in cases where the native prototypes - e.g. Object.prototype - +have been extended.

    + +

    One widely used framework which does this is Prototype. When this +framework is included, for in loops that do not use hasOwnProperty are +guaranteed to break.

    + +

    In Conclusion

    + +

    It is recommended to always use hasOwnProperty. Never should any +assumptions be made about the environment the code is running in, or whether the +native prototypes have been extended or not.

    関数

    Function Declarations and Expressions

    Functions in JavaScript are first class objects. That means they can be +passed around like any other value. One common use of this feature is to pass +an anonymous function as a callback to another, possibly asynchronous function.

    + +

    The function Declaration

    + +
    function foo() {}
    +
    + +

    The above function gets hoisted before the execution of the +program starts; thus, it is available everywhere in the scope it was defined +in, even if called before the actual definition in the source.

    + +
    foo(); // Works because foo was created before this code runs
    +function foo() {}
    +
    + +

    The function Expression

    + +
    var foo = function() {};
    +
    + +

    This example assigns the unnamed and anonymous function to the variable foo.

    + +
    foo; // 'undefined'
    +foo(); // this raises a TypeError
    +var foo = function() {};
    +
    + +

    Due to the fact that var is a declaration, that hoists the variable name foo +before the actual execution of the code starts, foo is already defined when +the script gets executed.

    + +

    But since assignments only happen at runtime, the value of foo will default +to undefined before the corresponding code is executed.

    + +

    Named Function Expression

    + +

    Another special case is the assignment of named functions.

    + +
    var foo = function bar() {
    +    bar(); // Works
    +}
    +bar(); // ReferenceError
    +
    + +

    Here bar is not available in the outer scope, since the function only gets +assigned to foo; however, inside of bar it is available. This is due to +how name resolution in JavaScript works, the name of the +function is always made available in the local scope of the function itself.

    How this Works

    JavaScript has a different concept of what the special name this refers to +than most other programming languages do. There are exactly five different +ways in which the value of this can be bound in the language.

    + +

    The Global Scope

    + +
    this;
    +
    + +

    When using this in global scope, it will simply refer to the global object.

    + +

    Calling a Function

    + +
    foo();
    +
    + +

    Here this will again refer to the global object.

    + + + +

    Calling a Method

    + +
    test.foo(); 
    +
    + +

    In this example this will refer to test.

    + +

    Calling a Constructor

    + +
    new foo(); 
    +
    + +

    A function call that is preceded by the new keyword acts as +a constructor. Inside the function this will refer +to a newly created Object.

    + +

    Explicit Setting of this

    + +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // array will expand to the below
    +foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3
    +
    + +

    When using the call or apply methods of Function.prototype, the value of +this inside the called function gets explicitly set to the first argument +of the corresponding function call.

    + +

    As a result, the above example the method case does not apply, and this +inside of foo will be set to bar.

    + + + +

    Common Pitfalls

    + +

    While most of these cases make sense, the first one is to be considered another +mis-design of the language, as it never has any practical use.

    + +
    Foo.method = function() {
    +    function test() {
    +        // this is set to the global object
    +    }
    +    test();
    +}
    +
    + +

    A common misconception is that this inside of test refers to Foo, while in +fact it does not.

    + +

    In order to gain access to Foo from within test it is necessary to create a +local variable inside of method which refers to Foo.

    + +
    Foo.method = function() {
    +    var that = this;
    +    function test() {
    +        // Use that instead of this here
    +    }
    +    test();
    +}
    +
    + +

    that is just a normal variable name, but it is commonly used for the reference to an +outer this. In combination with closures, it can also +be used to pass this values around.

    + +

    Assigning Methods

    + +

    Another thing that does not work in JavaScript is function aliasing, that is, +assigning a method to a variable.

    + +
    var test = someObject.methodTest;
    +test();
    +
    + +

    Due to the first case test now acts like a plain function call; therefore, +this inside it will no longer refer to someObject.

    + +

    While the late binding of this might seem like a bad idea at first, it is in +fact what makes prototypal inheritance work.

    + +
    function Foo() {}
    +Foo.prototype.method = function() {};
    +
    +function Bar() {}
    +Bar.prototype = Foo.prototype;
    +
    +new Bar().method();
    +
    + +

    When method gets called on a instance of Bar, this will now refer to that +very instance.

    Closures and References

    One of JavaScript's most powerful features is the availability of closures, +this means that scopes always keep access to the outer scope they were +defined in. Since the only scoping that JavaScript has is +function scope, all functions, by default, act as closures.

    + +

    Emulating private variables

    + +
    function Counter(start) {
    +    var count = start;
    +    return {
    +        increment: function() {
    +            count++;
    +        },
    +
    +        get: function() {
    +            return count;
    +        }
    +    }
    +}
    +
    +var foo = Counter(4);
    +foo.increment();
    +foo.get(); // 5
    +
    + +

    Here, Counter returns two closures. The function increment as well as +the function get. Both of these functions keep a reference to the scope of +Counter and, therefore, always keep access to the count variable that was +defined in that very scope.

    + +

    Why Private Variables Work

    + +

    Since it is not possible to reference or assign scopes in JavaScript, there is +no way of accessing the variable count from the outside. The only way to +interact with it is via the two closures.

    + +
    var foo = new Counter(4);
    +foo.hack = function() {
    +    count = 1337;
    +};
    +
    + +

    The above code will not change the variable count in the scope of Counter, +since foo.hack was not defined in that scope. It will instead create - or +override - the global variable count.

    + +

    Closures Inside Loops

    + +

    One often made mistake is to use closures inside of loops, as if they were +copying the value of the loops index variable.

    + +
    for(var i = 0; i < 10; i++) {
    +    setTimeout(function() {
    +        console.log(i);  
    +    }, 1000);
    +}
    +
    + +

    The above will not output the numbers 0 through 9, but will simply print +the number 10 ten times.

    + +

    The anonymous function keeps a reference to i and at the time +console.log gets called, the for loop has already finished and the value of +i as been set to 10.

    + +

    In order to get the desired behavior, it is necessary to create a copy of +the value of i.

    + +

    Avoiding the Reference Problem

    + +

    In order to copy the value of the loop's index variable, it is best to use an +anonymous wrapper.

    + +
    for(var i = 0; i < 10; i++) {
    +    (function(e) {
    +        setTimeout(function() {
    +            console.log(e);  
    +        }, 1000);
    +    })(i);
    +}
    +
    + +

    The anonymous outer function gets called immediately with i as its first +argument and will receive a copy of the value of i as its parameter e.

    + +

    The anonymous function that gets passed to setTimeout now has a reference to +e, whose value does not get changed by the loop.

    + +

    There is another possible way of achieving this; that is to return a function +from the anonymous wrapper, that will then have the same behavior as the code +above.

    + +
    for(var i = 0; i < 10; i++) {
    +    setTimeout((function(e) {
    +        return function() {
    +            console.log(e);
    +        }
    +    })(i), 1000)
    +}
    +

    The arguments Object

    Every function scope in JavaScript can access the special variable arguments. +This variable holds a list of all the arguments that were passed to the function.

    + + + +

    The arguments object is not an Array. While it has some of the +semantics of an array - namely the length property - it does not inherit from +Array.prototype and is in fact an Object.

    + +

    Due to this, it is not possible to use standard array methods like push, +pop or slice on arguments. While iteration with a plain for loop works +just fine, it is necessary to convert it to a real Array in order to use the +standard Array methods on it.

    + +

    Converting to an Array

    + +

    The code below will return a new Array containing all the elements of the +arguments object.

    + +
    Array.prototype.slice.call(arguments);
    +
    + +

    This conversion is slow, it is not recommended to use it in performance +critical sections of code.

    + +

    Passing Arguments

    + +

    The following is the recommended way of passing arguments from one function to +another.

    + +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // do stuff here
    +}
    +
    + +

    Another trick is to use both call and apply together to create fast, unbound +wrappers.

    + +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// Create an unbound version of "method" 
    +// It takes the parameters: this, arg1, arg2...argN
    +Foo.method = function() {
    +
    +    // Result: Foo.prototype.method.call(this, arg1, arg2... argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +};
    +
    + +

    Formal Parameters and Arguments Indices

    + +

    The arguments object creates getter and setter functions for both its +properties as well as the function's formal parameters.

    + +

    As a result, changing the value of a formal parameter will also change the value +of the corresponding property on the arguments object, and the other way around.

    + +
    function foo(a, b, c) {
    +    arguments[0] = 2;
    +    a; // 2                                                           
    +
    +    b = 4;
    +    arguments[1]; // 4
    +
    +    var d = c;
    +    d = 9;
    +    c; // 3
    +}
    +foo(1, 2, 3);
    +
    + +

    Performance Myths and Truths

    + +

    The arguments object is always created with the only two exceptions being the +cases where it is declared as a name inside of a function or one of its formal +parameters. It does not matter whether it is used or not.

    + +

    Both getters and setters are always created; thus, using it has nearly +no performance impact at all, especially not in real world code where there is +more than a simple access to the arguments object's properties.

    + + + +

    However, there is one case which will drastically reduce the performance in +modern JavaScript engines. That case is the use of arguments.callee.

    + +
    function foo() {
    +    arguments.callee; // do something with this function object
    +    arguments.callee.caller; // and the calling function object
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // Would normally be inlined...
    +    }
    +}
    +
    + +

    In the above code, foo can no longer be a subject to inlining since it +needs to know about both itself and its caller. This not only defeats possible +performance gains that would arise from inlining, it also breaks encapsulation +since the function may now be dependent on a specific calling context.

    + +

    It is highly recommended to never make use of arguments.callee or any of +its properties.

    + +

    Constructors

    Constructors in JavaScript are yet again different from many other languages. Any +function call that is preceded by the new keyword acts as a constructor.

    + +

    Inside the constructor - the called function - the value of this refers to a +newly created Object. The prototype of this new +object is set to the prototype of the function object that was invoked as the +constructor.

    + +

    If the function that was called has no explicit return statement, then it +implicitly returns the value of this - the new object.

    + +
    function Foo() {
    +    this.bla = 1;
    +}
    +
    +Foo.prototype.test = function() {
    +    console.log(this.bla);
    +};
    +
    +var test = new Foo();
    +
    + +

    The above calls Foo as constructor and sets the prototype of the newly +created object to Foo.prototype.

    + +

    In case of an explicit return statement the function returns the value +specified that statement, but only if the return value is an Object.

    + +
    function Bar() {
    +    return 2;
    +}
    +new Bar(); // a new object
    +
    +function Test() {
    +    this.value = 2;
    +
    +    return {
    +        foo: 1
    +    };
    +}
    +new Test(); // the returned object
    +
    + +

    When the new keyword is omitted, the function will not return a new object.

    + +
    function Foo() {
    +    this.bla = 1; // gets set on the global object
    +}
    +Foo(); // undefined
    +
    + +

    While the above example might still appear to work in some cases, due to the +workings of this in JavaScript, it will use the +global object as the value of this.

    + +

    Factories

    + +

    In order to be able to omit the new keyword, the constructor function has to +explicitly return a value.

    + +
    function Bar() {
    +    var value = 1;
    +    return {
    +        method: function() {
    +            return value;
    +        }
    +    }
    +}
    +Bar.prototype = {
    +    foo: function() {}
    +};
    +
    +new Bar();
    +Bar();
    +
    + +

    Both calls to Bar return the exact same thing, a newly create object which +has a property called method on it, that is a +Closure.

    + +

    It is also to note that the call new Bar() does not affect the prototype +of the returned object. While the prototype will be set on the newly created +object, Bar never returns that new object.

    + +

    In the above example, there is no functional difference between using and +not using the new keyword.

    + +

    Creating New Objects via Factories

    + +

    An often made recommendation is to not use new since forgetting its use +may lead to bugs.

    + +

    In order to create new object, one should rather use a factory and construct a +new object inside of that factory.

    + +
    function Foo() {
    +    var obj = {};
    +    obj.value = 'blub';
    +
    +    var private = 2;
    +    obj.someMethod = function(value) {
    +        this.value = value;
    +    }
    +
    +    obj.getPrivate = function() {
    +        return private;
    +    }
    +    return obj;
    +}
    +
    + +

    While the above is robust against a missing new keyword and certainly makes +the use of private variables easier, it comes with some +downsides.

    + +
      +
    1. It uses more memory since the created objects do not share the methods +on a prototype.
    2. +
    3. In order to inherit the factory needs to copy all the methods from another +object or put that object on the prototype of the new object.
    4. +
    5. Dropping the prototype chain just because of a left out new keyword +somehow goes against the spirit of the language.
    6. +
    + +

    In Conclusion

    + +

    While omitting the new keyword might lead to bugs, it is certainly not a +reason to drop the use of prototypes altogether. In the end it comes down to +which solution is better suited for the needs of the application, it is +especially important to choose a specific style of object creation and stick +with it.

    Scopes and Namespaces

    Although JavaScript deals fine with the syntax of two matching curly +braces for blocks, it does not support block scope; hence, all that is left +is in the language is function scope.

    + +
    function test() { // a scope
    +    for(var i = 0; i < 10; i++) { // not a scope
    +        // count
    +    }
    +    console.log(i); // 10
    +}
    +
    + + + +

    There are also no distinct namespaces in JavaScript, that means that everything +gets defined in one globally shared namespace.

    + +

    Each time a variable is referenced, JavaScript will traverse upwards through all +the scopes until it finds it. In the case that it reaches the global scope and +still has not found the requested name, it will raise a ReferenceError.

    + +

    The Bane of Global Variables

    + +
    // script A
    +foo = '42';
    +
    +// script B
    +var foo = '42'
    +
    + +

    The above two scripts do not have the same effect. Script A defines a +variable called foo in the global scope and script B defines a foo in the +current scope.

    + +

    Again, that is not at all the same effect, not using var can have major +implications.

    + +
    // global scope
    +var foo = 42;
    +function test() {
    +    // local scope
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +
    + +

    Leaving out the var statement inside the function test will override the +value of foo. While this might not seem like a big deal at first, having +thousands of lines of JavaScript and not using var will introduce horrible and +hard to track down bugs.

    + +
    // global scope
    +var items = [/* some list */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // scope of subLoop
    +    for(i = 0; i < 10; i++) { // missing var statement
    +        // do amazing stuff!
    +    }
    +}
    +
    + +

    The outer loop will terminate after the first call to subLoop, since subLoop +overwrites the global value of i. Using a var for the second for loop would +have easily avoided this error. The var statement should never be left out +unless the desired effect is to affect the outer scope.

    + +

    Local Variables

    + +

    The only source for local variables in JavaScript are +function parameters and variables that were declared via the +var statement.

    + +
    // global scope
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // local scope of the function test
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +
    + +

    While foo and i are local variables inside the scope of the function test, +the assignment of bar will override the global variable with the same name.

    + +

    Hoisting

    + +

    JavaScript hoists declarations. This means that both var statements and +function declarations will be moved to the top of their enclosing scope.

    + +
    bar();
    +var bar = function() {};
    +var someValue = 42;
    +
    +test();
    +function test(data) {
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        var goo = 2;
    +    }
    +    for(var i = 0; i < 100; i++) {
    +        var e = data[i];
    +    }
    +}
    +
    + +

    The above code gets transformed before any execution is started. JavaScript moves +the var statements as well as the function declarations to the top of the +nearest surrounding scope.

    + +
    // var statements got moved here
    +var bar, someValue; // default to 'undefined'
    +
    +// the function declartion got moved up too
    +function test(data) {
    +    var goo, i, e; // missing block scope moves these here
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // fails with a TypeError since bar is still 'undefined'
    +someValue = 42; // assignments are not affected by hoisting
    +bar = function() {};
    +
    +test();
    +
    + +

    Missing block scoping will not only move var statements out of loops and +their bodies, it will also make the results of certain if constructs +non-intuitive.

    + +

    In the original code the if statement seemed to modify the global +variable goo, while actually it modifies the local variable - after hoisting +has been applied.

    + +

    Without the knowledge about hoisting, below code might seem to raise a +ReferenceError.

    + +
    // check whether SomeImportantThing has been initiliazed
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +
    + +

    But of course, the above works due to the fact that the var statement is being +moved to the top of the global scope.

    + +
    var SomeImportantThing;
    +
    +// other code might initiliaze SomeImportantThing here, or not
    +
    +// make sure it's there
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +
    + +

    Name Resolution Order

    + +

    All scopes in JavaScript, including the global scope, have the special name +this defined in them, which refers to the current object.

    + +

    Function scopes also have the name arguments defined in +them which contains the arguments that were passed to a function.

    + +

    For example, when trying to access a variable named foo inside the scope of a +function, JavaScript will lookup the name in the following order:

    + +
      +
    1. In case there is a var foo statement in the current scope use that.
    2. +
    3. If one of the function parameters is named foo use that.
    4. +
    5. If the function itself is called foo use that.
    6. +
    7. Go to the next outer scope and start with #1 again.
    8. +
    + + + +

    Namespaces

    + +

    A common problem of having only one global namespace is the likeliness of running +into problems where variable names clash. In JavaScript, this problem can +easily be avoided with the help of anonymous wrappers.

    + +
    (function() {
    +    // a self contained "namespace"
    +
    +    window.foo = function() {
    +        // an exposed closure
    +    };
    +
    +})(); // execute the function immediately
    +
    + +

    Unnamed functions are considered expressions; so in order to +being callable, they must first be evaluated.

    + +
    ( // evaluate the function inside the paranthesis
    +function() {}
    +) // and return the function object
    +() // call the result of the evaluation
    +
    + +

    There are other ways for evaluating and calling the function expression; which, +while different in syntax, do behave the exact same way.

    + +
    // Two other ways
    ++function(){}();
    +(function(){}());
    +
    + +

    In Conclusion

    + +

    It is recommended to always use an anonymous wrapper for encapsulating code in +its own namespace. This does not only protect code against name clashes, it +also allows for better modularization of programs.

    + +

    Additionally, the use of global variables is considered bad practice. Any +use of them indicates badly written code that is prone to errors and hard to maintain.

    配列

    Array Iteration and Properties

    Although arrays in JavaScript are objects, there are no good reasons to use +the for in loop in for iteration on them. In fact there +are a number of good reasons against the use of for in on arrays.

    + + + +

    Since the for in loop enumerates all the properties that are on the prototype +chain and the only way to exclude those properties is to use +hasOwnProperty, it is already up to twenty times +slower than a normal for loop.

    + +

    Iteration

    + +

    In order to achieve the best performance when iterating over arrays, it is best +to use the classic for loop.

    + +
    var list = [1, 2, 3, 4, 5, ...... 100000000];
    +for(var i = 0, l = list.length; i < l; i++) {
    +    console.log(list[i]);
    +}
    +
    + +

    There is one extra catch in the above example, that is the caching of the +length of the array via l = list.length.

    + +

    Although the length property is defined on the array itself, there is still an +overhead for doing the lookup on each iteration of the loop. And while recent +JavaScript engines may apply optimization in this case, there is no way of +telling whether the code will run on one of these newer engines or not.

    + +

    In fact, leaving out the caching may result in the loop being only half as +fast as with the cached length.

    + +

    The length Property

    + +

    While the getter of the length property simply returns the number of +elements that are contained in the array, the setter can be used to +truncate the array.

    + +
    var foo = [1, 2, 3, 4, 5, 6];
    +foo.length = 3;
    +foo; // [1, 2, 3]
    +
    +foo.length = 6;
    +foo; // [1, 2, 3]
    +
    + +

    Assigning a smaller length does truncate the array, but increasing the length +does not have any effect on the array.

    + +

    In Conclusion

    + +

    For the best performance it is recommended to always use the plain for loop +and cache the length property. The use of for in on an array is a sign of +badly written code that is prone to bugs and bad performance.

    The Array Constructor

    Since the Array constructor is ambiguous in how it deals with its parameters, +it is highly recommended to always use the array literals - [] notation - +when creating new arrays.

    + +
    [1, 2, 3]; // Result: [1, 2, 3]
    +new Array(1, 2, 3); // Result: [1, 2, 3]
    +
    +[3]; // Result: [3]
    +new Array(3); // Result: []
    +new Array('3') // Result: ['3']
    +
    + +

    In cases when there is only one argument passed to the Array constructor, +and that argument is a Number, the constructor will return a new sparse +array with the length property set to the value of the argument. It should be +noted that only the length property of the new array will be set this way, +the actual indexes of the array will not be initialized.

    + +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // false, the index was not set
    +
    + +

    The behavior of being able to set the length of the array upfront only comes in +handy in a few cases, like repeating a string, in which it avoids the use of a +for loop code.

    + +
    new Array(count + 1).join(stringToRepeat);
    +
    + +

    In Conclusion

    + +

    The use of the Array constructor should be avoided as much as possible. +Literals are definitely preferred. They are shorter and have a clearer syntax; +therefore, they also increase the readability of the code.

    Equality and Comparisons

    JavaScript has two different ways of comparing the values of objects for equality.

    + +

    The Equality Operator

    + +

    The equality operator consists of two equal signs: ==

    + +

    JavaScript features weak typing. This means that the equality operator +coerces types in order to compare them.

    + +
    ""           ==   "0"           // false
    +0            ==   ""            // true
    +0            ==   "0"           // true
    +false        ==   "false"       // false
    +false        ==   "0"           // true
    +false        ==   undefined     // false
    +false        ==   null          // false
    +null         ==   undefined     // true
    +" \t\r\n"    ==   0             // true
    +
    + +

    The above table shows the results of the type coercion and it is the main reason +why the use of == is widely regarded as bad practice, it introduces hard to +track down bugs due to its complicated conversion rules.

    + +

    Additionally there is also a performance impact when type coercion is in play; +for example, a string has to be converted to a number before it can be compared +to another number.

    + +

    The Strict Equality Operator

    + +

    The strict equality operator consists of three equal signs: ===

    + +

    It works exactly like the normal equality operator, except that strict equality +operator does not perform type coercion between its operands.

    + +
    ""           ===   "0"           // false
    +0            ===   ""            // false
    +0            ===   "0"           // false
    +false        ===   "false"       // false
    +false        ===   "0"           // false
    +false        ===   undefined     // false
    +false        ===   null          // false
    +null         ===   undefined     // false
    +" \t\r\n"    ===   0             // false
    +
    + +

    The above results are a lot clearer and allow for early breakage of code. This +hardens code to a certain degree and also gives performance improvements in case +the operands are of different types.

    + +

    Comparing Objects

    + +

    While both == and === are stated as equality operators, they behave +different when at least one of their operands happens to be an Object.

    + +
    {} === {};                   // false
    +new String('foo') === 'foo'; // false
    +new Number(10) === 10;       // false
    +var foo = {};
    +foo === foo;                 // true
    +
    + +

    Here both operators compare for identity and not equality; that is, they +will compare for the same instance of the object, much like is in Python +and pointer comparison in C.

    + +

    In Conclusion

    + +

    It is highly recommended to only use the strict equality operator. In cases +where types need to be coerced, it should be done explicitly +and not left to the language's complicated coercion rules.

    The typeof Operator

    The typeof operator (together with +instanceof) is probably the biggest +design flaw of JavaScript, as it is near of being completely broken.

    + +

    Although instanceof still has its limited uses, typeof really has only one +practical use case, which does not happen to be checking the type of an +object.

    + + + +

    The JavaScript Type Table

    + +
    Value               Class      Type
    +-------------------------------------
    +"foo"               String     string
    +new String("foo")   String     object
    +1.2                 Number     number
    +new Number(1.2)     Number     object
    +true                Boolean    boolean
    +new Boolean(true)   Boolean    object
    +new Date()          Date       object
    +new Error()         Error      object
    +[1,2,3]             Array      object
    +new Array(1, 2, 3)  Array      object
    +new Function("")    Function   function
    +/abc/g              RegExp     object (function in Nitro/V8)
    +new RegExp("meow")  RegExp     object (function in Nitro/V8)
    +{}                  Object     object
    +new Object()        Object     object
    +
    + +

    In the above table Type refers to the value, that the typeof operator returns. +As can be clearly seen, this value is anything but consistent.

    + +

    The Class refers to the value of the internal [[Class]] property of an object.

    + + + +

    In order to retrieve the value of [[Class]] one has to make use of the +toString method of Object.prototype.

    + +

    The Class of an Object

    + +

    The specification gives exactly one way of accessing the [[Class]] value, +with the use of Object.prototype.toString.

    + +
    function is(type, obj) {
    +    var clas = Object.prototype.toString.call(obj).slice(8, -1);
    +    return obj !== undefined && obj !== null && clas === type;
    +}
    +
    +is('String', 'test'); // true
    +is('String', new String('test')); // true
    +
    + +

    In the above example, Object.prototype.toString gets called with the value of +this being set to the object whose [[Class]] value should be +retrieved.

    + + + +

    Testing for Undefined Variables

    + +
    typeof foo !== 'undefined'
    +
    + +

    The above will check whether foo was actually declared or not; just +referencing it would result in a ReferenceError. This is the only thing +typeof is actually useful for.

    + +

    In Conclusion

    + +

    In order to check the type of an object, it is highly recommended to use +Object.prototype.toString; as this is the only reliable way of doing so. +As shown in the above type table, some return values of typeof are not defined +in the specification; thus, they can differ across various implementations.

    + +

    Unless checking whether a variable is defined, typeof should be avoided at +all costs.

    The instanceof Operator

    The instanceof operator compares the constructors of its two operands. It is +only useful when comparing custom made objects. Used on built-in types, it is +nearly as useless as the typeof operator.

    + +

    Comparing Custom Objects

    + +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // true
    +new Bar() instanceof Foo; // true
    +
    +// This just sets Bar.prototype to the function object Foo
    +// But not to an actual instance of Foo
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // false
    +
    + +

    Using instanceof with Native Types

    + +
    new String('foo') instanceof String; // true
    +new String('foo') instanceof Object; // true
    +
    +'foo' instanceof String; // false
    +'foo' instanceof Object; // false
    +
    + +

    One important thing to note here is, that instanceof does not work on objects +that origin from different JavaScript contexts (e.g. different documents +in a web browser), since their constructors will not be the exact same object.

    + +

    In Conclusion

    + +

    The instanceof operator should only be used when dealing with custom made +objects that origin from the same JavaScript context. Just like the +typeof operator, every other use of it should be avoided.

    Type Casting

    JavaScript is a weakly typed language, so it will apply type coercion +wherever possible.

    + +
    // These are true
    +new Number(10) == 10; // Number.toString() is converted
    +                      // back to a number
    +
    +10 == '10';           // Strings gets converted to Number
    +10 == '+10 ';         // More string madness
    +10 == '010';          // And more 
    +isNaN(null) == false; // null converts to 0
    +                      // which of course is not NaN
    +
    +// These are false
    +10 == 010;
    +10 == '-10';
    +
    + + + +

    In order to avoid the above, use of the strict equal operator +is highly recommended. Although this avoids a lot of common pitfalls, there +are still many further issues that arise from JavaScript's weak typing system.

    + +

    Constructors of Built-In Types

    + +

    The constructors of the built in types like Number and String behave +differently when being used with the new keyword and without it.

    + +
    new Number(10) === 10;     // False, Object and Number
    +Number(10) === 10;         // True, Number and Number
    +new Number(10) + 0 === 10; // True, due to implicit conversion
    +
    + +

    Using a built-in type like Number as a constructor will create a new Number +object, but leaving out the new keyword will make the Number function behave +like a converter.

    + +

    In addition, having literals or non-object values in there will result in even +more type coercion.

    + +

    The best option is to cast to one of the three possible types explicitly.

    + +

    Casting to a String

    + +
    '' + 10 === '10'; // true
    +
    + +

    By prepending a empty string a value can easily be casted to a string.

    + +

    Casting to a Number

    + +
    +'10' === 10; // true
    +
    + +

    Using the unary plus operator it is possible to cast to a number.

    + +

    Casting to a Boolean

    + +

    By using the not operator twice, a value can be converted a boolean.

    + +
    !!'foo';   // true
    +!!'';      // false
    +!!'0';     // true
    +!!'1';     // true
    +!!'-1'     // true
    +!!{};      // true
    +!!true;    // true
    +

    コア

    Why Not to Use eval

    The eval function will execute a string of JavaScript code in the local scope.

    + +
    var foo = 1;
    +function test() {
    +    var foo = 2;
    +    eval('foo = 3');
    +    return foo;
    +}
    +test(); // 3
    +foo; // 1
    +
    + +

    But eval only executes in local scope when it is being called directly and +the name of the called function is actually eval.

    + +
    var foo = 1;
    +function test() {
    +    var foo = 2;
    +    var bar = eval;
    +    bar('foo = 3');
    +    return foo;
    +}
    +test(); // 2
    +foo; // 3
    +
    + +

    The use of eval should be avoided at all costs. 99.9% of its "uses" can be +achieved without it.

    + +

    eval in Disguise

    + +

    The timeout functions setTimeout and setInterval can both +take a string as their first argument. This string will always get executed +in the global scope since eval is not being called directly in that case.

    + +

    Security Issues

    + +

    eval also is a security problem as it executes any code given to it, +it should never be used with strings of unknown or untrusted origins.

    + +

    In Conclusion

    + +

    eval should never be used, any code that makes use of it is to be questioned in +its workings, performance and security. In case something requires eval in +order to work, its design is to be questioned and should not be used in the +first place, a better design should be used, that does not require the use of +eval.

    undefined and null

    JavaScript has two distinct values for nothing, the more useful of these two +being undefined.

    + +

    The Value undefined

    + +

    undefined is a type with exactly one value: undefined.

    + +

    The language also defines a global variable that has the value of undefined, +this variable is also called undefined. But this variable is not a constant, +nor is it a keyword of the language. This means that its value can be easily +overwritten.

    + + + +

    Some examples for when the value undefined is returned:

    + +
      +
    • Accessing the (unmodified) global variable undefined.
    • +
    • Implicit returns of functions due to missing return statements.
    • +
    • return statements which do not explicitly return anything.
    • +
    • Lookups of non-existent properties.
    • +
    • Function parameters which do not had any explicit value passed.
    • +
    • Anything that has been set to the value of undefined.
    • +
    + +

    Handling Changes to the Value of undefined

    + +

    Since the global variable undefined only holds a copy of the actual value of +undefined, assigning a new value to it does not change the value of the +type undefined.

    + +

    Still, in order to compare something against the value of undefined it is +necessary to retrieve the value of undefined first.

    + +

    In order to protect code against a possible overwritten undefined variable, a +common technique used is to add an additional parameter to an +anonymous wrapper, that gets no argument passed to it.

    + +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // undefined in the local scope does 
    +    // now again refer to the value
    +
    +})('Hello World', 42);
    +
    + +

    Another way to achieve the same effect would be to use a declaration inside the +wrapper.

    + +
    var undefined = 123;
    +(function(something, foo) {
    +    var undefined;
    +    ...
    +
    +})('Hello World', 42);
    +
    + +

    The only difference being here, that this version results in 4 more bytes being +used in case it is minified and there is no other var statement inside the +anonymous wrapper.

    + +

    Uses of null

    + +

    While undefined in the context of the JavaScript language is mostly used in +the sense of a traditional null, the actual null (both a literal and a type) +is more or less just another data type.

    + +

    It is used in some JavaScript internals (like declaring the end of the +prototype chain by setting Foo.prototype = null), but in almost all cases it +can be replaced by undefined.

    Automatic Semicolon Insertion

    Although JavaScript has C style syntax, it does not enforce the use of +semicolons in the source code, it is possible to omit them.

    + +

    But JavaScript is not a semicolon-less language, it in fact needs the +semicolons in order to understand the source code. Therefore the JavaScript +parser automatically inserts them whenever it encounters a parse +error due to a missing semicolon.

    + +
    var foo = function() {
    +} // parse error, semicolon expected
    +test()
    +
    + +

    Insertion happens, and the parser tries again.

    + +
    var foo = function() {
    +}; // no error, parser continues
    +test()
    +
    + +

    The automatic insertion of semicolon is considered to be one of biggest +design flaws in the language, as it can change the behavior of code.

    + +

    How it Works

    + +

    The code below has no semicolons in it, so it is up to the parser to decide where +to insert them.

    + +
    (function(window, undefined) {
    +    function test(options) {
    +        log('testing!')
    +
    +        (options.list || []).forEach(function(i) {
    +
    +        })
    +
    +        options.value.test(
    +            'long string to pass here',
    +            'and another long string to pass'
    +        )
    +
    +        return
    +        {
    +            foo: function() {}
    +        }
    +    }
    +    window.test = test
    +
    +})(window)
    +
    +(function(window) {
    +    window.someLibrary = {}
    +
    +})(window)
    +
    + +

    Below is the result of the parser's "guessing" game.

    + +
    (function(window, undefined) {
    +    function test(options) {
    +
    +        // Not inserted, lines got merged
    +        log('testing!')(options.list || []).forEach(function(i) {
    +
    +        }); // <- inserted
    +
    +        options.value.test(
    +            'long string to pass here',
    +            'and another long string to pass'
    +        ); // <- inserted
    +
    +        return; // <- inserted, breaks the return statement
    +        { // treated as a block
    +
    +            // a label and a single expression statement
    +            foo: function() {} 
    +        }; // <- inserted
    +    }
    +    window.test = test; // <- inserted
    +
    +// The lines got merged again
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- inserted
    +
    +})(window); //<- inserted
    +
    + + + +

    The parser drastically changed the behavior of the code above, in certain cases +it does the wrong thing.

    + +

    Leading Parenthesis

    + +

    In case of a leading parenthesis, the parser will not insert a semicolon.

    + +
    log('testing!')
    +(options.list || []).forEach(function(i) {})
    +
    + +

    This code gets transformed into one line.

    + +
    log('testing!')(options.list || []).forEach(function(i) {})
    +
    + +

    Chances are very high that log does not return a function; therefore, +the above will yield a TypeError stating that undefined is not a function.

    + +

    In Conclusion

    + +

    It is highly recommended to never omit semicolons, it is also advocated to +keep braces on the same line with their corresponding statements and to never omit +them for one single-line if / else statements. Both of these measures will +not only improve the consistency of the code, they will also prevent the +JavaScript parser from changing its behavior.

    その他

    setTimeout and setInterval

    Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the setTimeout and setInterval functions.

    + + + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // returns a Number > 0
    +
    + +

    When setTimeout gets called, it will return the ID of the timeout and schedule +foo to run in approximately one thousand milliseconds in the future. +foo will then get executed exactly once.

    + +

    Depending on the timer resolution of the JavaScript engine that is running the +code, as well as the fact that JavaScript is single threaded and other code that +gets executed might block the thread, it is by no means a safe bet that one +will get the exact delay that was specified in the setTimeout call.

    + +

    The function that was passed as the first parameter will get called by the +global object, that means, that this inside the called function +refers to that very object.

    + +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this refers to the global object
    +        console.log(this.value); // will log undefined
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    +
    + + + +

    Stacking Calls with setInterval

    + +

    While setTimeout only runs the function once, setInterval - as the name +suggests - will execute the function every X milliseconds. But its use is +discouraged.

    + +

    When code that is being executed blocks the timeout call, setInterval will +still issue more calls to the specified function. This can, especially with small +intervals, result in function calls stacking up.

    + +
    function foo(){
    +    // something that blocks for 1 second
    +}
    +setInterval(foo, 100);
    +
    + +

    In the above code foo will get called once and will then block for one second.

    + +

    While foo blocks the code setInterval will still schedule further calls to +it. Now, when foo has finished, there will already be ten further calls to +it waiting for execution.

    + +

    Dealing with Possible Blocking Code

    + +

    The easiest as well as most controllable solution, is to use setTimeout within +the function itself.

    + +
    function foo(){
    +    // something that blocks for 1 second
    +    setTimeout(foo, 100);
    +}
    +foo();
    +
    + +

    Not only does this encapsulate the setTimeout call, but it also prevents the +stacking of calls and it gives additional control.foo itself can now decide +whether it wants to run again or not.

    + +

    Manually Clearing Timeouts

    + +

    Clearing timeouts and intervals works by passing the respective ID to +clearTimeout or clearInterval, depending which set function was used in +the first place.

    + +
    var id = setTimeout(foo, 1000);
    +clearTimeout(id);
    +
    + +

    Clearing all timeouts

    + +

    As there is no built-in method for clearing all timeouts and/or intervals, +it is necessary to use brute force in order to achieve this functionality.

    + +
    // clear "all" timeouts
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +
    + +

    There might still be timeouts that are unaffected by this arbitrary number; +therefore, is is instead recommended to keep track of all the timeout IDs, so +they can be cleared specifically.

    + +

    Hidden use of eval

    + +

    setTimeout and setInterval can also take a string as their first parameter. +This feature should never be used, since it internally makes use of eval.

    + + + +
    function foo() {
    +    // will get called
    +}
    +
    +function bar() {
    +    function foo() {
    +        // never gets called
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +
    + +

    Since eval is not getting called directly in this case, the string +passed to setTimeout will get executed in the global scope; thus, it will +not use the local variable foo from the scope of bar.

    + +

    It is further recommended to not use a string for passing arguments to the +function that will get called by either of the timeout functions.

    + +
    function foo(a, b, c) {}
    +
    +// NEVER use this
    +setTimeout('foo(1,2, 3)', 1000)
    +
    +// Instead use an anonymous function
    +setTimeout(function() {
    +    foo(a, b, c);
    +}, 1000)
    +
    + + + +

    In Conclusion

    + +

    Never should a string be used as the parameter of setTimeout or +setInterval. It is a clear sign of really bad code, when arguments need +to be supplied to the function that gets called. An anonymous function should +be passed that then takes care of the actual call.

    + +

    Further, the use of setInterval should be avoided since its scheduler is not +blocked by executing JavaScript.

    \ No newline at end of file From 292e3e01065d6db9117e45f05f1089d372685cc1 Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 23:11:26 +0900 Subject: [PATCH 191/641] Translate Intro to Japanese --- doc/ja/intro/index.md | 40 ++++++++++++++++------------------------ site/ja/index.html | 37 ++++++++++++++----------------------- 2 files changed, 30 insertions(+), 47 deletions(-) diff --git a/doc/ja/intro/index.md b/doc/ja/intro/index.md index 6ddd7c71..7928eadb 100644 --- a/doc/ja/intro/index.md +++ b/doc/ja/intro/index.md @@ -1,37 +1,29 @@ -## Intro +## 前書き -**JavaScript Garden** is a growing collection of documentation about the most -quirky parts of the JavaScript programming language. It gives advice to -avoid common mistakes, subtle bugs, as well as performance issues and bad -practices that non-expert JavaScript programmers may encounter on their -endeavours into the depths of the language. +**JavaScript Garden** はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 +このドキュメントはJavaScriptという言語に対して不慣れなプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。 -JavaScript Garden does **not** aim to teach you JavaScript. Former knowledge -of the language is strongly recommended in order to understand the topics covered -in this guide. In order to learn the basics of the language, please head over to -the excellent [guide][1] on the Mozilla Developer Network. +JavaScript GardenはJavaScriptを教える事を**目的にしていません**。このガイドの項目を理解する為には、この言語に対する前提知識がある事を推奨します。この言語の基礎部分についてはMozilla Developer Networkの[ガイド][1] がオススメです。 -## The Authors +## 著者 -This guide is the work of two lovely [Stack Overflow][2] users, [Ivo Wetzel][3] -(Writing) and [Zhang Yi Jiang][4] (Design). +このガイドは2人の愛すべき[Stack Overflow][2]ユーザーである[Ivo Wetzel][3] +(執筆)と[Zhang Yi Jiang][4] (デザイン)によって作られました。 -## Contributors +## 貢献者 - - [Caio Romão][5] (Spelling corrections) - - [Andreas Blixt][6] (Language corrections) + - [Caio Romão][5] (スペル校正) + - [Andreas Blixt][6] (言語校正) -## Hosting +## ホスティング -JavaScript Garden is hosted on GitHub, but [Cramer Development][7] supports us -with a mirror at [JavaScriptGarden.info][8]. +JavaScript GardenはGitHubでホスティングされていますが、[Cramer Development][7]が[JavaScriptGarden.info][8]というミラーサイトを作ってくれています。 -## License +## ライセンス + +JavaScript Gardenは[MIT license][9]の下で公開されており、[GitHub][10]でホスティングされています。もしもエラーやtypoを見つけたら[file an issue][11]に登録するかレポジトリにプルリクエストを送ってください。 +またStack Overflowチャットの[JavaScript room][12]に私達はいます。 -JavaScript Garden is published under the [MIT license][9] and hosted on -[GitHub][10]. If you find errors or typos please [file an issue][11] or a pull -request on the repository. You can also find us in the [JavaScript room][12] on -Stack Overflow chat. [1]: https://developer.mozilla.org/en/JavaScript/Guide [2]: http://stackoverflow.com/ diff --git a/site/ja/index.html b/site/ja/index.html index 60e5d1c9..ff9f4bb6 100644 --- a/site/ja/index.html +++ b/site/ja/index.html @@ -1,40 +1,31 @@ JavaScript Garden -

    イントロ

    JavaScript Garden is a growing collection of documentation about the most -quirky parts of the JavaScript programming language. It gives advice to -avoid common mistakes, subtle bugs, as well as performance issues and bad -practices that non-expert JavaScript programmers may encounter on their -endeavours into the depths of the language.

    +

    前書き

    JavaScript Garden はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 +このドキュメントはJavaScriptという言語に対して不慣れなプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。

    -

    JavaScript Garden does not aim to teach you JavaScript. Former knowledge -of the language is strongly recommended in order to understand the topics covered -in this guide. In order to learn the basics of the language, please head over to -the excellent guide on the Mozilla Developer Network.

    +

    JavaScript GardenはJavaScriptを教える事を目的にしていません。このガイドの項目を理解する為には、この言語に対する前提知識がある事を推奨します。この言語の基礎部分についてはMozilla Developer Networkのガイド がオススメです。

    -

    The Authors

    +

    著者

    -

    This guide is the work of two lovely Stack Overflow users, Ivo Wetzel -(Writing) and Zhang Yi Jiang (Design).

    +

    このガイドは2人の愛すべきStack OverflowユーザーであるIvo Wetzel +(執筆)とZhang Yi Jiang (デザイン)によって作られました。

    -

    Contributors

    +

    貢献者

    -

    Hosting

    +

    ホスティング

    -

    JavaScript Garden is hosted on GitHub, but Cramer Development supports us -with a mirror at JavaScriptGarden.info.

    +

    JavaScript GardenはGitHubでホスティングされていますが、Cramer DevelopmentJavaScriptGarden.infoというミラーサイトを作ってくれています。

    -

    License

    +

    ライセンス

    -

    JavaScript Garden is published under the MIT license and hosted on -GitHub. If you find errors or typos please file an issue or a pull -request on the repository. You can also find us in the JavaScript room on -Stack Overflow chat.

    オブジェクト

    Object Usage and Properties

    Everything in JavaScript acts like an object, with the only two exceptions being +

    JavaScript GardenはMIT licenseの下で公開されており、GitHubでホスティングされています。もしもエラーやtypoを見つけたらfile an issueに登録するかレポジトリにプルリクエストを送ってください。 +またStack OverflowチャットのJavaScript roomに私達はいます。

    オブジェクト

    Object Usage and Properties

    Everything in JavaScript acts like an object, with the only two exceptions being null and undefined.

    false.toString() // 'false'
    
    From b0ec092a040f7174c286142e261176794084fb37 Mon Sep 17 00:00:00 2001
    From: Satoru HIRAKI 
    Date: Sat, 2 Jul 2011 23:11:42 +0900
    Subject: [PATCH 192/641] Fix intro in index.json
    
    ---
     doc/ja/index.json | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/doc/ja/index.json b/doc/ja/index.json
    index 85e25a6b..cea3fb99 100644
    --- a/doc/ja/index.json
    +++ b/doc/ja/index.json
    @@ -4,7 +4,7 @@
         "description": "JavaScriptの奇抜さと欠陥についてのガイドライン",
         "sections": [
             {
    -            "title": "イントロ",
    +            "title": "前書き",
                 "dir": "intro",
                 "articles": []
             },
    
    From 843b159e60daf0609f4a2cecb5eb85059e0d542e Mon Sep 17 00:00:00 2001
    From: Satoru HIRAKI 
    Date: Sun, 3 Jul 2011 00:05:22 +0900
    Subject: [PATCH 193/641] fix typo
    
    ---
     doc/ja/intro/index.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/doc/ja/intro/index.md b/doc/ja/intro/index.md
    index 7928eadb..b2fcfb3c 100644
    --- a/doc/ja/intro/index.md
    +++ b/doc/ja/intro/index.md
    @@ -21,7 +21,7 @@ JavaScript GardenはGitHubでホスティングされていますが、[Cramer D
     
     ## ライセンス
     
    -JavaScript Gardenは[MIT license][9]の下で公開されており、[GitHub][10]でホスティングされています。もしもエラーやtypoを見つけたら[file an issue][11]に登録するかレポジトリにプルリクエストを送ってください。
    +JavaScript Gardenは[MIT license][9]の下で公開されており、[GitHub][10]でホスティングされています。もしもエラーやtypoを見つけたら[file an issue][11]に登録するかリポジトリにプルリクエストを送ってください。
     またStack Overflowチャットの[JavaScript room][12]に私達はいます。
     
     
    
    From f2082d261b5fa806a5cac511ec1d7dd705dfb6d6 Mon Sep 17 00:00:00 2001
    From: Satoru HIRAKI 
    Date: Sun, 3 Jul 2011 00:07:37 +0900
    Subject: [PATCH 194/641] build pl/index.html I don't know why...
    
    ---
     site/pl/index.html | 1914 ++++++++++++++++++++++++++++++++++++++++++++
     1 file changed, 1914 insertions(+)
     create mode 100644 site/pl/index.html
    
    diff --git a/site/pl/index.html b/site/pl/index.html
    new file mode 100644
    index 00000000..cfeeb4fa
    --- /dev/null
    +++ b/site/pl/index.html
    @@ -0,0 +1,1914 @@
    +JavaScript Garden
    +

    Wstęp

    JavaScript Garden jest rosnącą kolekcją dokumentów o najdziwniejszych +częściach języka JavaScript. Dokumentacja pomaga uniknąć najczęściej popełnianych +błędów, sybtelnych bugów, problemów wydajnościowych oraz złych praktyk, na które +niedoświadczeni programiści JavaScript mogą natrafić próbując poznać tajniki tego +języka.

    + +

    JavaScript Garden nie ma na celu nauczyć Cię języka JavaScript. Podstawowa +wiedza na temat języka jest wymagana do zrozumienia zagadnień poruszanych w tym +przewodniku. Aby nauczyć się podstaw jezyka JavaScript, odwiedź znakomity +przewodnik na stronach Mozilla Developer Network.

    Licencja

    JavaScript Garden jest publikowany w ramach licencji MIT i kod źródłowy znajduje +się na serwerze GitHub. Jeśli znajdziesz jakieś błędy lub literówek zgłoś proszę +problem lub rozwiązag go i zglosić pull request ze swojego repozytorium. +Możesz nas także znaleźć w pokoju JavaScript na chacie Stack Overflow.

    Obiekty

    Wykorzystanie obiektów i ich właściwości

    Wszystko w JavaScripcie zachowuje sie jak obiekt, z dwoma wyjątkami +null oraz undefined.

    + +
    false.toString() // 'false'
    +[1, 2, 3].toString(); // '1,2,3'
    +
    +function Foo(){}
    +Foo.bar = 1;
    +Foo.bar; // 1
    +
    + +

    Popularnym błędem jest wykorzystanie literałów liczbowych jako obiektu. +Spowodowanie jest to usterką w parserze JavaScript, który interpretuje kropkę +po literale liczbowym jako rozdzielenie części całkowitej od części po przecinku +liczby.

    + +
    2.toString(); // wyrzuca błąd SyntaxError
    +
    + +

    Istnieje kilka rozwiązań, dzieki którym literał liczbowy będzie zachowywał się +jak obiekt.

    + +
    2..toString(); // druga kropka jest poprawnie rozpoznana
    +2 .toString(); // zauważ, że pozostawiona jest spacja przed kropką
    +(2).toString(); // 2 zostanie zewaluowane najpiewr
    +
    + +

    Obiekty jako typy danych

    + +

    Obiekty w języku JavaScript mogą być używana jako tablice asocjacyjne. +Ponieważ obiekty głównie składają się z mapowań pomiędzy nazwanymi właściwościami (kluczami) +a wartościami dla tych atrybutów.

    + +

    Używając literału obiektu - notacji {} - istnieje możliwość stworzenie obiektu prostego. +Ten nowy obiekt bedzie dziedziczył z Object.prototype oraz +nie bedzie posiadał żadnych własnych właściwości zdefiniowanych w sobie.

    + +
    var foo = {}; // nowy pusty obiekt
    +
    +// nowy obiekt z właściwością test o wartości 12
    +var bar = {test: 12}; 
    +
    + +

    Dostęp do właściwości

    + +

    Właściwości obiektu można uzyskać na dwa sposoby, poprzez notację z kropką +lub notacje z nawiasami kwadratowymi.

    + +
    var foo = {name: 'Kitten'}
    +foo.name; // kitten
    +foo['name']; // kitten
    +
    +var get = 'name';
    +foo[get]; // kitten
    +
    +foo.1234; // wyrzuca błąd SyntaxError
    +foo['1234']; // działa, zwraca undefined
    +
    + +

    Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą że notacja z nawiasami +kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzycenia +błędu podczas odczytu nieistniejącej właściwości.

    + +

    Usuwanie właściwości

    + +

    Jedynym sposobem na faktycze usunięcie własności z obiektu jest użycie operatora +delete. Ustawienie własności na undefined lub null usunie tylko wartość +związaną z własnością, ale nie usunie to klucza (nazwy własności) z obiektu.

    + +
    var obj = {
    +    bar: 1,
    +    foo: 2,
    +    baz: 3
    +};
    +obj.bar = undefined;
    +obj.foo = null;
    +delete obj.baz;
    +
    +for(var i in obj) {
    +    if (obj.hasOwnProperty(i)) {
    +        console.log(i, '' + obj[i]);
    +    }
    +}
    +
    + +

    Powyższy kod wypisuje dwie linie bar undefined i foo null - tylko własność baz +została usunięta i dlatego nie została wypisana.

    + +

    Notacja właściwości

    + +
    var test = {
    +    'case': 'I am a keyword so I must be notated as a string',
    +    delete: 'I am a keyword too so me' // wyrzuca błąd SyntaxError
    +};
    +
    + +

    Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst(bez cudzysłowów +lub apostrofów) lub jako string (w cudzisłowach lub apostrofach). +Ze względu na kolejne niedociągnięcie w parserze JavaScript +powyższy kod wyrzuci błąd SyntaxError dla implementacji JavaScript ponizej ECMAScript 5.

    + +

    Ten błąd wynika z faktu, że delete jest słowem kluczowym, dlatego musi zostać +zapisany jako string (z cudzysłowami lub apostrofami), aby zapewnić, że zostanie +to poprawnie zinterpretowane przez starsze silniki języka JavaScript.

    Prototyp

    JavaScript nie posiada klasycznego modelu dziedziczenia, lecz zamiast tego +dziedziczenie jest realizowane poprzez prototypy.

    + +

    Choć jest to często uważane za jedną ze słabości języka JavaScript, +prototypowy model dziedziczenia, jest w rzeczywistości potężniejszy od klasycznego +modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowym +jest dość proste, podczas gdy zrobienie odwrotnie to już o wiele trudniejsze zadanie.

    + +

    Ze względu na fakt, że w JavaScript jest w zasadzie jedynym powszechnie stosowanym +językiem, któy posiada prototypowy model dziedziczenia, to wymaga troche czasu aby +dostosować się do różnic pomiędzy tymi dwoma modelami.

    + +

    Pierwszą znaczącą różnicą jest to, że dziedziczenie w JavaScript odbywa się za pomocą +tak zwanych łańcuchów prototypów.

    + + + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Ustawienie prototypu Bar na nową instancję Foo
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// Upewniamy się, że Bar jest ustawiony jako rzeczywisty konstruktor
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar() // tworzymy nową instancję Bar
    +
    +// The resulting prototype chain
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo] 
    +        { foo: 'Hello World' }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* etc. */ }
    +
    + +

    W powyższym przykładzie obiekt test będzie dziedziczył z obydwu tj. +Bar.prototyp i Foo.prototyp, stąd będzie miał dostęp do funkcji method, +która była zdefiniowana w Foo. Ponadto obiekt będzie miał dostęp do +właściwości value, która jest jednyną instancją Foo i stała się jego prototypem. +Ważne jest, aby pamiętać new Bar nie tworzy nowej instancji Foo, +ale wykorzystuje instancje, którą jest przypisana do własności prototype. +Zatem Wszystkie instancje Bar będą dzieliły tą samą własność value.

    + + + +

    Wyszukiwanie własności

    + +

    Podczas dostępu do właściwości obiektu, JavaScript przejdzie w górę łańcucha +prototypów dopóki nie znajdzie właściwości z żądaną nazwą.

    + +

    Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha mianowicie Object.prototype +i nadal nie znajdzie określonej właściwości, to zwróci wartość +undefined.

    + +

    Właściwość prototype

    + +

    Podczas gdy właściwość prototype jest używana przez język do budowania łańcucha +prototypów, istnieje możliwość przypisania do niej dowolnej wartości. Jednakże +prymitywne typy będą po prostu ignorowanie, jeżeli zostaną ustawione jako prototype.

    + +
    function Foo() {}
    +Foo.prototype = 1; // nie ma wpływu
    +
    + +

    Przypisywanie obiektów, jak pokazano w powyższym przykładzie, zadziała i pozwala +na dynamiczne tworzenie łańcuchów prototypów.

    + +

    Wydajność

    + +

    Czas wyszukiwania właściwości, które są na końcu łańcucha prototypów może mieć +negatywny wpływ na wydajność krytycznych części kodu. Dodatkowo, próba dostępu +do nieistniejącej właściwości powoduje zawsze przeszukanie całego łańcucha prototypów.

    + +

    Również, podczas iteracji po właściwościach obiektu +każda właściwość, która znajduje się w łańcuchu prototypów niezależnie +na jakim znajduje się poziomie zostanie wyliczona.

    + +

    Rozszerzanie natywnych prototypów

    + +

    Rozszerzanie Object.prototype lub innego prototypu wbudowanych typów jest jednym z +najczęściej używanych niedoskonałej częsci języka JavaScript.

    + +

    Technika ta nazywana jest monkey patching i łamie zasady enkapsulacji. +Jednak jest szeroko rozpowszechniona w frameworkach takich jak Prototype. +Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez dodawanie do nich +niestandardowych funkcjonalności.

    + +

    Jedynym dobrym powodem do rozszerzania wbudowanych prototypów jest portowanie
    +funkcjonalności znajdujących sie w nowszych silnikach JavaScript np. Array.forEach

    + +

    Wnioski

    + +

    Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczanie +należy całkowicie rozumieć prototypowy model dziedziczenia. Ponadto należy uważać +na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń +aby uniknąć problemów z wydajnością. Natywne prototypy nie powinny nigdy być +rozszerzane, chyba że ze względu na wprowadzanie kompatybilności z nowszymi silnikami +JavaScript.

    hasOwnProperty

    W celu sprawdzenia czy dana właściwość została zdefiniowana w tym obiekcie a nie +w łańcuchu prototypów niezbędne jest skorzystanie z metody +hasOwnProperty, która wszystkie obiekty dziedziczą z Object.prototype.

    + + + +

    hasOwnProperty jest jedyna metodą w języku JavaScript która operuje na właściwościach +i nie przegląda całego łańcucha prototypów.

    + +
    // Zatrucie Object.prototype
    +Object.prototype.bar = 1; 
    +var foo = {goo: undefined};
    +
    +foo.bar; // 1
    +'bar' in foo; // true
    +
    +foo.hasOwnProperty('bar'); // false
    +foo.hasOwnProperty('goo'); // true
    +
    + +

    Tylko hasOwnProperty da prawidłowy i oczekiwany rezultat. Jest to istotne podczas +iteracji po właściwościach obiektu. Nie ma innego sposobu na ominięcie +właściwości, która nie została zdefiniowana przez ten konkretny obiekt, +ale gdzieś indziej w łańcuchu prototypów.

    + +

    hasOwnProperty jako właściwość

    + +

    JavaScript nie chroni właściwości o nazwie hasOwnProperty, zatem istnieje +możliwość, że obiekt może posiadać tak nazwaną właściwość. Konieczne jest użycie +zewnętrznego hasOwnProperty, aby otrzymać poprawne rezultaty.

    + +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // zawsze zwraca false
    +
    +// Została użyta metoda innego obiektu i wywołana z konkekstem 
    +// `this` ustawionym na foo
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +
    + +

    Wnioski

    + +

    Jedyną metodą służącą do sprawdzenia zdefiniowania jakiejś właściwości w konkretnym +obiekcie jest metoda hasOwnProperty. Zaleca się korzystać z hasOwnProperty jako część +każdej pętli for in, pozwoli to uniknąć błędów pochodzących z +rozszerzonych natywnych prototypów.

    The for in Loop

    Podobnie jak operator in, pętla for in przeszukuje łańcuch prototypów +podczas iteracji po właściwościach obiektu.

    + + + +
    // Zatrucie Object.prototype
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // wyświetla obie właściwości: bar i moo
    +}
    +
    + +

    Ponieważ nie jest możliwe, aby zmienić zachowanie pętli for in to niezbędne +jest odfiltrowanie niechcianych właściwości wewnątrz ciała pętli, korzystając +z metody hasOwnProperty z Object.prototype.

    + + + +

    Korzystanie z hasOwnProperty do odfiltrowania

    + +
    // foo z przykładu powyżej
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +
    + +

    To jest jedyna poprawna wersja, którą należy używać. Ze względu na użycie +hasOwnProperty zostanie wypisane jedynie moo. Gdy opuścimy hasOwnProperty +kod będzie podatny na błędy, gdy natywne prototypy np. Object.prototype +zostanie rozszerzony.

    + +

    Prototype jest jednym z szeroko rozpowszechniony frameworków, który dokonuje +takiego rozszerzenia. Używanie tego frameworku oraz nie używanie w pętle for in +metody hasOwnProperty gwarantuje błędy w wykonaniu.

    + +

    Wnioski

    + +

    Zaleca się aby zawsze używać metody hasOwnProperty. Nigdy nie powinno się dokonywać +żadnych założeń na temat środowiska, w którym kod będzie wykonywany i czy natywne +prototypy zostały rozszerzone czy nie.

    Funkcje

    Deklaracje funkcji i wyrażenia funkcyjne

    Funcje w języku JavaScript są typami pierwszoklasowymi. Co oznacza, że mogą +być przekazywane jak każda inna wartość. Jednym z typowych zastosowań tej cechy +jest przekazywanie anonimowej funkcji jako callback do innej, prawdopodobnie +asynchronicznej funkcji.

    + +

    Deklaracja funckcji

    + +
    function foo() {}
    +
    + +

    Powyższa funkcja zostaje wyniesiona zanim program wystartuje, dzięki temu +jest dostępna wszędzie w ramach zasięgu, w którym została zadeklarowana, +nawet jeżeli ta funkcja została wywołana przed faktyczną definicją w kodzie źródłowym.

    + +
    foo(); // Działa ponieważ definicja funkcji została wyniesiona 
    +       // na początek zasięgu przed uruchomieniem kodu
    +function foo() {}
    +
    + +

    Wyrażenie funkcyjne

    + +
    var foo = function() {};
    +
    + +

    Ten przykład przypisuje nienazwaną i anonimową funkcję do zmiennej foo.

    + +
    foo; // 'undefined'
    +foo(); // wyrzuca błąd TypeError
    +var foo = function() {};
    +
    + +

    Ze względu na fakt, że deklaracja var wynosi zmienną foo na początek zasięgu, +zanim kod faktycznie zostanie uruchomiony, foo będzie zdefiniowane kiedy skrypt +będzie wykonywany.

    + +

    Ale ponieważ przypisania robione są dopiero podczas wykonania, wartość foo będzie +ustawiona na domyślną wartość undefined zanim powyższy kod +zostanie uruchomiony.

    + +

    Nazwane wyrażenia funkdyjne

    + +

    Kolejnym specjalnym przypadkiem jest przypisanie nazwanej funkcji.

    + +
    var foo = function bar() {
    +    bar(); // Działa
    +}
    +bar(); // wyrzuca ReferenceError
    +
    + +

    W zewnętrznym zakresie bar nie będzie dostępne, ponieważ funkcja zostaje +przypisana do foo, jednakże w wewnętrznym zakresie bar będzie dostępna. +Jest to spowodowane tym, jak działa rozwiązywanie nazw +w języku JavaScript. Nazwa funkcji jest zawsze dostępna w lokalnym +zakresie tej funkcji.

    Jak działa this

    JavaScript posiada inną koncepcję odnośnie tego na co wskazuje specjalna +nazwa this, niż większość innych języków programowania. Istnieją dokładnie +pięć różnych sytuacji w których wartość this zostaje przypisana w języku JavaScript.

    + +

    JavaScript has a different concept of what the special name this refers to +than most other programming languages do. There are exactly five different +ways in which the value of this can be bound in the language.

    + +

    Zasięg globalny

    + +
    this;
    +
    + +

    Używanie this w globalnym zasięgu, zwróci po prostu referencje do obiektu global.

    + +

    Wywołanie funkcji

    + +
    foo();
    +
    + +

    Tutaj this również będzie wkazywało na obiekt global

    + + + +

    Wywoływanie metody

    + +
    test.foo(); 
    +
    + +

    W tym przypadku this będzie wskazywało na test.

    + +

    Wywołanie konstruktora

    + +
    new foo(); 
    +
    + +

    Wywołanie funkcji, które jest poprzedzone słowem kluczowym new zachowuje się +jak konstruktor. Wewnątrz funkcji this będzie +wskazywało na nowo utworzony obiekt.

    + +

    Jawne ustawienie this

    + +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // tablica zostanie zamieniona w to co poniżej
    +foo.call(bar, 1, 2, 3); // rezultat a = 1, b = 2, c = 3
    +
    + +

    Używając metod call lub apply z prototypu Function.prototype, wartość this +wewnątrz wołanej funkcji zostanie jawnie ustawiona na pierwszy argument przekazany +podczas wywołania tych metod.

    + +

    Zatem w powyższym przykładzie przypadek Wywoływanie metody nie będzie miał +miejsca i this wewnątrz foo będzie wskazywać na bar.

    + + + +

    Częste pułapki

    + +

    Mimo iż Większość z tych przypadków ma sens, to pierwszy przypadek powinien być +traktorany jako błąd podczas projektowania języka i nigdy nie wykorzystywany +w praktyce.

    + +
    Foo.method = function() {
    +    function test() {
    +        // wewnątrz tej funkcji this wskazuje na obiekt global
    +    }
    +    test();
    +}
    +
    + +

    Powszechnym nieporozumieniem jest, że this wewnątrz test wskazuje na Foo, +podczas gdy w rzeczywistości tak nie jest.

    + +

    Aby uzyskać dostęp do Foo wewnątrz test niezbędne jest stworzenie wewnątrz +metody lokalnej zmiennej, która będzie wskazywała na Foo.

    + +
    Foo.method = function() {
    +    var that = this;
    +    function test() {
    +        // Należy używać that zamiast this wewnątrz tej funkcji
    +    }
    +    test();
    +}
    +
    + +

    that jest zwykłą zmienną, ale jest to powszechnie stosowana konwencja, aby otrzymać +wartość zewnętrznego this. W połączeniu z domknięciami(closures) +jest to sposób na przekazywanie wartości this wokoło.

    + +

    Metody przypisywania

    + +

    Kolejną rzeczą, która nie działa w języku JavaScript jest nadawanie aliasów +funkcjom, co oznacza przypisanie metody do zmiennej.

    + +
    var test = someObject.methodTest;
    +test();
    +
    + +

    Podobnie jak w pierwszym przypadku test zachowuje się jak wywołanie zwykłej +funkcji, a zatem wewnątrz funkcji this już nie będzie wskazywało someObject.

    + +

    Podczas gdy późne wiązanie this może się na początku wydawać złym pomysłem, +to w rzeczywistości jest to rzecz, która powoduje że +dziedziczenie prototypowe działa.

    + +
    function Foo() {}
    +Foo.prototype.method = function() {};
    +
    +function Bar() {}
    +Bar.prototype = Foo.prototype;
    +
    +new Bar().method();
    +
    + +

    Kiedy metoda method zostanie wywołana na instancji Bar, this będzie +wskazywało właśnie tą instancję.

    Domknięcia i referencje

    Jedną z najpotężniejszych funkcjonalności języka JavaScript są domknięcia, +oznacza to że zasięg zawsze posiada dostęp do zewnętrznego zasięgu w którym +został zdefiniowany. Ponieważ zasięg w JavaScript można definiować tylko poprzez +funckję, wszystkie funkcje domyślnie zachowują się jak domknięcia.

    + +

    Emulowanie prywatnych zmiennych

    + +
    function Counter(start) {
    +    var count = start;
    +    return {
    +        increment: function() {
    +            count++;
    +        },
    +
    +        get: function() {
    +            return count;
    +        }
    +    }
    +}
    +
    +var foo = Counter(4);
    +foo.increment();
    +foo.get(); // 5
    +
    + +

    Tutaj Counter zwraca dwa domknięcia: funkcję increment oraz funckję get. +Obie te funkcję trzymają referencję do zasięgu Counter a co za tym idzie +zawsze posiadają dostęp do zmiennej count tak, jakby ta zmienna była zdefiniowana +w zasięgu tych funkcji.

    + +

    Dlaczego zmienne przywatne działają

    + +

    Ponieważ nie ma możliwości wskazania lub przypisania zasięgu w JavaScript, to +nie istnieje sposób aby uzyskać dostęp do zmiennej count z zewnątrz. +Wykorzystanie tych dwóch domkinęć jest jedynym sposobem na interakcję z tą zmienną.

    + +
    var foo = new Counter(4);
    +foo.hack = function() {
    +    count = 1337;
    +};
    +
    + +

    Powyższy kod nie zmieni wartości zmiennej count wewnątrz zasięgu Counter, +ponieważ foo.hack nie została zadeklarowana wewnątrz tego konkretnego zasięgu. +Zamiast tego funkcja utworzy lub nadpisze globalną zmienną count.

    + +

    Domknięcia wewnątrz pętli

    + +

    Jednym z częstrzych błędów jest wykorzystywanie domknięć wewnątrz pętli, +aby wartość zmiennej po której odbywa się iteracja był kopiowana do +wewnętrznej funkcji.

    + +
    for(var i = 0; i < 10; i++) {
    +    setTimeout(function() {
    +        console.log(i);  
    +    }, 1000);
    +}
    +
    + +

    Powyższy kod nie wypisze numerów od 0 do 9, ale wypisze +dziesięć razy liczbę 10.

    + +

    Anonimowa funkcja trzyma wskaźnik do zmiennej i i podczas uruchomienia +console.log, pętla for już zakończyła działanie i wartość zmiennej i +została ustawiona na 10.

    + +

    Aby otrzymać zamierzony efekt, niezbędne jest skopiowanie wartości +zmiennej i.

    + +

    Unikanie problemu z referencją

    + +

    Aby skopiować wartość zmiennej, po której iterujemy w pętli, należy skorzystać +z anonimowego wrappera.

    + +
    for(var i = 0; i < 10; i++) {
    +    (function(e) {
    +        setTimeout(function() {
    +            console.log(e);  
    +        }, 1000);
    +    })(i);
    +}
    +
    + +

    Zewnętrzna anonimowa funkcja zostaje wywołana od razu z parametrem i +jako pierwszym argumentem i otrzyma kopię wartości zmiennej i jako +zmienną e.

    + +

    Anonimowa funkcja która zostaje przekazana do setTimeout teraz posiada +referencję do zmiennej e, która nie zostanie zmieniona przez pętle for.

    + +

    Istnieje jeszcze jeden sposób na osiągnięcie tego samego efektu. Należy zwrócic +fukcję z anonimowego wrappera, wówczas kod będzie zachowywał się jak ten +wcześniejszy.

    + +
    for(var i = 0; i < 10; i++) {
    +    setTimeout((function(e) {
    +        return function() {
    +            console.log(e);
    +        }
    +    })(i), 1000)
    +}
    +

    Obiekt arguments

    Każda zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej arguments. +Ta zmienna trzyma listę wszystkich argumentów przekazanych do funkcji.

    + + + +

    Obiekt arguments nie jest typu Array. Mimo, że posiada pewne cechy +semantyki tablic - właściwość length - to nie dziedziczy on z Array.prototype, +ale w rzeczywistości z Object.

    + +

    Ze względu na to nie można używać standardowych dla tablic metod takich jak +push, pop czy slice na obiekcie arguments. Mimo, że iteracja przy pomocy +pętli for działa dobrze, to aby skorzystać ze standardowych metod tablicowych +należy skonwertować arguments do prawdziwego obiekt Array.

    + +

    Konwersja do tablicy

    + +

    Poniższy kod zwróci nowy obiekt Array zawierający wszystkie elementy +obiektu arguments.

    + +
    Array.prototype.slice.call(arguments);
    +
    + +

    Jednakże konwersja ta jest wolna i nie jest zalecana w sekcjach, +które mają duży wpływ na wydajność.

    + +

    Przekazywanie argumentów

    + +

    Zalecany sposób przekazywania argumentów z jednej funkcji do następnej +wyglada następująco.

    + +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // do stuff here
    +}
    +
    + +

    Kolejną sztuczką jest użycie razem call i apply w celu stworzenia +szybkich i nieograniczonych wrapperów.

    + +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// Stworzenie nieograniczoną wersję metody "method" 
    +// która przyjmuje parametry: this, arg1, arg2...argN
    +Foo.method = function() {
    +
    +    // Rezultat: Foo.prototype.method.call(this, arg1, arg2... argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +};
    +
    + +

    Parametry formalne i indeksy argumentów

    + +

    Obiekt arguments tworzy funckje getter i setter nie tylko dla swoich +właściwości, ale również dla parametrów formalnych funkcji.

    + +

    W rezultacie zmiana wartości parametru formalnego zmieni również wartość +odpowiadającemu mu wpisowi w obiekcie arguments, zachodzi to również w drugą stronę.

    + +
    function foo(a, b, c) {
    +    arguments[0] = 2;
    +    a; // 2                                                           
    +
    +    b = 4;
    +    arguments[1]; // 4
    +
    +    var d = c;
    +    d = 9;
    +    c; // 3
    +}
    +foo(1, 2, 3);
    +
    + +

    Mity i prawdy o wydajności

    + +

    Obiekt arguments jest zawsze tworzony z wyjątkiem dwóch przypadków, gdy +zmienna o takiej nazwie jest zdefiniowana wewnątrz funkcji lub jeden z parametrów +formalnych funkcji ma taką nazwę. Nie ma znaczenia czy obiekt arguments jest +używany czy nie.

    + +

    Zarówno gettery jak i settery są zawsze tworzone, zatem używanie ich nie ma +praktycznie żadnego wpływu na wydajność. Zwłaszcza w rzeczywistym kodzie, który +wykorzystuje coś więcej niż tylko prosty dostęp do właściwości obiektu arguments.

    + + + +

    Jednakże, istnieje jeden przypadek w którym wydajność drastycznie spada w +nowoczesnych silnikach JavaScript. Ten przypadek to wykorzystanie +arguments.callee.

    + +
    function foo() {
    +    arguments.callee; // operowanie na obiekcie funkcji
    +    arguments.callee.caller; // i obiekcie funkcji wywołującej
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // Normalnie zostałaby wykorzystana metoda inline
    +    }
    +}
    +
    + +

    W powyższym przykładzie foo nie może zostać wykorzystana metoda inline +ponieważ potrzebne są nie tylko informacje na własny temat ale również +na temat funkcji wywołującej. Takie użycie nie tylko uniemożliwia +inlining i korzyści z niej wynikające, ale również stanowi złamanie +zasad enkapsulacji ponieważ ta funkcja jest zależna od kontekstu +w jakim została wywołana.

    + +

    Mocno zalecane jest aby nigdy nie korzystać z arguments.callee +i żadnej jej własności.

    + +

    Konstruktory

    Konstruktory w JavaScript również wyglądają inaczej niż innych języka. Każde +wywołanie funkcji, które jest poprzedone słowem kluczowym new zachowuje się +jak konstruktor.

    + +

    Wewnątrz konstruktora - wywoływanej fukcji - wartość this wskazuje na +nowo utworzony obiekt Object. Prototyp prototype tego +nowego obiektu będzie wskazywał na prototyp prototype obiektu fukcji, +która została wywołana jako konstruktor.

    + +

    Jeżeli wywołana funkcja nie posiada jawnej deklaracji return, wówczas +fukcja domyślnie zwraca wartość this - nowy obiekt.

    + +
    function Foo() {
    +    this.bla = 1;
    +}
    +
    +Foo.prototype.test = function() {
    +    console.log(this.bla);
    +};
    +
    +var test = new Foo();
    +
    + +

    Powyżej wywołanya została funkcja Foo jako konstruktor oraz ustawia +nowo utworzonemu obiektowi właściwość prototype na Foo.prototype.

    + +

    W tym przypadku jawna deklaracja return w funkcji zwraca wartość +ustawioną w deklaracji, ale tylko jeżeli zwracaną wartością jest +obiekt Object.

    + +
    function Bar() {
    +    return 2;
    +}
    +new Bar(); // nowy obiekt
    +
    +function Test() {
    +    this.value = 2;
    +
    +    return {
    +        foo: 1
    +    };
    +}
    +new Test(); // zwrócony obiekt
    +
    + +

    Jeżeli słowo kluczowe new zostanie pominięte funkcja nie zwróci nowego +obiektu.

    + +
    function Foo() {
    +    this.bla = 1; // zostanie ustawiona w obiekcie global
    +}
    +Foo(); // undefined
    +
    + +

    Mimo, że powyższy kod może zadziałać w pewnych przypadkach, w związku +z działaniem this w języku JavaScript to jako +wartość thiszostanie wykorzystany obiekt global.

    + +

    Fabryki

    + +

    Aby móc ominąć słowo kluczowe new konstruktor musi jawnie zwracać wartość.

    + +
    function Bar() {
    +    var value = 1;
    +    return {
    +        method: function() {
    +            return value;
    +        }
    +    }
    +}
    +Bar.prototype = {
    +    foo: function() {}
    +};
    +
    +new Bar();
    +Bar();
    +
    + +

    Oba wywołania Bar zwrócą tą samą rzecz, nowo utworzony obiekt, który posiada +właściwość nazwaną method w sobie, dla którego Bar jest Domknięciem.

    + +

    Należy również pamiętać, że wywołanie new Bar() nie ma wpływu na +prototyp zwróconego obiektu (prototypem będzie object.prototype a nie Bar.prototype). +Podczas gdy prototyp zostanie przypisany do nowo utworzonego obiektu, to jednak Bar +nidgy nie zwróci tego nowego obiektu Bar, ale literał obiektu, który jest po +słowie kluczowym return.

    + +

    W powyższym przykładzie nie ma żadnej różnicy w działaniu pomiędzy użyciem +i nieużyciem słowa kluczowego new.

    + +

    Tworzenie nowych obiektów korzystając z fabryk

    + +

    Często zaleca się nie korzystać z operatora new ponieważ zapominając +go zastosować może prowadzić do błędów.

    + +

    W celu stworzenia nowego obiektu, powinno się używać fabryki i konstruować +nowy obiekt wewnątrz tej fabryki.

    + +
    function Foo() {
    +    var obj = {};
    +    obj.value = 'blub';
    +
    +    var private = 2;
    +    obj.someMethod = function(value) {
    +        this.value = value;
    +    }
    +
    +    obj.getPrivate = function() {
    +        return private;
    +    }
    +    return obj;
    +}
    +
    + +

    Mimo, że powyższy kod jest odporny na brak słowa kluczowego new i ułatwia +korzystanie ze zmiennych prywatnych, to posiada +pewne wady. +While the above is robust against a missing new keyword and certainly makes +the use of private variables easier, it comes with some +downsides. + 1. Zużywa więcej pamięci, ponieważ tworzony obiekt nie współdzieli metod + poprzez prototyp + 2. Aby móc dziedziczyć fabryka musi skopiować wszystkie metody z dziedziczonego + obiektu lub przypisać ten obiekt, z którego się dziedziczy, jako prototyp + do nowo utworzonego obiektu. + 3. Porzucenie łańcucha prototypów tylko ze względu na opuszczone słowo kluczowe + new jest sprzeczne z duchem języka.

    + +

    Wnioski

    + +

    Pominięcie słowa kluczowego new może prowadzić do błędów, ale na pewno nie +powinno to być powodem odrzucenia używania prototypów w ogóle. Sprowadza się to +do wyboru rozwiązania, które bardziej pasuje do potrzeb aplikacji. Szczególnie +ważne jest aby wybrać określony styl tworzenia obiektów i się go trzymać.

    Zasięg zmiennych i przestrzenie nazw

    Mimo, że JavaScript radzi sobie dobrze ze składnią, opisującą dwa pasujące +nawiasy klamrowe jako blok, to jednak nie wspiera zasięgu blokowego. +Jedynym zasięgiem jaki istnieje w JavaScript jest zasięg funkcyjny.

    + +
    function test() { // definiuje zasięg (scope)
    +    for(var i = 0; i < 10; i++) { // nie definiuje zasięgu (scope)
    +        // count
    +    }
    +    console.log(i); // 10
    +}
    +
    + + + +

    W JavaScripcie nie ma również przestrzeni nazw, co oznacza, że wszystko jest +definiowane w jednej globalnie współdzielonej przestrzeni nazw.

    + +

    Za każdym razem gdy zmienna jest +Z każdym odwołaniem do zmiennej JavaScript przeszukuje w górę wszystkie zasięgi +dopóki nie znajdzie tej zmiennej. W przypadku gdy przeszukiwanie dotrze do globalnego +zasięgu i nadal nie znajdzie żądanej nazwy to wyrzuca błąd ReferenceError.

    + +

    Zmora globalnych zmiennych

    + +
    // script A
    +foo = '42';
    +
    +// script B
    +var foo = '42'
    +
    + +

    Powyższe dwa skrypty nie dają tego samego efektu. Skrypt A definiuje zmienna +nazwaną foo w globalnym zasięgu natomiast skrypt B definiuje foo +w aktualnym zasięgu.

    + +

    Jeszcze raz, to wcale nie daje tego samego efektu, nie użycie var może mieć +poważne konsekwencje.

    + +
    // globalny zasięg
    +var foo = 42;
    +function test() {
    +    // lokalny zasięg
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +
    + +

    Pominięcie słowa var w deklaracji wewnątrz funkcji test nadpisze wartość +zmiennej globalnej foo. Mimo, że nie wygląda to na początku jak duży problem, +to posiadając wiele tysięcy linii kodu w JavaScript i nie korzystanie z var +wprowadzi straszne i trudne do wyśledzenia błędy.

    + +
    // globalny zasięg 
    +var items = [/* jakaś lista */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // scope of subLoop
    +    for(i = 0; i < 10; i++) { // brakuje słowa var w deklaracji
    +        // do amazing stuff!
    +    }
    +}
    +
    + +

    Zewnętrzna pętla zakończy działanie po pierwszym wywołaniu subLoop, ponieważ +subLoop nadpisuje wartość globalnej zmiennej i. Użycie var w drugiej pętli +for pozwoliło by łatwo uniknąć problemu. Słowo kluczowe var nie powinno być +nigdy pominięte w deklaracji, chyba że pożądanym skutkiem jest wpłynięcie na +zewnętrzny zasięg.

    + +

    Lokalne zmienne

    + +

    Jedynym źródłem zmiennych lokalnych w JavaScripcie są parametry funkcji +oraz zmienne zadeklarowane poprzez deklaracje var wewnątrz funkcji.

    + +
    // globalny zasięg
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // lokalny zasięg fukcji test
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +
    + +

    Zmienne foo oraz i są lokalnymi zmiennymi wewnątrz zasiegu funkcji test, +natomiast przypisanie wartości do bar nadpisze zmienną globalną o tej samej nazwie.

    + +

    "Hoisting" - wywindowanie, podnoszenie

    + +

    JavaScript winduje deklaracje. Oznacza to, że zarówno deklaracja ze słowem +kluczowym var jak i deklaracje funkcji function zostaną przeniesione na +początek otaczającego zasięgu.

    + +
    bar();
    +var bar = function() {};
    +var someValue = 42;
    +
    +test();
    +function test(data) {
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        var goo = 2;
    +    }
    +    for(var i = 0; i < 100; i++) {
    +        var e = data[i];
    +    }
    +}
    +
    + +

    Powyższy kod zostanie przekształcony przed rozpoczęciem wykonania. JavaScript +przeniesie deklarację zmiennej var oraz deklarację funkcji function na szczyt +najbliższego zasięgu.

    + +
    // deklaracje var zostaną przeniesione tutaj
    +var bar, someValue; // ustawione domyślnie na 'undefined'
    +
    +// deklaracje funkcji zostaną również przeniesione na górę
    +function test(data) {
    +    var goo, i, e; // brak blokowego zasięgu spowoduje przeniesienie tutaj
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // powoduje błąd TypeError ponieważ bar jest nadal 'undefined'
    +someValue = 42; // przypisania nie zostają zmienione przez 'hoisting'
    +bar = function() {};
    +
    +test();
    +
    + +

    Brak blokowego zasięgu nie tylko przeniesie deklaracje var poza ciało pętle, +ale również spowoduje, że niektóre porównania if staną się nieintuicyjne.

    + +

    W oryginalnym kodzie wewnątrz deklaracja if zdaje się modyfikować zmienną +globalną goo, podczas gdy faktycznie modyfikuje ona zmienną lokalną - po tym +jak zostało zastosowane windowanie (hoisting).

    + +

    Bez wiedzy na temat podnoszenia (hoistingu), poniższy kod może wydawać się +wyrzucać błąd ReferenceError.

    + +
    // sprawdz czy SomeImportantThing zostało zainicjalizowane
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +
    + +

    Oczywiście powyższy kod działa ze względu na fakt, że deklaracja var zostanie +przeniesiona na początek globalnego zasięgu.

    + +
    var SomeImportantThing;
    +
    +// inny kod który może ale nie musi zainicjalizować SomeImportantThing
    +
    +// upewnienie sie że SomeImportantThing zostało zainicjalizowane
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +
    + +

    Kolejność rozwiązywania nazw

    + +

    Wszystkie zasięgi w JavaScripcie, włączając globalny zasięg, posiadają +zdefiniowana wewnątrz specjalną nazwę this, która wskazuje +na aktualny obiekt.

    + +

    Zasięg funkcyjny posiada również zdefiniowaną wewnętrznie nazwę +arguments, która zawiera listę argumentów przekazaną do +funkcji.

    + +

    Na przykład, kiedy próbujemy odczytać zmienną foo wewnątrz zasięgu funkcji, +JavaScript będzie szukać nazwy w określonej kolejności: + 1. Jeżeli wewnątrz aktualnego zasięgu znajduje się deklaracja var foo skorzystaj z niej. + 2. Jeżeli jeden z parametrów fukcji został nazwany foo użyj go. + 3. Jeżeli fukcja została nazwana foo skorzystaj z tego. + 4. Przejdz do zewnętrznego zasięgu i przejdz do kroku #1.

    + + + +

    Przestrzenie nazw

    + +

    Powszechnym problemem posiadania tylko jednej globalnej przestrzeni nazw jest +prawdopodobieństwo wystąpienia kolizji nazw. W JavaScripcie, można łatwo uniknąć +tego problemu korzystając z anonimowych wrapperów.

    + +
    (function() {
    +    // autonomiczna "przestrzeń nazw"
    +
    +    window.foo = function() {
    +        // wyeksponowane domkniecie (closure)
    +    };
    +
    +})(); // natychmiastowe wykonanie funkcji
    +
    + +

    Nienazwane funkcje są rozpoznane jako wyrażenia, więc +aby mogły zostać wywołane muszą zostać zewaluowane.

    + +
    ( // zewaluowanie fukcji znajdującej się wewnątrz nawiasów
    +function() {}
    +) // zwrócenie obiektu funkcji
    +() // wywołanie rezultatu ewaluacji
    +
    + +

    Istnieją inne sposoby aby zewaluować i wykonać wyrażenie funkcyjne. Mimo, że +mają inną składnie, zachowują się dokładnie tak samo.

    + +
    // Trzy inne sposoby
    +!function(){}();
    ++function(){}();
    +(function(){}());
    +
    + +

    Wnioski

    + +

    Zaleca się aby zawsze używać anonimowych wrapperów do hermetyzacji kodu wewnątrz +jego własnej przestrzeni nazw. To nie tylko chroni kod przed kolizją nazw, ale +również wprowadza lepszą modularyzację programów.

    + +

    Ponadto, stosowanie zmiennych globalnych jest uznawane za złą praktykę. +Jakiekolwiek wykorzystanie zmiennych globalnych wskazuje na źle napisany kod, +który jest podatny na błędy i trudny do utrzymania.

    Tablice

    Iterowanie po tablicach oraz właściwości tablic

    Mimo, że tablice w JavaScript są obiektami, nie ma dobrych powodów do używania +pętli for in do iteracji po nich. W rzeczywiści istnieje +wiele dobrych powodów przeciwko wykorzystania for in na tablicach.

    + + + +

    Ponieważ pętla for in wylicza wszystkie właściwości, które są wewnątrz +łańcucha prototypów i jedynym sposobem aby wykluczyć te właściwości to użycie +hasOwnProperty, ale wówczas pętla staje się +dwadzieście razy wolniejsza od normalnej pętli for.

    + +

    Iteracja

    + +

    W celu osiągnięcia najlepszej wydajności podczas iteracji po tablicach należy +użyć klasycznej pętli for.

    + +
    var list = [1, 2, 3, 4, 5, ...... 100000000];
    +for(var i = 0, l = list.length; i < l; i++) {
    +    console.log(list[i]);
    +}
    +
    + +

    Jest tam jeszcze jeden dodatkowy haczyk w przykładzie powyżej. Jest to zbuforowanie +długości tablicy poprzez l = list.length.

    + +

    Mimo, że właściwość length jest zdefiniowana w wewnątrz tablicy, istnieje nadal +dodatkowy koszt na wyszukiwanie tej właściwości przy każdej iteracji w pętli. +Chociaż najnowsze silniki JavaScript mogą zastosować optymalizację w tym +przypadku. Nie ma jednak możliwość ustalenia czy kod będzie wykonywany w jednym +z tych nowych silników czy też nie.

    + +

    W rzeczywistości pomijając buforowanie długości tablicy może spowodować, że pętla +będzie tylko w połowie tak szybka jak ta z buforowaniem długości.

    + +

    Właściwość length

    + +

    Mimo, że getter właściwości length po prostu zwraca liczbę elementów, które są +zawarte w tablicy, to setter może być użyta do skracania tablicy.

    + +
    var foo = [1, 2, 3, 4, 5, 6];
    +foo.length = 3;
    +foo; // [1, 2, 3]
    +
    +foo.length = 6;
    +foo; // [1, 2, 3]
    +
    + +

    Przypisanie mniejszej długości spowoduje skrócenie tablicy, ale zwiększenie wartości +length nie ma żadnego wpływu na tablicę.

    + +

    Wnioski

    + +

    Aby uzyskać najlepszą wydajność zaleca się, aby zawsze używać zwykłej pętli for +i zbuforowanie właściwości length. Korzystanie z pętli for in na tablicy jest +znakiem źle napisanego kodu, który jest podatny na błędy i ma słabą wydajność.

    Konstruktor Array

    Zaleca się zawsze korzystać z literału tablicy - notacja [] - podczas tworzenia +nowych tablic, ponieważ konstruktor Array niejednoznacznie interpretuje +parametry do niego przekazane.

    + +
    [1, 2, 3]; // Rezultat: [1, 2, 3]
    +new Array(1, 2, 3); // Rezultat: [1, 2, 3]
    +
    +[3]; // Rezultat: [3]
    +new Array(3); // Rezultat: []
    +new Array('3') // Rezultat: ['3']
    +
    + +

    W przypadku gdy tylko jeden argument zostanie przekazany do kostruktora Array i +ten argument jest typu Number, konstruktor zwróci nową dziwną tablicę +z ustawioną właściwością length na wartość przekazaną jako argument. Należy +zauważyć, że tylko właściwość length zostanie ustawiona w ten sposób, +rzeczywiste indeksy w tej tablicy nie zostaną zainicjalizowane.

    + +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // zwraca false, indeks nie został ustawiony
    +
    + +

    Możliwość ustanienia z góry długości tablicy jest użyteczna tylko w kilku +przypadkach, jak powtarzanie ciągu znaków, w którym unika się stosowania +pętli for.

    + +
    // count - ilosc powtorzen
    +// stringToRepeat - ciąg znaków do powtórzenia 
    +new Array(count + 1).join(stringToRepeat); 
    +
    + +

    Wnioski

    + +

    W miare możliwości należy unikać używania konstruktora Array. Literały są +zdecydowanie lepszym rozwiązaniem, są krótsze i mają bardziej precyzyjną składnię. +Zwiększają również czytelność kodu.

    Typy

    Równość i porównania

    JavaScript posiada dwa różne sposoby równościowego porównywania obiektów.

    + +

    Operator równości

    + +

    Operator równości składa się z dwóch znaków "równa się": ==

    + +

    JavaScript jest słabo typowanym językiem. Oznacza to, że operator równości +konwertuje typy (dokonuje koercji), aby wykonać porównanie.

    + +
    ""           ==   "0"           // false
    +0            ==   ""            // true
    +0            ==   "0"           // true
    +false        ==   "false"       // false
    +false        ==   "0"           // true
    +false        ==   undefined     // false
    +false        ==   null          // false
    +null         ==   undefined     // true
    +" \t\r\n"    ==   0             // true
    +
    + +

    Powyższa tabela przedstawia wyniki koercji typów. Nieprzewidywalne wyniki +porównania są głównym powodem, że stosowanie == jest powszechnie uważane za złą +praktykę. Skomplikowane reguły konwersji są powodem trudnych do wyśledzenia błędy.

    + +

    Ponadto koercja ma również wpływ na wydajność na przykład gdy typ String musi zostać +przekształcony na typ Number przed porównaniem z drugą liczbą.

    + +

    Operator ścisłej równości

    + +

    Operator ścisłej równości składa się z trzech znaków "równa się": ===

    + +

    Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem nie +dokonuje koercji typów przed porównaniem.

    + +
    ""           ===   "0"           // false
    +0            ===   ""            // false
    +0            ===   "0"           // false
    +false        ===   "false"       // false
    +false        ===   "0"           // false
    +false        ===   undefined     // false
    +false        ===   null          // false
    +null         ===   undefined     // false
    +" \t\r\n"    ===   0             // false
    +
    + +

    Powyższe rezultaty są o wiele bardziej przejrzyste. Powoduje to "ustatycznienie" +języka do pewnego stopnia oraz pozwala na wprowadzenie optymalizacji porównań +obiektów o różnych typach.

    + +

    Porównywanie obiektów

    + +

    Mimo, że oba operatory == i === nazywane są operatorami równościowymi, +to zachowują się różnie gdy jednym z operandów jest obiekt typu Object.

    + +
    {} === {};                   // false
    +new String('foo') === 'foo'; // false
    +new Number(10) === 10;       // false
    +var foo = {};
    +foo === foo;                 // true
    +
    + +

    Oba operatory porównują toższmość a nie równość, czyli będą porównywać czy +jeden i drugi operand jest tą samą instancją obiektu, podobnie jak operator +is w Pythonie i porównanie wskaźników w C.

    + +

    Wnioski

    + +

    Zaleca się aby używać tylko operatora ścisłej równości. W sytuacjach gdy +potrzebna jest koercja (porównanie obiektów różnych typów), konwersja powinna +być dokonana jawnie a nie pozostawiona trudnym regułom koercji +obowiązującym w języku.

    Operator typeof

    Operator typeof (razem z operatorem instanceof) jest +prawdopodobnie najwiekszą wadą konstrukcji języka JavaScript, jest on praktycznie +całkowicie wadliwy.

    + +

    Mimo, że instanceof ma swoje wady to nadal ma ograniczone zastosowanie w praktyce, +natomiast typeof ma tylko jeden praktyczny przypadek użycia, który na dodatek +nie jest związany z sprawdzaniem typu obiektu.

    + + + +

    Tablica typów JavaScript

    + +
    Wartość             Klasa      Typ
    +-------------------------------------
    +"foo"               String     string
    +new String("foo")   String     object
    +1.2                 Number     number
    +new Number(1.2)     Number     object
    +true                Boolean    boolean
    +new Boolean(true)   Boolean    object
    +new Date()          Date       object
    +new Error()         Error      object
    +[1,2,3]             Array      object
    +new Array(1, 2, 3)  Array      object
    +new Function("")    Function   function
    +/abc/g              RegExp     object (function w Nitro i V8)
    +new RegExp("meow")  RegExp     object (function w Nitro i V8)
    +{}                  Object     object
    +new Object()        Object     object
    +
    + +

    W powyższej tabeli Typ odnosi się do wartości zwracanej przez operator typeof. +Wyraźnie widać, że zwracane wartości w ogóle nie są spójne.

    + +

    Klasa odnosi sie do wartości wewnętrznej właściwości [[Class]] obiektu.

    + + + +

    W celu uzyskania wartości właściwości [[Class]] trzeba skorzystać z metody +toString z Object.prototype.

    + +

    Klasa obiektu

    + +

    Specyfikacja zawiera dokładnie jeden sposób dostepu do wartości [[Class]], +wykorzystując Object.prototype.toString.

    + +
    function is(type, obj) {
    +    var clas = Object.prototype.toString.call(obj).slice(8, -1);
    +    return obj !== undefined && obj !== null && clas === type;
    +}
    +
    +is('String', 'test'); // true
    +is('String', new String('test')); // true
    +
    + +

    Powyższy przykład wywołuje Object.prototype.toString z wartością +this ustawioną na obiekt, dla której wartość właściwości +[[Class]] ma zostać odczytana.

    + + + +

    Testowanie niezdefiniowania zmiennej

    + +
    typeof foo !== 'undefined'
    +
    + +

    Powyższy kod sprawdza czy foo została faktycznie zadeklarowana czy też nie. +Próba odwołania się do zmiennej spowodowała by wyrzucenie błędu ReferenceError. +Jest to jedyne praktyczne wykorzystanie operatora typeof.

    + +

    Wnioski

    + +

    W celu sprawdzenia typu obiektu zalecane jest skorzystanie z +Object.prototype.toString, ponieważ jest to jedyny wiarygodny sposób. Jak +pokazano w powyższej tabeli typów, niektóre wartości zwracane przez typeof nie +są zdefiniowane w specyfikacji, co za tym idzie mogą się różnić w różnych +implementacjach.

    + +

    O ile nie operator typeof nie jest użyty do sprawdzania czy zmienna została +zdefiniowana, powinien być unikany o ile to tylko możliwe.

    Operator instanceof

    Operator instanceof porównuje konstruktory obiektów przekazanych jako operendy. +Jest on jedynie użyteczny do porównywania obiektów utworzonych klas. Stosowanie +go na wbudowanych typach jest praktycznie tak samo bezużyteczne jak operatora +typeof.

    + +

    Porównywanie obiektów utworzonych klas

    + +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // true
    +new Bar() instanceof Foo; // true
    +
    +// Poniżej kod który przypisuje do Bar.prototype obiekt funkcji Foo
    +// a nie faktyczną instancję Foo
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // false
    +
    + +

    Stosowanie instanceof na natywnych typach

    + +
    new String('foo') instanceof String; // true
    +new String('foo') instanceof Object; // true
    +
    +'foo' instanceof String; // false
    +'foo' instanceof Object; // false
    +
    + +

    Jedną ważną rzeczą, którą należy zauważyć jest to, że instanceof nie zadziała +na obiektach, które pochodzą z różnych kontekstów JavaScript (np. z różnych +dokumentów wewnątrz przeglądarki), ponieważ ich konstruktory nie będą tymi +samymi obiektami.

    + +

    Wnioski

    + +

    Operator instanceof powinien być tylko używany podczas korzystania z obiektów +klas utworzonych, które były zdefiniowane w tym samym kontekscie JavaScriptowym. +Podobnie jak operator typeof, należy unikać korzystania +z tego operatora w innych sytuacjach.

    Rzutowanie typów

    JavaScript jest językiem słabo typowanym, co za tym idzie będzie stosować koercję +typów gdziekolwiek jest to możliwe.

    + +
    // These are true
    +new Number(10) == 10; // Number.toString() zostanie przekształcone
    +                      // z powrotem do liczby
    +
    +10 == '10';           // Stringi zostaną przekształcone do typu Number
    +10 == '+10 ';         // Kolejne wariacje
    +10 == '010';          // i następne
    +isNaN(null) == false; // null zostanie przekształcony do 0
    +                      // który oczywiście nie jest NaN
    +
    +// Poniższe zwracają false
    +10 == 010;
    +10 == '-10';
    +
    + + + +

    Aby uniknąć powyższych problemów, należy koniecznie skorzystać ze +ściełego operatora równości. Mimo, że pozwala to uniknąć wiele +typowych problemów to nadal istnieje wiele innych, które powstają na bazie słabego +typowania języka JavaScript.

    + +

    Konstruktory typów wbudowanych

    + +

    Konstruktory typów wbudowanych takich, jak Number lub String zachowują się +inaczej jeżeli są poprzedzone słowem kluczowym new a inaczej jeżeli nie są.

    + +
    new Number(10) === 10;     // False, Object i Number
    +Number(10) === 10;         // True, Number i Number
    +new Number(10) + 0 === 10; // True, ponieważ dokonano jawnej konwersji
    +
    + +

    Korzystanie z wbudowanych typów jak Number jako konstruktor utworzy nowy obiekt +typu Number, natomiast opuszczenie słowa kluczowego new spowoduje, że funkcja +Number zachowa się jak konwerter.

    + +

    Ponadto, użycie literałów lub wartości nieobiektowych zaowocuje jeszcze większą +ilością rzutowań (koercją) typów.

    + +

    Najlepszym rozwiązaniem jest jawne rzutowanie do jednego z trzech typów.

    + +

    Rzutowanie do typu String

    + +
    '' + 10 === '10'; // true
    +
    + +

    Konkatenacja pustego stringu i wartości powoduje rzutowanie do typu String.

    + +

    Rzutowanie do typu Number

    + +
    +'10' === 10; // true
    +
    + +

    Zastosowanie unarnego operatora + spowoduje rzutowanie do typu Number.

    + +

    Rzutowanie do typu Boolean

    + +

    Używając dwukrotnie operatora negacji dowolna wartość może zostać zrzutowana +do typu Boolean

    + +
    !!'foo';   // true
    +!!'';      // false
    +!!'0';     // true
    +!!'1';     // true
    +!!'-1'     // true
    +!!{};      // true
    +!!true;    // true
    +

    Jądro

    Dlaczego nie należy używać eval

    Funkcja eval uruchomi podany string jako kod JavaScript w lokalnym zasięgu (scopie).

    + +
    var foo = 1;
    +function test() {
    +    var foo = 2;
    +    eval('foo = 3');
    +    return foo;
    +}
    +test(); // 3
    +foo; // 1
    +
    + +

    Niestaty eval zostanie wykonana w lokalnym zasięgu tylko jeżeli została wywołana +bezpośrednio i nazwa wołanej funkcji równa sie eval.

    + +
    var foo = 1;
    +function test() {
    +    var foo = 2;
    +    var bar = eval;
    +    bar('foo = 3');
    +    return foo;
    +}
    +test(); // 2
    +foo; // 3
    +
    + +

    Należy unikać stosowania eval o ile to tylko możliwe. W 99.9% przypadków można +osiągnąć ten sam efekt nie używając eval.

    + +

    eval w przebraniu

    + +

    Funkcje wykonywane po upływie czasu setTimeout i setInterval +mogą przyjąć string jako pierwszy argument. String ten zostanie zawsze wykonany +w globalnym zasięgu, ponieważ funkcja eval zostanie wywołana niebezpośrednio w tym +przypadku.

    + +

    Problemy z bezpieczeństwem

    + +

    Funkcja eval jest również problematyczna od strony bezpieczeństwa, ponieważ +wykonuje każdy kod, który zostanie do niej przekazany i nie należy nigdy +używać jej na stringach nieznanego lub niezaufanego pochodzenia.

    + +

    Wnioski

    + +

    Funkcja eval nie powinna być w ogole używana, każdy kod, który ją wykorzystuje +powinien zostać sprawdzony pod względem działania, wydajności i bezpieczeństwa. +W przypadku gdy użycie eval jest niezbędne do działania, wówczas taki kod +należy przemyśleć raz jeszcze i ulepszyć kod aby nie wymagał użycia eval.

    undefined i null

    JavaScript ma dwie różne wartości dla pustych wartości, bardziej użyteczną +z tych dwóch jest undefined.

    + +

    Wartość undefined

    + +

    undefined jest typem z dokładnie jedną wartością: undefined.

    + +

    Język również definiuje globalną zmienną, która ma wartość undefined, zmienna +ta jest nazwana undefined. Jednakże jest to zmienna a nie stała czy słowo +kluczowe w języku. Oznacza to że możliwe jest nadpisanie wartości tej zmiennej.

    + + + +

    Kilka przykładów kiedy wartość undefined jest zwracana:

    + +
      +
    • Dostęp do (niemodyfikowalnej) zmiennej globalnej undefined.
    • +
    • Wyjście z funkcji, która nie ma deklaracji return.
    • +
    • Deklaracja return, która nic jawnie nie zwraca.
    • +
    • Poszukiwanie nieistniejącej właściwości.
    • +
    • Parametr funkcji, który nie został jawnie przekazany podczas wywołania funkcji
    • +
    • Wszystko co zostało ustawione na wartość undefined
    • +
    + +

    Obsługa przypadku zmiany wartości undefined

    + +

    Ponieważ globalna zmienna undeined tylko zawiera kopię prawdziwej wartości typu +undefined, przypisanie nowej wartości do tej zmiennej nie zmienia wartości +typu undefined.

    + +

    Jednak, aby porównać coś do wartości undefined potrzebne jest odczytanie wartości +undefined.

    + +

    Aby uchronić swój kod przeciwko możliwemu nadpisaniu zmiennej undefined, korzysta +się z powszechnej techniki dodania dodatkowego parametru do +anonimowego wrappera, do którego nie zostanie przekazany +argument.

    + +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // undefined lokalnym zasięgu znowu 
    +    // odnosi się do poprawnej wartości
    +
    +})('Hello World', 42);
    +
    + +

    Kolejnym sposobem aby osiągnąć ten sam efekt jest użycie deklaracji zmiennej +wewnątrz wrappera.

    + +
    var undefined = 123;
    +(function(something, foo) {
    +    var undefined;
    +    ...
    +
    +})('Hello World', 42);
    +
    + +

    Jedyną różnicą pomięcy tymi sposobami są dodatkowe 4 bajty przeznaczone na słowo +kluczowe var i spację po nim.

    + +

    Zastosowanie null

    + +

    Podczas gdy undefined w kontekście języka jest używany jak null w sensie +tradycyjnych języków, to null w JavaScript (jako literał i jako typ) jest po +prostu kolejnym typem danych.

    + +

    Jest wykorzystywany we wnętrzu JavaScript (np. deklaracji końca łańcucha prototypów +poprzez ustawienie Foo.prototype = null), ale prawie w każdym przypadku można go +zastąpić przez undefined.

    Automatyczne wstawianie średnika

    Mimo, że JavaScript ma składnię podobną do języka C, to nie wymusza stosowania +średników w kodzie źródłowym. Istnieje możliwość ich pominięcia.

    + +

    Lecz JavaScript nie jest językiem bez średników, tak na prawdę potrzebuje +średników aby zinterpretować kod źródłowy. Jednakże parser JavaScript +automatycznie wstawia średniki o ile napotka błąd parsowania związany z +brakiem średnika.

    + +
    var foo = function() {
    +} // błąd parsowania, oczekiwany był w tym miejscu średnik
    +test()
    +
    + +

    Parser dodaje średnik, i próbuje jeszcze raz sparsować skrypt.

    + +
    var foo = function() {
    +}; // bez błędu parser kontynuuje
    +test()
    +
    + +

    Automatyczne wstawianie średników jest uważane za jeden z największych błędów +konstrukcji języka, ponieważ może ono zachowanie kodu.

    + +

    Jak działa wstawianie

    + +

    Kod poniżej nie ma żadnych średników, więc parser zdecyduje, w których miejscach +je wstawi.

    + +
    (function(window, undefined) {
    +    function test(options) {
    +        log('testing!')
    +
    +        (options.list || []).forEach(function(i) {
    +
    +        })
    +
    +        options.value.test(
    +            'long string to pass here',
    +            'and another long string to pass'
    +        )
    +
    +        return
    +        {
    +            foo: function() {}
    +        }
    +    }
    +    window.test = test
    +
    +})(window)
    +
    +(function(window) {
    +    window.someLibrary = {}
    +
    +})(window)
    +
    + +

    Poniżej znajduje się rezultat "zgadywania" parsera.

    + +
    (function(window, undefined) {
    +    function test(options) {
    +
    +        // Nie wstaniony średnik, linie zostały połączone
    +        log('testing!')(options.list || []).forEach(function(i) {
    +
    +        }); // <- wstawiony
    +
    +        options.value.test(
    +            'long string to pass here',
    +            'and another long string to pass'
    +        ); // <- wstawiony
    +
    +        return; // <- wstawiony, psując deklarację return
    +        { // potraktowane jako definicja bloku
    +
    +            // etykieta oraz pojedyncze wyrażenie
    +            foo: function() {} 
    +        }; // <- wstawiony
    +    }
    +    window.test = test; // <- wstawiony
    +
    +// Kolejna połączona linia
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- wstawiony
    +
    +})(window); //<- wstawiony
    +
    + + + +

    Parser drastycznie zmienił działanie powyższego kodu, w niektórych przypadkach +zmienił go źle.

    + +

    Nawiasy

    + +

    W przypadku, gdy w następnej linii znajduje się nawias, parser nie wstawi +średnika.

    + +
    log('testing!')
    +(options.list || []).forEach(function(i) {})
    +
    + +

    Ten kod zostanie zmieniony w poniższą jedną linię.

    + +
    log('testing!')(options.list || []).forEach(function(i) {})
    +
    + +

    Jest bardzo prawdopodobne, że log nie zwróci fukcji, co za tym idzie +powyższy kod wyrzuci błąd TypeError oznajmując, że undefined is not a +function - undefined nie jest funkcją.

    + +

    Wnioski

    + +

    Zaleca się aby nigdy nie pomijać średników, pozostawiać nawias otwierający +w tej samej linii co odpowiadająca mu definicja i nigdy nie pozostawiać deklaracji +if / else bez nawiasów nawet jeżeli są jednolinijkowe. Wszystkie te uwagi nie +tylko pomagają poprawić spójność kodu, ale również zapobiegają parser JavaScript +przed zmianą działania kod.

    Inne

    setTimeout i setInterval

    Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania +funkcji korzystając z funkcji setTimeout i setInterval. +Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the setTimeout and setInterval functions.

    + + + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // zwraca licznę typu Number > 0
    +
    + +

    Powyższe wywołanie setTimeout zwraca ID budzika i planuje wywołanie foo za +około tysiąc milisekund. foo zostanie wykonana dokładnie jeden raz.

    + +

    Nie ma pewności, że kod zaplanowany do wykonania wykona się dokładnie po +upłynięciu zadanego czasu podanego jako parametr do setTimeout, ponieważ zależy +to od dokładności zegara w silniku JavaScript, który wykonuje kod oraz od tego, +że inny kawałek kodu może zablokować wątek, ponieważ JavaScript jest tylko +jedno wątkowy.

    + +

    Funkcja, która została przekazana jako pierwszy parametr zostanie wykonana w +globalnym zasięgu, co oznacza, że this wewnątrz tej funkcji +będzie wkazywać na obiekt global.

    + +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this wskazuje na obiekt global
    +        console.log(this.value); // wypisze undefined
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    +
    + + + +

    Kolejkowanie wywołań z setInterval

    + +

    Podczas, gdy setTimeout wywołuje podaną funkcję tylko raz, setInterval - +jak wskazuje nazwa - będzie wykonywać funkcję w odstępach czasowych co X +milisekund. Jednakże korzystanie z tej funkcji jest odradzane.

    + +

    Kiedy wykonywany kod zablokuje możliwość uruchomienia zaplanowanej funkcji, +setInterval będzie próbować uruchamiać daną funkcję co będzie powodować +kolejkowanie wykonania tej samej funkcji kilkakrotnie. W szczególności może się +to wydarzyć przy krótkim interwale.

    + +
    function foo(){
    +    // coś co blokuje wykonanie na 1 sekundę 
    +}
    +setInterval(foo, 100);
    +
    + +

    W powyższym kodzie kod foo zostanie wywołany tylko raz i zablokuje wywołanie na +jedną sekundę.

    + +

    Podczas, gdy funkcja foo blokuje wykonanie setInterval będzie planować kolejne +wywołania foo. W momencie, gdy pierwsze wywołanie foo się zakończy, już +w kolejce do wywołania będą czekały kolejne dziesięć wywołań tej funkcji.

    + +

    Radzenie sobie z możliwymi blokadami

    + +

    Najprostrzym jak również najbardziej kontrolowaną sytuacją jest użycie setTimeout +wewnątrz wywoływanej funkcji.

    + +
    function foo(){
    +    // coś co blokuje wykonanie na 1 sekundę
    +    setTimeout(foo, 100);
    +}
    +foo();
    +
    + +

    Powyższy kod nie tylko hermetyzuje wywołanie setTimeout ale również zapobiega +kolejkowaniu wywołań fukcji i daje dodatkową kontrolę. W tym przypadku funkcja +foo może zdecydować czy powinna się wywołać ponownie czy też nie.

    + +

    Ręczne usuwanie budzików

    + +

    Usuwanie budzików i interwałów dokonywane jest przez przekazanie odpowiedniego ID +do clearTimeout lub clearInterval, w zależności z jakiej funkcji zostało +zwrócone ID.

    + +
    var id = setTimeout(foo, 1000);
    +clearTimeout(id);
    +
    + +

    Usuwanie wszystkich budzików

    + +

    Ponieważ nie istnieje wbudowana metoda na usunięcie wszystkich budzików i/lub +interwałów, konieczne jest użycie metody brute force aby osiągnąć ten efekt.

    + +
    // usunięcie "wszystkich" budzików 
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +
    + +

    Nadal może istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała, +ponieważ ID było z innego przedziału, dlatego zamiast korzystania z metody brute +force, zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć.

    + +

    Ukryte wykorzystanie eval

    + +

    Do setTimeout i setInterval można również przekazać string jako pierwszy +parametr zamiast obiektu funkcji, jednakże nigdy nie należy korzystać z tej +możliwości, ponieważ wewnętrznie setTimeout i setInterval wykorzystują eval.

    + + + +
    function foo() {
    +    // zostanie wykonane 
    +}
    +
    +function bar() {
    +    function foo() {
    +        // nigdy nie zostanie wywołane
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +
    + +

    Ponieważ eval nie zostało wywołane wprost w tym przypadku, to +string przekazany do setTimeout zostanie uruchomiony w zasięgu globalnym, +co za tym idzie lokalna zmienna foo z zasięgu bar nie zostanie użyta.

    + +

    Kolejnym zaleceniem jest aby nie stosować stringów do przekazywania argumentów +do funkcji, która ma zostać wywołana przez budzik.

    + +
    function foo(a, b, c) {}
    +
    +// NIGDY nie należy tak robić 
    +setTimeout('foo(1,2, 3)', 1000)
    +
    +// Zamiast tego należy skorzystać z anonimowej funkcji
    +setTimeout(function() {
    +    foo(a, b, c);
    +}, 1000)
    +
    + + + +

    Wnioski

    + +

    Nie należy nigdy przekazywać stringu jako parametru do setTimeout lub +setInterval. Jest to wyraźną oznaką bardzo złego kodu, jeżeli potrzebne jest +przekazanie argumentów do funkcji należy skorzystać z anonimowej funkcji i +wewnątrz niej dokonać przekazania argumentów.

    + +

    Ponadto, należy unikać korzystanie z setInterval, ponieważ planista może +zablokować wykonanie JavaScriptu.

    \ No newline at end of file From 7155c9e223d326fe17a55904d247d551e0d3b18f Mon Sep 17 00:00:00 2001 From: Colinvivy Date: Mon, 4 Jul 2011 00:21:27 +0800 Subject: [PATCH 195/641] modify a wrong word --- doc/zh/other/timeouts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/zh/other/timeouts.md b/doc/zh/other/timeouts.md index 5993e362..e404a6d1 100755 --- a/doc/zh/other/timeouts.md +++ b/doc/zh/other/timeouts.md @@ -35,7 +35,7 @@ `setTimeout` 只会执行回调函数一次,不过 `setInterval` - 正如名字建议的 - 会每隔 `X` 毫秒执行函数一次。 但是却不鼓励使用这个函数。 -当回调函数的执行被阻塞时,`setInterval` 仍然会发布更多的毁掉指令。在很小的定时间隔情况下,这会导致回调函数被堆积起来。 +当回调函数的执行被阻塞时,`setInterval` 仍然会发布更多的回调指令。在很小的定时间隔情况下,这会导致回调函数被堆积起来。 function foo(){ // 阻塞执行 1 秒 From be35c3e5ad40bad69a0c7471b30e21f13a434bac Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Mon, 4 Jul 2011 03:03:53 +0900 Subject: [PATCH 196/641] translate object/general.md to Japanese --- doc/ja/object/general.md | 74 ++++++++++++++++------------------------ site/ja/index.html | 70 +++++++++++++++---------------------- 2 files changed, 56 insertions(+), 88 deletions(-) diff --git a/doc/ja/object/general.md b/doc/ja/object/general.md index cbad5f11..2838cf91 100644 --- a/doc/ja/object/general.md +++ b/doc/ja/object/general.md @@ -1,7 +1,7 @@ -## Object Usage and Properties +## オブジェクトの使用法とプロパティ -Everything in JavaScript acts like an object, with the only two exceptions being -[`null`](#core.undefined) and [`undefined`](#core.undefined). +JavaScriptの全ての要素は2つの例外を除いて、オブジェクトのように振る舞います。 +その2つとは[`null`](#core.undefined)と[`undefined`](#core.undefined)です。 false.toString() // 'false' [1, 2, 3].toString(); // '1,2,3' @@ -10,57 +10,46 @@ Everything in JavaScript acts like an object, with the only two exceptions being Foo.bar = 1; Foo.bar; // 1 -A common misconception is that number literals cannot be used as -objects. That is because a flaw in JavaScript's parser tries to parse the *dot -notation* on a number as a floating point literal. +良くありがちな誤解として、数値リテラルがオブジェクトとして使用できないというものがあります。この理由としては、JavaScriptパーサーが浮動小数点のドットを*ドット記法*として解釈しようとしてしまうからです。 - 2.toString(); // raises SyntaxError + 2.toString(); // シンタックスエラーが発生する -There are a couple of workarounds which can be used in order make number -literals act as objects too. +数値リテラルをオブジェクトとして使用する為の回避策がいくつかあります。 - 2..toString(); // the second point is correctly recognized - 2 .toString(); // note the space left to the dot - (2).toString(); // 2 is evaluated first + 2..toString(); // 2つ目のドットが正しく解釈される + 2 .toString(); // ドットの左隣のスペースがポイント + (2).toString(); // 2が一番最初に評価される -### Objects as a Data Type +### オブジェクトはデータタイプ -Objects in JavaScript can also be used as a [*Hashmap*][1], they mainly consist -of named properties mapping to values. +JavaScriptのオブジェクトは[*ハッシュマップ*][1]としても使用されます。これは名前付きのプロパティと値として構成されています。 -Using a object literal - `{}` notation - it is possible to create a -plain object. This new object [inherits](#object.prototype) from `Object.prototype` and -has no [own properties](#object.hasownproperty) defined on it. +オブジェクトリテラル(`{}`記法)を使用すると、オブジェクトそのものを作る事ができます。この方法で作られたオブジェクトは`Object.prototype`から[継承](#object.prototype)され、[own properties](#object.hasownproperty)が何も設定されてない状態になります。 - var foo = {}; // a new empty object + var foo = {}; // 新しい空のオブジェクト - // a new object with a property called 'test' with value 12 + // 12という値の'test'というプロパティを持った新しいオブジェクト var bar = {test: 12}; -### Accessing Properties +### プロパティへのアクセス + +オブジェクトのプロパティには2通りのアクセス方法があります。1つはドット記法によるアクセス、もう1つはブラケット記法です。 -The properties of an object can be accessed in two ways, via either the dot -notation, or the square bracket notation. - var foo = {name: 'Kitten'} foo.name; // kitten foo['name']; // kitten - + var get = 'name'; foo[get]; // kitten - - foo.1234; // SyntaxError - foo['1234']; // works -Both notations are identical in their workings, with the only difference being that -the square bracket notation allows for dynamic setting of properties, as well as -the use of property names that would otherwise lead to a syntax error. + foo.1234; // シンタックスエラー + foo['1234']; // 動作する + +どちらの記法も働きとしての違いは無いですが、唯一の違いとしてブラケット記法は通常のプロパティ名と同様に動的にプロパティを設定する事ができます。これ以外で動的にプロパティを設定しようとするとシンタックスエラーになります。 -### Deleting Properties +### プロパティの削除 -The only way to actually remove a property from an object is to use the `delete` -operator; setting the property to `undefined` or `null` only remove the -*value* associated with the property, but not the *key*. +実際にオブジェクトからプロパティを削除する唯一の方法は`delete`演算子を使う事です。プロパティに`undefined`や`null`をセットしても、プロパティ自身ではなく、*キー*に設定された*値*を削除するだけです。 var obj = { bar: 1, @@ -77,23 +66,18 @@ operator; setting the property to `undefined` or `null` only remove the } } -The above outputs both `bar undefined` and `foo null` - only `baz` was -removed and is therefore missing from the output. +上記の例では、`baz`は完全に削除されて出力がされていませんが、それ以外の2つ`bar undefined`と`foo null`はどちらも出力されてしまっています。 -### Notation of Keys +### キーの記法 var test = { 'case': 'I am a keyword so I must be notated as a string', - delete: 'I am a keyword too so me' // raises SyntaxError + delete: 'I am a keyword too so me' // シンタックスエラーが起こる }; -Object properties can be both notated as plain characters and as strings. Due to -another mis-design in JavaScript's parser, the above will throw -a `SyntaxError` prior to ECMAScript 5. +オブジェクトのプロパティは普通の文字か文字列として記述する事が出来ます。JavaScriptパーサーの設計ミスが原因ですが、ECMAScript5以前では上記のコードは`シンタックスエラー`を表示するでしょう。 -This error arises from the fact that `delete` is a *keyword*; therefore, it must be -notated as a *string literal* to ensure that it will be correctly interpreted by -older JavaScript engines. +このエラーは`delete`が*予約語*になっているのが原因なので、古いJavaScriptエンジンに正しく解釈させる為には*文字リテラル*を使って記述する事を推奨します。 [1]: http://en.wikipedia.org/wiki/Hashmap diff --git a/site/ja/index.html b/site/ja/index.html index ff9f4bb6..874fa097 100644 --- a/site/ja/index.html +++ b/site/ja/index.html @@ -1,7 +1,7 @@ JavaScript Garden -

    前書き

    JavaScript Garden はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 +

    前書き

    JavaScript Garden はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 このドキュメントはJavaScriptという言語に対して不慣れなプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。

    JavaScript GardenはJavaScriptを教える事を目的にしていません。このガイドの項目を理解する為には、この言語に対する前提知識がある事を推奨します。この言語の基礎部分についてはMozilla Developer Networkのガイド がオススメです。

    @@ -24,9 +24,9 @@

    ホスティング

    ライセンス

    -

    JavaScript GardenはMIT licenseの下で公開されており、GitHubでホスティングされています。もしもエラーやtypoを見つけたらfile an issueに登録するかレポジトリにプルリクエストを送ってください。 -またStack OverflowチャットのJavaScript roomに私達はいます。

    オブジェクト

    Object Usage and Properties

    Everything in JavaScript acts like an object, with the only two exceptions being -null and undefined.

    +

    JavaScript GardenはMIT licenseの下で公開されており、GitHubでホスティングされています。もしもエラーやtypoを見つけたらfile an issueに登録するかリポジトリにプルリクエストを送ってください。 +またStack OverflowチャットのJavaScript roomに私達はいます。

    オブジェクト

    オブジェクトの使用法とプロパティ

    JavaScriptの全ての要素は2つの例外を除いて、オブジェクトのように振る舞います。 +その2つとはnullundefinedです。

    false.toString() // 'false'
     [1, 2, 3].toString(); // '1,2,3'
    @@ -36,40 +36,33 @@ 

    ライセンス

    Foo.bar; // 1
    -

    A common misconception is that number literals cannot be used as -objects. That is because a flaw in JavaScript's parser tries to parse the dot -notation on a number as a floating point literal.

    +

    良くありがちな誤解として、数値リテラルがオブジェクトとして使用できないというものがあります。この理由としては、JavaScriptパーサーが浮動小数点のドットをドット記法として解釈しようとしてしまうからです。

    -
    2.toString(); // raises SyntaxError
    +
    2.toString(); // シンタックスエラーが発生する
     
    -

    There are a couple of workarounds which can be used in order make number -literals act as objects too.

    +

    数値リテラルをオブジェクトとして使用する為の回避策がいくつかあります。

    -
    2..toString(); // the second point is correctly recognized
    -2 .toString(); // note the space left to the dot
    -(2).toString(); // 2 is evaluated first
    +
    2..toString(); // 2つ目のドットが正しく解釈される
    +2 .toString(); // ドットの左隣のスペースがポイント
    +(2).toString(); // 2が一番最初に評価される
     
    -

    Objects as a Data Type

    +

    オブジェクトはデータタイプ

    -

    Objects in JavaScript can also be used as a Hashmap, they mainly consist -of named properties mapping to values.

    +

    JavaScriptのオブジェクトはハッシュマップとしても使用されます。これは名前付きのプロパティと値として構成されています。

    -

    Using a object literal - {} notation - it is possible to create a -plain object. This new object inherits from Object.prototype and -has no own properties defined on it.

    +

    オブジェクトリテラル({}記法)を使用すると、オブジェクトそのものを作る事ができます。この方法で作られたオブジェクトはObject.prototypeから継承され、own propertiesが何も設定されてない状態になります。

    -
    var foo = {}; // a new empty object
    +
    var foo = {}; // 新しい空のオブジェクト
     
    -// a new object with a property called 'test' with value 12
    +// 12という値の'test'というプロパティを持った新しいオブジェクト
     var bar = {test: 12}; 
     
    -

    Accessing Properties

    +

    プロパティへのアクセス

    -

    The properties of an object can be accessed in two ways, via either the dot -notation, or the square bracket notation.

    +

    オブジェクトのプロパティには2通りのアクセス方法があります。1つはドット記法によるアクセス、もう1つはブラケット記法です。

    var foo = {name: 'Kitten'}
     foo.name; // kitten
    @@ -78,19 +71,15 @@ 

    ライセンス

    var get = 'name'; foo[get]; // kitten -foo.1234; // SyntaxError -foo['1234']; // works +foo.1234; // シンタックスエラー +foo['1234']; // 動作する
    -

    Both notations are identical in their workings, with the only difference being that -the square bracket notation allows for dynamic setting of properties, as well as -the use of property names that would otherwise lead to a syntax error.

    +

    どちらの記法も働きとしての違いは無いですが、唯一の違いとしてブラケット記法は通常のプロパティ名と同様に動的にプロパティを設定する事ができます。これ以外で動的にプロパティを設定しようとするとシンタックスエラーになります。

    -

    Deleting Properties

    +

    プロパティの削除

    -

    The only way to actually remove a property from an object is to use the delete -operator; setting the property to undefined or null only remove the -value associated with the property, but not the key.

    +

    実際にオブジェクトからプロパティを削除する唯一の方法はdelete演算子を使う事です。プロパティにundefinednullをセットしても、プロパティ自身ではなく、キーに設定されたを削除するだけです。

    var obj = {
         bar: 1,
    @@ -108,24 +97,19 @@ 

    ライセンス

    }
    -

    The above outputs both bar undefined and foo null - only baz was -removed and is therefore missing from the output.

    +

    上記の例では、bazは完全に削除されて出力がされていませんが、それ以外の2つbar undefinedfoo nullはどちらも出力されてしまっています。

    -

    Notation of Keys

    +

    キーの記法

    var test = {
         'case': 'I am a keyword so I must be notated as a string',
    -    delete: 'I am a keyword too so me' // raises SyntaxError
    +    delete: 'I am a keyword too so me' // シンタックスエラーが起こる
     };
     
    -

    Object properties can be both notated as plain characters and as strings. Due to -another mis-design in JavaScript's parser, the above will throw -a SyntaxError prior to ECMAScript 5.

    +

    オブジェクトのプロパティは普通の文字か文字列として記述する事が出来ます。JavaScriptパーサーの設計ミスが原因ですが、ECMAScript5以前では上記のコードはシンタックスエラーを表示するでしょう。

    -

    This error arises from the fact that delete is a keyword; therefore, it must be -notated as a string literal to ensure that it will be correctly interpreted by -older JavaScript engines.

    The Prototype

    JavaScript does not feature a classical inheritance model, instead it uses a +

    このエラーはdelete予約語になっているのが原因なので、古いJavaScriptエンジンに正しく解釈させる為には文字リテラルを使って記述する事を推奨します。

    The Prototype

    JavaScript does not feature a classical inheritance model, instead it uses a prototypal one.

    While this is often considered to be one of JavaScript's weaknesses, the From 4f3c32ff9e1a5e0ac0d3ece07610ea1d0f8513ea Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Mon, 4 Jul 2011 03:16:12 +0900 Subject: [PATCH 197/641] add ja and pl --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index fecb262f..a602d945 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,7 @@ /site/en /site/fi /site/tr +/site/pl +/site/ja *.md~ *.src.md From 5e4532f0f6adc593ecf4795bfdbe003b8a54a5d8 Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Mon, 4 Jul 2011 03:19:06 +0900 Subject: [PATCH 198/641] delate ja and pl from repository --- site/ja/index.html | 1853 ------------------------------------------ site/pl/index.html | 1914 -------------------------------------------- 2 files changed, 3767 deletions(-) delete mode 100644 site/ja/index.html delete mode 100644 site/pl/index.html diff --git a/site/ja/index.html b/site/ja/index.html deleted file mode 100644 index 874fa097..00000000 --- a/site/ja/index.html +++ /dev/null @@ -1,1853 +0,0 @@ -JavaScript Garden -

    前書き

    JavaScript Garden はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 -このドキュメントはJavaScriptという言語に対して不慣れなプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。

    - -

    JavaScript GardenはJavaScriptを教える事を目的にしていません。このガイドの項目を理解する為には、この言語に対する前提知識がある事を推奨します。この言語の基礎部分についてはMozilla Developer Networkのガイド がオススメです。

    - -

    著者

    - -

    このガイドは2人の愛すべきStack OverflowユーザーであるIvo Wetzel -(執筆)とZhang Yi Jiang (デザイン)によって作られました。

    - -

    貢献者

    - - - -

    ホスティング

    - -

    JavaScript GardenはGitHubでホスティングされていますが、Cramer DevelopmentJavaScriptGarden.infoというミラーサイトを作ってくれています。

    - -

    ライセンス

    - -

    JavaScript GardenはMIT licenseの下で公開されており、GitHubでホスティングされています。もしもエラーやtypoを見つけたらfile an issueに登録するかリポジトリにプルリクエストを送ってください。 -またStack OverflowチャットのJavaScript roomに私達はいます。

    オブジェクト

    オブジェクトの使用法とプロパティ

    JavaScriptの全ての要素は2つの例外を除いて、オブジェクトのように振る舞います。 -その2つとはnullundefinedです。

    - -
    false.toString() // 'false'
    -[1, 2, 3].toString(); // '1,2,3'
    -
    -function Foo(){}
    -Foo.bar = 1;
    -Foo.bar; // 1
    -
    - -

    良くありがちな誤解として、数値リテラルがオブジェクトとして使用できないというものがあります。この理由としては、JavaScriptパーサーが浮動小数点のドットをドット記法として解釈しようとしてしまうからです。

    - -
    2.toString(); // シンタックスエラーが発生する
    -
    - -

    数値リテラルをオブジェクトとして使用する為の回避策がいくつかあります。

    - -
    2..toString(); // 2つ目のドットが正しく解釈される
    -2 .toString(); // ドットの左隣のスペースがポイント
    -(2).toString(); // 2が一番最初に評価される
    -
    - -

    オブジェクトはデータタイプ

    - -

    JavaScriptのオブジェクトはハッシュマップとしても使用されます。これは名前付きのプロパティと値として構成されています。

    - -

    オブジェクトリテラル({}記法)を使用すると、オブジェクトそのものを作る事ができます。この方法で作られたオブジェクトはObject.prototypeから継承され、own propertiesが何も設定されてない状態になります。

    - -
    var foo = {}; // 新しい空のオブジェクト
    -
    -// 12という値の'test'というプロパティを持った新しいオブジェクト
    -var bar = {test: 12}; 
    -
    - -

    プロパティへのアクセス

    - -

    オブジェクトのプロパティには2通りのアクセス方法があります。1つはドット記法によるアクセス、もう1つはブラケット記法です。

    - -
    var foo = {name: 'Kitten'}
    -foo.name; // kitten
    -foo['name']; // kitten
    -
    -var get = 'name';
    -foo[get]; // kitten
    -
    -foo.1234; // シンタックスエラー
    -foo['1234']; // 動作する
    -
    - -

    どちらの記法も働きとしての違いは無いですが、唯一の違いとしてブラケット記法は通常のプロパティ名と同様に動的にプロパティを設定する事ができます。これ以外で動的にプロパティを設定しようとするとシンタックスエラーになります。

    - -

    プロパティの削除

    - -

    実際にオブジェクトからプロパティを削除する唯一の方法はdelete演算子を使う事です。プロパティにundefinednullをセットしても、プロパティ自身ではなく、キーに設定されたを削除するだけです。

    - -
    var obj = {
    -    bar: 1,
    -    foo: 2,
    -    baz: 3
    -};
    -obj.bar = undefined;
    -obj.foo = null;
    -delete obj.baz;
    -
    -for(var i in obj) {
    -    if (obj.hasOwnProperty(i)) {
    -        console.log(i, '' + obj[i]);
    -    }
    -}
    -
    - -

    上記の例では、bazは完全に削除されて出力がされていませんが、それ以外の2つbar undefinedfoo nullはどちらも出力されてしまっています。

    - -

    キーの記法

    - -
    var test = {
    -    'case': 'I am a keyword so I must be notated as a string',
    -    delete: 'I am a keyword too so me' // シンタックスエラーが起こる
    -};
    -
    - -

    オブジェクトのプロパティは普通の文字か文字列として記述する事が出来ます。JavaScriptパーサーの設計ミスが原因ですが、ECMAScript5以前では上記のコードはシンタックスエラーを表示するでしょう。

    - -

    このエラーはdelete予約語になっているのが原因なので、古いJavaScriptエンジンに正しく解釈させる為には文字リテラルを使って記述する事を推奨します。

    The Prototype

    JavaScript does not feature a classical inheritance model, instead it uses a -prototypal one.

    - -

    While this is often considered to be one of JavaScript's weaknesses, the -prototypal inheritance model is in fact more powerful than the classic model. -It is for example fairly trivial to build a classic model on top of it, while the -other way around is a far more difficult task.

    - -

    Due to the fact that JavaScript is basically the only widely used language that -features prototypal inheritance, it takes some time to adjust to the -differences between the two models.

    - -

    The first major difference is that inheritance in JavaScript is done by using so -called prototype chains.

    - - - -
    function Foo() {
    -    this.value = 42;
    -}
    -Foo.prototype = {
    -    method: function() {}
    -};
    -
    -function Bar() {}
    -
    -// Set Bar's prototype to a new instance of Foo
    -Bar.prototype = new Foo();
    -Bar.prototype.foo = 'Hello World';
    -
    -// Make sure to list Bar as the actual constructor
    -Bar.prototype.constructor = Bar;
    -
    -var test = new Bar() // create a new bar instance
    -
    -// The resulting prototype chain
    -test [instance of Bar]
    -    Bar.prototype [instance of Foo] 
    -        { foo: 'Hello World' }
    -        Foo.prototype
    -            { method: ... }
    -            Object.prototype
    -                { toString: ... /* etc. */ }
    -
    - -

    In the above, the object test will inherit from both Bar.prototype and -Foo.prototype; hence, it will have access to the function method that was -defined on Foo. It will also have access to the property value of the -one Foo instance that is its prototype. It is important to note that new -Bar() does not create a new Foo instance, but reuses the one assigned to -its prototype; thus, all Bar instances will share the same value property.

    - - - -

    Property Lookup

    - -

    When accessing the properties of an object, JavaScript will traverse the -prototype chain upwards until it finds a property with the requested name.

    - -

    When it reaches the top of the chain - namely Object.prototype - and still -hasn't found the specified property, it will return the value -undefined instead.

    - -

    The Prototype Property

    - -

    While the prototype property is used by the language to build the prototype -chains, it is still possible to assign any given value to it. Although -primitives will simply get ignored when assigned as a prototype.

    - -
    function Foo() {}
    -Foo.prototype = 1; // no effect
    -
    - -

    Assigning objects, as shown in the example above, will work, and allows for dynamic -creation of prototype chains.

    - -

    Performance

    - -

    The lookup time for properties that are high up on the prototype chain can have a -negative impact on performance critical sections of code. Additionally, trying to -access non-existent properties will always traverse the full prototype chain.

    - -

    Also, when iterating over the properties of an object -every property that is on the prototype chain will get enumerated.

    - -

    Extension of Native Prototypes

    - -

    One mis-feature that is often used is to extend Object.prototype or one of the -other built in prototypes.

    - -

    This technique is called monkey patching and breaks encapsulation. While -used by widely spread frameworks such as Prototype, there is still no good -reason for cluttering built-in types with additional non-standard functionality.

    - -

    The only good reason for extending a built-in prototype is to backport -the features of newer JavaScript engines; for example, -Array.forEach.

    - -

    In Conclusion

    - -

    It is a must to understand the prototypal inheritance model completely -before writing complex code which makes use of it. Also, watch the length of -the prototype chains and break them up if necessary to avoid possible -performance issues. Further, the native prototypes should never be extended -unless it is for the sake of compatibility with newer JavaScript features.

    hasOwnProperty

    In order to check whether a object has a property defined on itself and not -somewhere on its prototype chain, it is necessary to use the -hasOwnProperty method which all objects inherit from Object.prototype.

    - - - -

    hasOwnProperty is the only thing in JavaScript which deals with properties and -does not traverse the prototype chain.

    - -
    // Poisoning Object.prototype
    -Object.prototype.bar = 1; 
    -var foo = {goo: undefined};
    -
    -foo.bar; // 1
    -'bar' in foo; // true
    -
    -foo.hasOwnProperty('bar'); // false
    -foo.hasOwnProperty('goo'); // true
    -
    - -

    Only hasOwnProperty will give the correct and expected result, this is -essential when iterating over the properties of any object. There is no other -way to exclude properties that are not defined on the object itself, but -somewhere on its prototype chain.

    - -

    hasOwnProperty as a Property

    - -

    JavaScript does not protect the property name hasOwnProperty; thus, if the -possibility exists that an object might have a property with this name, it is -necessary to use an external hasOwnProperty in order to get correct results.

    - -
    var foo = {
    -    hasOwnProperty: function() {
    -        return false;
    -    },
    -    bar: 'Here be dragons'
    -};
    -
    -foo.hasOwnProperty('bar'); // always returns false
    -
    -// Use another Object's hasOwnProperty and call it with 'this' set to foo
    -({}).hasOwnProperty.call(foo, 'bar'); // true
    -
    - -

    In Conclusion

    - -

    When checking for the existence of a property on a object, hasOwnProperty is -the only method of doing so. It is also recommended to make hasOwnProperty -part of every for in loop, this will avoid errors from -extended native prototypes.

    The for in Loop

    Just like the in operator, the for in loop also traverses the prototype -chain when iterating over the properties of an object.

    - - - -
    // Poisoning Object.prototype
    -Object.prototype.bar = 1;
    -
    -var foo = {moo: 2};
    -for(var i in foo) {
    -    console.log(i); // prints both bar and moo
    -}
    -
    - -

    Since it is not possible to change the behavior of the for in loop itself, it -is necessary to filter out the unwanted properties inside the loop body , -this is done by using the hasOwnProperty method of -Object.prototype.

    - - - -

    Using hasOwnProperty for Filtering

    - -
    // still the foo from above
    -for(var i in foo) {
    -    if (foo.hasOwnProperty(i)) {
    -        console.log(i);
    -    }
    -}
    -
    - -

    This version is the only correct one to use. Due to the use of hasOwnProperty it -will only print out moo. When hasOwnProperty is left out, the code is -prone to errors in cases where the native prototypes - e.g. Object.prototype - -have been extended.

    - -

    One widely used framework which does this is Prototype. When this -framework is included, for in loops that do not use hasOwnProperty are -guaranteed to break.

    - -

    In Conclusion

    - -

    It is recommended to always use hasOwnProperty. Never should any -assumptions be made about the environment the code is running in, or whether the -native prototypes have been extended or not.

    関数

    Function Declarations and Expressions

    Functions in JavaScript are first class objects. That means they can be -passed around like any other value. One common use of this feature is to pass -an anonymous function as a callback to another, possibly asynchronous function.

    - -

    The function Declaration

    - -
    function foo() {}
    -
    - -

    The above function gets hoisted before the execution of the -program starts; thus, it is available everywhere in the scope it was defined -in, even if called before the actual definition in the source.

    - -
    foo(); // Works because foo was created before this code runs
    -function foo() {}
    -
    - -

    The function Expression

    - -
    var foo = function() {};
    -
    - -

    This example assigns the unnamed and anonymous function to the variable foo.

    - -
    foo; // 'undefined'
    -foo(); // this raises a TypeError
    -var foo = function() {};
    -
    - -

    Due to the fact that var is a declaration, that hoists the variable name foo -before the actual execution of the code starts, foo is already defined when -the script gets executed.

    - -

    But since assignments only happen at runtime, the value of foo will default -to undefined before the corresponding code is executed.

    - -

    Named Function Expression

    - -

    Another special case is the assignment of named functions.

    - -
    var foo = function bar() {
    -    bar(); // Works
    -}
    -bar(); // ReferenceError
    -
    - -

    Here bar is not available in the outer scope, since the function only gets -assigned to foo; however, inside of bar it is available. This is due to -how name resolution in JavaScript works, the name of the -function is always made available in the local scope of the function itself.

    How this Works

    JavaScript has a different concept of what the special name this refers to -than most other programming languages do. There are exactly five different -ways in which the value of this can be bound in the language.

    - -

    The Global Scope

    - -
    this;
    -
    - -

    When using this in global scope, it will simply refer to the global object.

    - -

    Calling a Function

    - -
    foo();
    -
    - -

    Here this will again refer to the global object.

    - - - -

    Calling a Method

    - -
    test.foo(); 
    -
    - -

    In this example this will refer to test.

    - -

    Calling a Constructor

    - -
    new foo(); 
    -
    - -

    A function call that is preceded by the new keyword acts as -a constructor. Inside the function this will refer -to a newly created Object.

    - -

    Explicit Setting of this

    - -
    function foo(a, b, c) {}
    -
    -var bar = {};
    -foo.apply(bar, [1, 2, 3]); // array will expand to the below
    -foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3
    -
    - -

    When using the call or apply methods of Function.prototype, the value of -this inside the called function gets explicitly set to the first argument -of the corresponding function call.

    - -

    As a result, the above example the method case does not apply, and this -inside of foo will be set to bar.

    - - - -

    Common Pitfalls

    - -

    While most of these cases make sense, the first one is to be considered another -mis-design of the language, as it never has any practical use.

    - -
    Foo.method = function() {
    -    function test() {
    -        // this is set to the global object
    -    }
    -    test();
    -}
    -
    - -

    A common misconception is that this inside of test refers to Foo, while in -fact it does not.

    - -

    In order to gain access to Foo from within test it is necessary to create a -local variable inside of method which refers to Foo.

    - -
    Foo.method = function() {
    -    var that = this;
    -    function test() {
    -        // Use that instead of this here
    -    }
    -    test();
    -}
    -
    - -

    that is just a normal variable name, but it is commonly used for the reference to an -outer this. In combination with closures, it can also -be used to pass this values around.

    - -

    Assigning Methods

    - -

    Another thing that does not work in JavaScript is function aliasing, that is, -assigning a method to a variable.

    - -
    var test = someObject.methodTest;
    -test();
    -
    - -

    Due to the first case test now acts like a plain function call; therefore, -this inside it will no longer refer to someObject.

    - -

    While the late binding of this might seem like a bad idea at first, it is in -fact what makes prototypal inheritance work.

    - -
    function Foo() {}
    -Foo.prototype.method = function() {};
    -
    -function Bar() {}
    -Bar.prototype = Foo.prototype;
    -
    -new Bar().method();
    -
    - -

    When method gets called on a instance of Bar, this will now refer to that -very instance.

    Closures and References

    One of JavaScript's most powerful features is the availability of closures, -this means that scopes always keep access to the outer scope they were -defined in. Since the only scoping that JavaScript has is -function scope, all functions, by default, act as closures.

    - -

    Emulating private variables

    - -
    function Counter(start) {
    -    var count = start;
    -    return {
    -        increment: function() {
    -            count++;
    -        },
    -
    -        get: function() {
    -            return count;
    -        }
    -    }
    -}
    -
    -var foo = Counter(4);
    -foo.increment();
    -foo.get(); // 5
    -
    - -

    Here, Counter returns two closures. The function increment as well as -the function get. Both of these functions keep a reference to the scope of -Counter and, therefore, always keep access to the count variable that was -defined in that very scope.

    - -

    Why Private Variables Work

    - -

    Since it is not possible to reference or assign scopes in JavaScript, there is -no way of accessing the variable count from the outside. The only way to -interact with it is via the two closures.

    - -
    var foo = new Counter(4);
    -foo.hack = function() {
    -    count = 1337;
    -};
    -
    - -

    The above code will not change the variable count in the scope of Counter, -since foo.hack was not defined in that scope. It will instead create - or -override - the global variable count.

    - -

    Closures Inside Loops

    - -

    One often made mistake is to use closures inside of loops, as if they were -copying the value of the loops index variable.

    - -
    for(var i = 0; i < 10; i++) {
    -    setTimeout(function() {
    -        console.log(i);  
    -    }, 1000);
    -}
    -
    - -

    The above will not output the numbers 0 through 9, but will simply print -the number 10 ten times.

    - -

    The anonymous function keeps a reference to i and at the time -console.log gets called, the for loop has already finished and the value of -i as been set to 10.

    - -

    In order to get the desired behavior, it is necessary to create a copy of -the value of i.

    - -

    Avoiding the Reference Problem

    - -

    In order to copy the value of the loop's index variable, it is best to use an -anonymous wrapper.

    - -
    for(var i = 0; i < 10; i++) {
    -    (function(e) {
    -        setTimeout(function() {
    -            console.log(e);  
    -        }, 1000);
    -    })(i);
    -}
    -
    - -

    The anonymous outer function gets called immediately with i as its first -argument and will receive a copy of the value of i as its parameter e.

    - -

    The anonymous function that gets passed to setTimeout now has a reference to -e, whose value does not get changed by the loop.

    - -

    There is another possible way of achieving this; that is to return a function -from the anonymous wrapper, that will then have the same behavior as the code -above.

    - -
    for(var i = 0; i < 10; i++) {
    -    setTimeout((function(e) {
    -        return function() {
    -            console.log(e);
    -        }
    -    })(i), 1000)
    -}
    -

    The arguments Object

    Every function scope in JavaScript can access the special variable arguments. -This variable holds a list of all the arguments that were passed to the function.

    - - - -

    The arguments object is not an Array. While it has some of the -semantics of an array - namely the length property - it does not inherit from -Array.prototype and is in fact an Object.

    - -

    Due to this, it is not possible to use standard array methods like push, -pop or slice on arguments. While iteration with a plain for loop works -just fine, it is necessary to convert it to a real Array in order to use the -standard Array methods on it.

    - -

    Converting to an Array

    - -

    The code below will return a new Array containing all the elements of the -arguments object.

    - -
    Array.prototype.slice.call(arguments);
    -
    - -

    This conversion is slow, it is not recommended to use it in performance -critical sections of code.

    - -

    Passing Arguments

    - -

    The following is the recommended way of passing arguments from one function to -another.

    - -
    function foo() {
    -    bar.apply(null, arguments);
    -}
    -function bar(a, b, c) {
    -    // do stuff here
    -}
    -
    - -

    Another trick is to use both call and apply together to create fast, unbound -wrappers.

    - -
    function Foo() {}
    -
    -Foo.prototype.method = function(a, b, c) {
    -    console.log(this, a, b, c);
    -};
    -
    -// Create an unbound version of "method" 
    -// It takes the parameters: this, arg1, arg2...argN
    -Foo.method = function() {
    -
    -    // Result: Foo.prototype.method.call(this, arg1, arg2... argN)
    -    Function.call.apply(Foo.prototype.method, arguments);
    -};
    -
    - -

    Formal Parameters and Arguments Indices

    - -

    The arguments object creates getter and setter functions for both its -properties as well as the function's formal parameters.

    - -

    As a result, changing the value of a formal parameter will also change the value -of the corresponding property on the arguments object, and the other way around.

    - -
    function foo(a, b, c) {
    -    arguments[0] = 2;
    -    a; // 2                                                           
    -
    -    b = 4;
    -    arguments[1]; // 4
    -
    -    var d = c;
    -    d = 9;
    -    c; // 3
    -}
    -foo(1, 2, 3);
    -
    - -

    Performance Myths and Truths

    - -

    The arguments object is always created with the only two exceptions being the -cases where it is declared as a name inside of a function or one of its formal -parameters. It does not matter whether it is used or not.

    - -

    Both getters and setters are always created; thus, using it has nearly -no performance impact at all, especially not in real world code where there is -more than a simple access to the arguments object's properties.

    - - - -

    However, there is one case which will drastically reduce the performance in -modern JavaScript engines. That case is the use of arguments.callee.

    - -
    function foo() {
    -    arguments.callee; // do something with this function object
    -    arguments.callee.caller; // and the calling function object
    -}
    -
    -function bigLoop() {
    -    for(var i = 0; i < 100000; i++) {
    -        foo(); // Would normally be inlined...
    -    }
    -}
    -
    - -

    In the above code, foo can no longer be a subject to inlining since it -needs to know about both itself and its caller. This not only defeats possible -performance gains that would arise from inlining, it also breaks encapsulation -since the function may now be dependent on a specific calling context.

    - -

    It is highly recommended to never make use of arguments.callee or any of -its properties.

    - -

    Constructors

    Constructors in JavaScript are yet again different from many other languages. Any -function call that is preceded by the new keyword acts as a constructor.

    - -

    Inside the constructor - the called function - the value of this refers to a -newly created Object. The prototype of this new -object is set to the prototype of the function object that was invoked as the -constructor.

    - -

    If the function that was called has no explicit return statement, then it -implicitly returns the value of this - the new object.

    - -
    function Foo() {
    -    this.bla = 1;
    -}
    -
    -Foo.prototype.test = function() {
    -    console.log(this.bla);
    -};
    -
    -var test = new Foo();
    -
    - -

    The above calls Foo as constructor and sets the prototype of the newly -created object to Foo.prototype.

    - -

    In case of an explicit return statement the function returns the value -specified that statement, but only if the return value is an Object.

    - -
    function Bar() {
    -    return 2;
    -}
    -new Bar(); // a new object
    -
    -function Test() {
    -    this.value = 2;
    -
    -    return {
    -        foo: 1
    -    };
    -}
    -new Test(); // the returned object
    -
    - -

    When the new keyword is omitted, the function will not return a new object.

    - -
    function Foo() {
    -    this.bla = 1; // gets set on the global object
    -}
    -Foo(); // undefined
    -
    - -

    While the above example might still appear to work in some cases, due to the -workings of this in JavaScript, it will use the -global object as the value of this.

    - -

    Factories

    - -

    In order to be able to omit the new keyword, the constructor function has to -explicitly return a value.

    - -
    function Bar() {
    -    var value = 1;
    -    return {
    -        method: function() {
    -            return value;
    -        }
    -    }
    -}
    -Bar.prototype = {
    -    foo: function() {}
    -};
    -
    -new Bar();
    -Bar();
    -
    - -

    Both calls to Bar return the exact same thing, a newly create object which -has a property called method on it, that is a -Closure.

    - -

    It is also to note that the call new Bar() does not affect the prototype -of the returned object. While the prototype will be set on the newly created -object, Bar never returns that new object.

    - -

    In the above example, there is no functional difference between using and -not using the new keyword.

    - -

    Creating New Objects via Factories

    - -

    An often made recommendation is to not use new since forgetting its use -may lead to bugs.

    - -

    In order to create new object, one should rather use a factory and construct a -new object inside of that factory.

    - -
    function Foo() {
    -    var obj = {};
    -    obj.value = 'blub';
    -
    -    var private = 2;
    -    obj.someMethod = function(value) {
    -        this.value = value;
    -    }
    -
    -    obj.getPrivate = function() {
    -        return private;
    -    }
    -    return obj;
    -}
    -
    - -

    While the above is robust against a missing new keyword and certainly makes -the use of private variables easier, it comes with some -downsides.

    - -
      -
    1. It uses more memory since the created objects do not share the methods -on a prototype.
    2. -
    3. In order to inherit the factory needs to copy all the methods from another -object or put that object on the prototype of the new object.
    4. -
    5. Dropping the prototype chain just because of a left out new keyword -somehow goes against the spirit of the language.
    6. -
    - -

    In Conclusion

    - -

    While omitting the new keyword might lead to bugs, it is certainly not a -reason to drop the use of prototypes altogether. In the end it comes down to -which solution is better suited for the needs of the application, it is -especially important to choose a specific style of object creation and stick -with it.

    Scopes and Namespaces

    Although JavaScript deals fine with the syntax of two matching curly -braces for blocks, it does not support block scope; hence, all that is left -is in the language is function scope.

    - -
    function test() { // a scope
    -    for(var i = 0; i < 10; i++) { // not a scope
    -        // count
    -    }
    -    console.log(i); // 10
    -}
    -
    - - - -

    There are also no distinct namespaces in JavaScript, that means that everything -gets defined in one globally shared namespace.

    - -

    Each time a variable is referenced, JavaScript will traverse upwards through all -the scopes until it finds it. In the case that it reaches the global scope and -still has not found the requested name, it will raise a ReferenceError.

    - -

    The Bane of Global Variables

    - -
    // script A
    -foo = '42';
    -
    -// script B
    -var foo = '42'
    -
    - -

    The above two scripts do not have the same effect. Script A defines a -variable called foo in the global scope and script B defines a foo in the -current scope.

    - -

    Again, that is not at all the same effect, not using var can have major -implications.

    - -
    // global scope
    -var foo = 42;
    -function test() {
    -    // local scope
    -    foo = 21;
    -}
    -test();
    -foo; // 21
    -
    - -

    Leaving out the var statement inside the function test will override the -value of foo. While this might not seem like a big deal at first, having -thousands of lines of JavaScript and not using var will introduce horrible and -hard to track down bugs.

    - -
    // global scope
    -var items = [/* some list */];
    -for(var i = 0; i < 10; i++) {
    -    subLoop();
    -}
    -
    -function subLoop() {
    -    // scope of subLoop
    -    for(i = 0; i < 10; i++) { // missing var statement
    -        // do amazing stuff!
    -    }
    -}
    -
    - -

    The outer loop will terminate after the first call to subLoop, since subLoop -overwrites the global value of i. Using a var for the second for loop would -have easily avoided this error. The var statement should never be left out -unless the desired effect is to affect the outer scope.

    - -

    Local Variables

    - -

    The only source for local variables in JavaScript are -function parameters and variables that were declared via the -var statement.

    - -
    // global scope
    -var foo = 1;
    -var bar = 2;
    -var i = 2;
    -
    -function test(i) {
    -    // local scope of the function test
    -    i = 5;
    -
    -    var foo = 3;
    -    bar = 4;
    -}
    -test(10);
    -
    - -

    While foo and i are local variables inside the scope of the function test, -the assignment of bar will override the global variable with the same name.

    - -

    Hoisting

    - -

    JavaScript hoists declarations. This means that both var statements and -function declarations will be moved to the top of their enclosing scope.

    - -
    bar();
    -var bar = function() {};
    -var someValue = 42;
    -
    -test();
    -function test(data) {
    -    if (false) {
    -        goo = 1;
    -
    -    } else {
    -        var goo = 2;
    -    }
    -    for(var i = 0; i < 100; i++) {
    -        var e = data[i];
    -    }
    -}
    -
    - -

    The above code gets transformed before any execution is started. JavaScript moves -the var statements as well as the function declarations to the top of the -nearest surrounding scope.

    - -
    // var statements got moved here
    -var bar, someValue; // default to 'undefined'
    -
    -// the function declartion got moved up too
    -function test(data) {
    -    var goo, i, e; // missing block scope moves these here
    -    if (false) {
    -        goo = 1;
    -
    -    } else {
    -        goo = 2;
    -    }
    -    for(i = 0; i < 100; i++) {
    -        e = data[i];
    -    }
    -}
    -
    -bar(); // fails with a TypeError since bar is still 'undefined'
    -someValue = 42; // assignments are not affected by hoisting
    -bar = function() {};
    -
    -test();
    -
    - -

    Missing block scoping will not only move var statements out of loops and -their bodies, it will also make the results of certain if constructs -non-intuitive.

    - -

    In the original code the if statement seemed to modify the global -variable goo, while actually it modifies the local variable - after hoisting -has been applied.

    - -

    Without the knowledge about hoisting, below code might seem to raise a -ReferenceError.

    - -
    // check whether SomeImportantThing has been initiliazed
    -if (!SomeImportantThing) {
    -    var SomeImportantThing = {};
    -}
    -
    - -

    But of course, the above works due to the fact that the var statement is being -moved to the top of the global scope.

    - -
    var SomeImportantThing;
    -
    -// other code might initiliaze SomeImportantThing here, or not
    -
    -// make sure it's there
    -if (!SomeImportantThing) {
    -    SomeImportantThing = {};
    -}
    -
    - -

    Name Resolution Order

    - -

    All scopes in JavaScript, including the global scope, have the special name -this defined in them, which refers to the current object.

    - -

    Function scopes also have the name arguments defined in -them which contains the arguments that were passed to a function.

    - -

    For example, when trying to access a variable named foo inside the scope of a -function, JavaScript will lookup the name in the following order:

    - -
      -
    1. In case there is a var foo statement in the current scope use that.
    2. -
    3. If one of the function parameters is named foo use that.
    4. -
    5. If the function itself is called foo use that.
    6. -
    7. Go to the next outer scope and start with #1 again.
    8. -
    - - - -

    Namespaces

    - -

    A common problem of having only one global namespace is the likeliness of running -into problems where variable names clash. In JavaScript, this problem can -easily be avoided with the help of anonymous wrappers.

    - -
    (function() {
    -    // a self contained "namespace"
    -
    -    window.foo = function() {
    -        // an exposed closure
    -    };
    -
    -})(); // execute the function immediately
    -
    - -

    Unnamed functions are considered expressions; so in order to -being callable, they must first be evaluated.

    - -
    ( // evaluate the function inside the paranthesis
    -function() {}
    -) // and return the function object
    -() // call the result of the evaluation
    -
    - -

    There are other ways for evaluating and calling the function expression; which, -while different in syntax, do behave the exact same way.

    - -
    // Two other ways
    -+function(){}();
    -(function(){}());
    -
    - -

    In Conclusion

    - -

    It is recommended to always use an anonymous wrapper for encapsulating code in -its own namespace. This does not only protect code against name clashes, it -also allows for better modularization of programs.

    - -

    Additionally, the use of global variables is considered bad practice. Any -use of them indicates badly written code that is prone to errors and hard to maintain.

    配列

    Array Iteration and Properties

    Although arrays in JavaScript are objects, there are no good reasons to use -the for in loop in for iteration on them. In fact there -are a number of good reasons against the use of for in on arrays.

    - - - -

    Since the for in loop enumerates all the properties that are on the prototype -chain and the only way to exclude those properties is to use -hasOwnProperty, it is already up to twenty times -slower than a normal for loop.

    - -

    Iteration

    - -

    In order to achieve the best performance when iterating over arrays, it is best -to use the classic for loop.

    - -
    var list = [1, 2, 3, 4, 5, ...... 100000000];
    -for(var i = 0, l = list.length; i < l; i++) {
    -    console.log(list[i]);
    -}
    -
    - -

    There is one extra catch in the above example, that is the caching of the -length of the array via l = list.length.

    - -

    Although the length property is defined on the array itself, there is still an -overhead for doing the lookup on each iteration of the loop. And while recent -JavaScript engines may apply optimization in this case, there is no way of -telling whether the code will run on one of these newer engines or not.

    - -

    In fact, leaving out the caching may result in the loop being only half as -fast as with the cached length.

    - -

    The length Property

    - -

    While the getter of the length property simply returns the number of -elements that are contained in the array, the setter can be used to -truncate the array.

    - -
    var foo = [1, 2, 3, 4, 5, 6];
    -foo.length = 3;
    -foo; // [1, 2, 3]
    -
    -foo.length = 6;
    -foo; // [1, 2, 3]
    -
    - -

    Assigning a smaller length does truncate the array, but increasing the length -does not have any effect on the array.

    - -

    In Conclusion

    - -

    For the best performance it is recommended to always use the plain for loop -and cache the length property. The use of for in on an array is a sign of -badly written code that is prone to bugs and bad performance.

    The Array Constructor

    Since the Array constructor is ambiguous in how it deals with its parameters, -it is highly recommended to always use the array literals - [] notation - -when creating new arrays.

    - -
    [1, 2, 3]; // Result: [1, 2, 3]
    -new Array(1, 2, 3); // Result: [1, 2, 3]
    -
    -[3]; // Result: [3]
    -new Array(3); // Result: []
    -new Array('3') // Result: ['3']
    -
    - -

    In cases when there is only one argument passed to the Array constructor, -and that argument is a Number, the constructor will return a new sparse -array with the length property set to the value of the argument. It should be -noted that only the length property of the new array will be set this way, -the actual indexes of the array will not be initialized.

    - -
    var arr = new Array(3);
    -arr[1]; // undefined
    -1 in arr; // false, the index was not set
    -
    - -

    The behavior of being able to set the length of the array upfront only comes in -handy in a few cases, like repeating a string, in which it avoids the use of a -for loop code.

    - -
    new Array(count + 1).join(stringToRepeat);
    -
    - -

    In Conclusion

    - -

    The use of the Array constructor should be avoided as much as possible. -Literals are definitely preferred. They are shorter and have a clearer syntax; -therefore, they also increase the readability of the code.

    Equality and Comparisons

    JavaScript has two different ways of comparing the values of objects for equality.

    - -

    The Equality Operator

    - -

    The equality operator consists of two equal signs: ==

    - -

    JavaScript features weak typing. This means that the equality operator -coerces types in order to compare them.

    - -
    ""           ==   "0"           // false
    -0            ==   ""            // true
    -0            ==   "0"           // true
    -false        ==   "false"       // false
    -false        ==   "0"           // true
    -false        ==   undefined     // false
    -false        ==   null          // false
    -null         ==   undefined     // true
    -" \t\r\n"    ==   0             // true
    -
    - -

    The above table shows the results of the type coercion and it is the main reason -why the use of == is widely regarded as bad practice, it introduces hard to -track down bugs due to its complicated conversion rules.

    - -

    Additionally there is also a performance impact when type coercion is in play; -for example, a string has to be converted to a number before it can be compared -to another number.

    - -

    The Strict Equality Operator

    - -

    The strict equality operator consists of three equal signs: ===

    - -

    It works exactly like the normal equality operator, except that strict equality -operator does not perform type coercion between its operands.

    - -
    ""           ===   "0"           // false
    -0            ===   ""            // false
    -0            ===   "0"           // false
    -false        ===   "false"       // false
    -false        ===   "0"           // false
    -false        ===   undefined     // false
    -false        ===   null          // false
    -null         ===   undefined     // false
    -" \t\r\n"    ===   0             // false
    -
    - -

    The above results are a lot clearer and allow for early breakage of code. This -hardens code to a certain degree and also gives performance improvements in case -the operands are of different types.

    - -

    Comparing Objects

    - -

    While both == and === are stated as equality operators, they behave -different when at least one of their operands happens to be an Object.

    - -
    {} === {};                   // false
    -new String('foo') === 'foo'; // false
    -new Number(10) === 10;       // false
    -var foo = {};
    -foo === foo;                 // true
    -
    - -

    Here both operators compare for identity and not equality; that is, they -will compare for the same instance of the object, much like is in Python -and pointer comparison in C.

    - -

    In Conclusion

    - -

    It is highly recommended to only use the strict equality operator. In cases -where types need to be coerced, it should be done explicitly -and not left to the language's complicated coercion rules.

    The typeof Operator

    The typeof operator (together with -instanceof) is probably the biggest -design flaw of JavaScript, as it is near of being completely broken.

    - -

    Although instanceof still has its limited uses, typeof really has only one -practical use case, which does not happen to be checking the type of an -object.

    - - - -

    The JavaScript Type Table

    - -
    Value               Class      Type
    --------------------------------------
    -"foo"               String     string
    -new String("foo")   String     object
    -1.2                 Number     number
    -new Number(1.2)     Number     object
    -true                Boolean    boolean
    -new Boolean(true)   Boolean    object
    -new Date()          Date       object
    -new Error()         Error      object
    -[1,2,3]             Array      object
    -new Array(1, 2, 3)  Array      object
    -new Function("")    Function   function
    -/abc/g              RegExp     object (function in Nitro/V8)
    -new RegExp("meow")  RegExp     object (function in Nitro/V8)
    -{}                  Object     object
    -new Object()        Object     object
    -
    - -

    In the above table Type refers to the value, that the typeof operator returns. -As can be clearly seen, this value is anything but consistent.

    - -

    The Class refers to the value of the internal [[Class]] property of an object.

    - - - -

    In order to retrieve the value of [[Class]] one has to make use of the -toString method of Object.prototype.

    - -

    The Class of an Object

    - -

    The specification gives exactly one way of accessing the [[Class]] value, -with the use of Object.prototype.toString.

    - -
    function is(type, obj) {
    -    var clas = Object.prototype.toString.call(obj).slice(8, -1);
    -    return obj !== undefined && obj !== null && clas === type;
    -}
    -
    -is('String', 'test'); // true
    -is('String', new String('test')); // true
    -
    - -

    In the above example, Object.prototype.toString gets called with the value of -this being set to the object whose [[Class]] value should be -retrieved.

    - - - -

    Testing for Undefined Variables

    - -
    typeof foo !== 'undefined'
    -
    - -

    The above will check whether foo was actually declared or not; just -referencing it would result in a ReferenceError. This is the only thing -typeof is actually useful for.

    - -

    In Conclusion

    - -

    In order to check the type of an object, it is highly recommended to use -Object.prototype.toString; as this is the only reliable way of doing so. -As shown in the above type table, some return values of typeof are not defined -in the specification; thus, they can differ across various implementations.

    - -

    Unless checking whether a variable is defined, typeof should be avoided at -all costs.

    The instanceof Operator

    The instanceof operator compares the constructors of its two operands. It is -only useful when comparing custom made objects. Used on built-in types, it is -nearly as useless as the typeof operator.

    - -

    Comparing Custom Objects

    - -
    function Foo() {}
    -function Bar() {}
    -Bar.prototype = new Foo();
    -
    -new Bar() instanceof Bar; // true
    -new Bar() instanceof Foo; // true
    -
    -// This just sets Bar.prototype to the function object Foo
    -// But not to an actual instance of Foo
    -Bar.prototype = Foo;
    -new Bar() instanceof Foo; // false
    -
    - -

    Using instanceof with Native Types

    - -
    new String('foo') instanceof String; // true
    -new String('foo') instanceof Object; // true
    -
    -'foo' instanceof String; // false
    -'foo' instanceof Object; // false
    -
    - -

    One important thing to note here is, that instanceof does not work on objects -that origin from different JavaScript contexts (e.g. different documents -in a web browser), since their constructors will not be the exact same object.

    - -

    In Conclusion

    - -

    The instanceof operator should only be used when dealing with custom made -objects that origin from the same JavaScript context. Just like the -typeof operator, every other use of it should be avoided.

    Type Casting

    JavaScript is a weakly typed language, so it will apply type coercion -wherever possible.

    - -
    // These are true
    -new Number(10) == 10; // Number.toString() is converted
    -                      // back to a number
    -
    -10 == '10';           // Strings gets converted to Number
    -10 == '+10 ';         // More string madness
    -10 == '010';          // And more 
    -isNaN(null) == false; // null converts to 0
    -                      // which of course is not NaN
    -
    -// These are false
    -10 == 010;
    -10 == '-10';
    -
    - - - -

    In order to avoid the above, use of the strict equal operator -is highly recommended. Although this avoids a lot of common pitfalls, there -are still many further issues that arise from JavaScript's weak typing system.

    - -

    Constructors of Built-In Types

    - -

    The constructors of the built in types like Number and String behave -differently when being used with the new keyword and without it.

    - -
    new Number(10) === 10;     // False, Object and Number
    -Number(10) === 10;         // True, Number and Number
    -new Number(10) + 0 === 10; // True, due to implicit conversion
    -
    - -

    Using a built-in type like Number as a constructor will create a new Number -object, but leaving out the new keyword will make the Number function behave -like a converter.

    - -

    In addition, having literals or non-object values in there will result in even -more type coercion.

    - -

    The best option is to cast to one of the three possible types explicitly.

    - -

    Casting to a String

    - -
    '' + 10 === '10'; // true
    -
    - -

    By prepending a empty string a value can easily be casted to a string.

    - -

    Casting to a Number

    - -
    +'10' === 10; // true
    -
    - -

    Using the unary plus operator it is possible to cast to a number.

    - -

    Casting to a Boolean

    - -

    By using the not operator twice, a value can be converted a boolean.

    - -
    !!'foo';   // true
    -!!'';      // false
    -!!'0';     // true
    -!!'1';     // true
    -!!'-1'     // true
    -!!{};      // true
    -!!true;    // true
    -

    コア

    Why Not to Use eval

    The eval function will execute a string of JavaScript code in the local scope.

    - -
    var foo = 1;
    -function test() {
    -    var foo = 2;
    -    eval('foo = 3');
    -    return foo;
    -}
    -test(); // 3
    -foo; // 1
    -
    - -

    But eval only executes in local scope when it is being called directly and -the name of the called function is actually eval.

    - -
    var foo = 1;
    -function test() {
    -    var foo = 2;
    -    var bar = eval;
    -    bar('foo = 3');
    -    return foo;
    -}
    -test(); // 2
    -foo; // 3
    -
    - -

    The use of eval should be avoided at all costs. 99.9% of its "uses" can be -achieved without it.

    - -

    eval in Disguise

    - -

    The timeout functions setTimeout and setInterval can both -take a string as their first argument. This string will always get executed -in the global scope since eval is not being called directly in that case.

    - -

    Security Issues

    - -

    eval also is a security problem as it executes any code given to it, -it should never be used with strings of unknown or untrusted origins.

    - -

    In Conclusion

    - -

    eval should never be used, any code that makes use of it is to be questioned in -its workings, performance and security. In case something requires eval in -order to work, its design is to be questioned and should not be used in the -first place, a better design should be used, that does not require the use of -eval.

    undefined and null

    JavaScript has two distinct values for nothing, the more useful of these two -being undefined.

    - -

    The Value undefined

    - -

    undefined is a type with exactly one value: undefined.

    - -

    The language also defines a global variable that has the value of undefined, -this variable is also called undefined. But this variable is not a constant, -nor is it a keyword of the language. This means that its value can be easily -overwritten.

    - - - -

    Some examples for when the value undefined is returned:

    - -
      -
    • Accessing the (unmodified) global variable undefined.
    • -
    • Implicit returns of functions due to missing return statements.
    • -
    • return statements which do not explicitly return anything.
    • -
    • Lookups of non-existent properties.
    • -
    • Function parameters which do not had any explicit value passed.
    • -
    • Anything that has been set to the value of undefined.
    • -
    - -

    Handling Changes to the Value of undefined

    - -

    Since the global variable undefined only holds a copy of the actual value of -undefined, assigning a new value to it does not change the value of the -type undefined.

    - -

    Still, in order to compare something against the value of undefined it is -necessary to retrieve the value of undefined first.

    - -

    In order to protect code against a possible overwritten undefined variable, a -common technique used is to add an additional parameter to an -anonymous wrapper, that gets no argument passed to it.

    - -
    var undefined = 123;
    -(function(something, foo, undefined) {
    -    // undefined in the local scope does 
    -    // now again refer to the value
    -
    -})('Hello World', 42);
    -
    - -

    Another way to achieve the same effect would be to use a declaration inside the -wrapper.

    - -
    var undefined = 123;
    -(function(something, foo) {
    -    var undefined;
    -    ...
    -
    -})('Hello World', 42);
    -
    - -

    The only difference being here, that this version results in 4 more bytes being -used in case it is minified and there is no other var statement inside the -anonymous wrapper.

    - -

    Uses of null

    - -

    While undefined in the context of the JavaScript language is mostly used in -the sense of a traditional null, the actual null (both a literal and a type) -is more or less just another data type.

    - -

    It is used in some JavaScript internals (like declaring the end of the -prototype chain by setting Foo.prototype = null), but in almost all cases it -can be replaced by undefined.

    Automatic Semicolon Insertion

    Although JavaScript has C style syntax, it does not enforce the use of -semicolons in the source code, it is possible to omit them.

    - -

    But JavaScript is not a semicolon-less language, it in fact needs the -semicolons in order to understand the source code. Therefore the JavaScript -parser automatically inserts them whenever it encounters a parse -error due to a missing semicolon.

    - -
    var foo = function() {
    -} // parse error, semicolon expected
    -test()
    -
    - -

    Insertion happens, and the parser tries again.

    - -
    var foo = function() {
    -}; // no error, parser continues
    -test()
    -
    - -

    The automatic insertion of semicolon is considered to be one of biggest -design flaws in the language, as it can change the behavior of code.

    - -

    How it Works

    - -

    The code below has no semicolons in it, so it is up to the parser to decide where -to insert them.

    - -
    (function(window, undefined) {
    -    function test(options) {
    -        log('testing!')
    -
    -        (options.list || []).forEach(function(i) {
    -
    -        })
    -
    -        options.value.test(
    -            'long string to pass here',
    -            'and another long string to pass'
    -        )
    -
    -        return
    -        {
    -            foo: function() {}
    -        }
    -    }
    -    window.test = test
    -
    -})(window)
    -
    -(function(window) {
    -    window.someLibrary = {}
    -
    -})(window)
    -
    - -

    Below is the result of the parser's "guessing" game.

    - -
    (function(window, undefined) {
    -    function test(options) {
    -
    -        // Not inserted, lines got merged
    -        log('testing!')(options.list || []).forEach(function(i) {
    -
    -        }); // <- inserted
    -
    -        options.value.test(
    -            'long string to pass here',
    -            'and another long string to pass'
    -        ); // <- inserted
    -
    -        return; // <- inserted, breaks the return statement
    -        { // treated as a block
    -
    -            // a label and a single expression statement
    -            foo: function() {} 
    -        }; // <- inserted
    -    }
    -    window.test = test; // <- inserted
    -
    -// The lines got merged again
    -})(window)(function(window) {
    -    window.someLibrary = {}; // <- inserted
    -
    -})(window); //<- inserted
    -
    - - - -

    The parser drastically changed the behavior of the code above, in certain cases -it does the wrong thing.

    - -

    Leading Parenthesis

    - -

    In case of a leading parenthesis, the parser will not insert a semicolon.

    - -
    log('testing!')
    -(options.list || []).forEach(function(i) {})
    -
    - -

    This code gets transformed into one line.

    - -
    log('testing!')(options.list || []).forEach(function(i) {})
    -
    - -

    Chances are very high that log does not return a function; therefore, -the above will yield a TypeError stating that undefined is not a function.

    - -

    In Conclusion

    - -

    It is highly recommended to never omit semicolons, it is also advocated to -keep braces on the same line with their corresponding statements and to never omit -them for one single-line if / else statements. Both of these measures will -not only improve the consistency of the code, they will also prevent the -JavaScript parser from changing its behavior.

    その他

    setTimeout and setInterval

    Since JavaScript is asynchronous, it is possible to schedule the execution of a -function by using the setTimeout and setInterval functions.

    - - - -
    function foo() {}
    -var id = setTimeout(foo, 1000); // returns a Number > 0
    -
    - -

    When setTimeout gets called, it will return the ID of the timeout and schedule -foo to run in approximately one thousand milliseconds in the future. -foo will then get executed exactly once.

    - -

    Depending on the timer resolution of the JavaScript engine that is running the -code, as well as the fact that JavaScript is single threaded and other code that -gets executed might block the thread, it is by no means a safe bet that one -will get the exact delay that was specified in the setTimeout call.

    - -

    The function that was passed as the first parameter will get called by the -global object, that means, that this inside the called function -refers to that very object.

    - -
    function Foo() {
    -    this.value = 42;
    -    this.method = function() {
    -        // this refers to the global object
    -        console.log(this.value); // will log undefined
    -    };
    -    setTimeout(this.method, 500);
    -}
    -new Foo();
    -
    - - - -

    Stacking Calls with setInterval

    - -

    While setTimeout only runs the function once, setInterval - as the name -suggests - will execute the function every X milliseconds. But its use is -discouraged.

    - -

    When code that is being executed blocks the timeout call, setInterval will -still issue more calls to the specified function. This can, especially with small -intervals, result in function calls stacking up.

    - -
    function foo(){
    -    // something that blocks for 1 second
    -}
    -setInterval(foo, 100);
    -
    - -

    In the above code foo will get called once and will then block for one second.

    - -

    While foo blocks the code setInterval will still schedule further calls to -it. Now, when foo has finished, there will already be ten further calls to -it waiting for execution.

    - -

    Dealing with Possible Blocking Code

    - -

    The easiest as well as most controllable solution, is to use setTimeout within -the function itself.

    - -
    function foo(){
    -    // something that blocks for 1 second
    -    setTimeout(foo, 100);
    -}
    -foo();
    -
    - -

    Not only does this encapsulate the setTimeout call, but it also prevents the -stacking of calls and it gives additional control.foo itself can now decide -whether it wants to run again or not.

    - -

    Manually Clearing Timeouts

    - -

    Clearing timeouts and intervals works by passing the respective ID to -clearTimeout or clearInterval, depending which set function was used in -the first place.

    - -
    var id = setTimeout(foo, 1000);
    -clearTimeout(id);
    -
    - -

    Clearing all timeouts

    - -

    As there is no built-in method for clearing all timeouts and/or intervals, -it is necessary to use brute force in order to achieve this functionality.

    - -
    // clear "all" timeouts
    -for(var i = 1; i < 1000; i++) {
    -    clearTimeout(i);
    -}
    -
    - -

    There might still be timeouts that are unaffected by this arbitrary number; -therefore, is is instead recommended to keep track of all the timeout IDs, so -they can be cleared specifically.

    - -

    Hidden use of eval

    - -

    setTimeout and setInterval can also take a string as their first parameter. -This feature should never be used, since it internally makes use of eval.

    - - - -
    function foo() {
    -    // will get called
    -}
    -
    -function bar() {
    -    function foo() {
    -        // never gets called
    -    }
    -    setTimeout('foo()', 1000);
    -}
    -bar();
    -
    - -

    Since eval is not getting called directly in this case, the string -passed to setTimeout will get executed in the global scope; thus, it will -not use the local variable foo from the scope of bar.

    - -

    It is further recommended to not use a string for passing arguments to the -function that will get called by either of the timeout functions.

    - -
    function foo(a, b, c) {}
    -
    -// NEVER use this
    -setTimeout('foo(1,2, 3)', 1000)
    -
    -// Instead use an anonymous function
    -setTimeout(function() {
    -    foo(a, b, c);
    -}, 1000)
    -
    - - - -

    In Conclusion

    - -

    Never should a string be used as the parameter of setTimeout or -setInterval. It is a clear sign of really bad code, when arguments need -to be supplied to the function that gets called. An anonymous function should -be passed that then takes care of the actual call.

    - -

    Further, the use of setInterval should be avoided since its scheduler is not -blocked by executing JavaScript.

    \ No newline at end of file diff --git a/site/pl/index.html b/site/pl/index.html deleted file mode 100644 index cfeeb4fa..00000000 --- a/site/pl/index.html +++ /dev/null @@ -1,1914 +0,0 @@ -JavaScript Garden -

    Wstęp

    JavaScript Garden jest rosnącą kolekcją dokumentów o najdziwniejszych -częściach języka JavaScript. Dokumentacja pomaga uniknąć najczęściej popełnianych -błędów, sybtelnych bugów, problemów wydajnościowych oraz złych praktyk, na które -niedoświadczeni programiści JavaScript mogą natrafić próbując poznać tajniki tego -języka.

    - -

    JavaScript Garden nie ma na celu nauczyć Cię języka JavaScript. Podstawowa -wiedza na temat języka jest wymagana do zrozumienia zagadnień poruszanych w tym -przewodniku. Aby nauczyć się podstaw jezyka JavaScript, odwiedź znakomity -przewodnik na stronach Mozilla Developer Network.

    Licencja

    JavaScript Garden jest publikowany w ramach licencji MIT i kod źródłowy znajduje -się na serwerze GitHub. Jeśli znajdziesz jakieś błędy lub literówek zgłoś proszę -problem lub rozwiązag go i zglosić pull request ze swojego repozytorium. -Możesz nas także znaleźć w pokoju JavaScript na chacie Stack Overflow.

    Obiekty

    Wykorzystanie obiektów i ich właściwości

    Wszystko w JavaScripcie zachowuje sie jak obiekt, z dwoma wyjątkami -null oraz undefined.

    - -
    false.toString() // 'false'
    -[1, 2, 3].toString(); // '1,2,3'
    -
    -function Foo(){}
    -Foo.bar = 1;
    -Foo.bar; // 1
    -
    - -

    Popularnym błędem jest wykorzystanie literałów liczbowych jako obiektu. -Spowodowanie jest to usterką w parserze JavaScript, który interpretuje kropkę -po literale liczbowym jako rozdzielenie części całkowitej od części po przecinku -liczby.

    - -
    2.toString(); // wyrzuca błąd SyntaxError
    -
    - -

    Istnieje kilka rozwiązań, dzieki którym literał liczbowy będzie zachowywał się -jak obiekt.

    - -
    2..toString(); // druga kropka jest poprawnie rozpoznana
    -2 .toString(); // zauważ, że pozostawiona jest spacja przed kropką
    -(2).toString(); // 2 zostanie zewaluowane najpiewr
    -
    - -

    Obiekty jako typy danych

    - -

    Obiekty w języku JavaScript mogą być używana jako tablice asocjacyjne. -Ponieważ obiekty głównie składają się z mapowań pomiędzy nazwanymi właściwościami (kluczami) -a wartościami dla tych atrybutów.

    - -

    Używając literału obiektu - notacji {} - istnieje możliwość stworzenie obiektu prostego. -Ten nowy obiekt bedzie dziedziczył z Object.prototype oraz -nie bedzie posiadał żadnych własnych właściwości zdefiniowanych w sobie.

    - -
    var foo = {}; // nowy pusty obiekt
    -
    -// nowy obiekt z właściwością test o wartości 12
    -var bar = {test: 12}; 
    -
    - -

    Dostęp do właściwości

    - -

    Właściwości obiektu można uzyskać na dwa sposoby, poprzez notację z kropką -lub notacje z nawiasami kwadratowymi.

    - -
    var foo = {name: 'Kitten'}
    -foo.name; // kitten
    -foo['name']; // kitten
    -
    -var get = 'name';
    -foo[get]; // kitten
    -
    -foo.1234; // wyrzuca błąd SyntaxError
    -foo['1234']; // działa, zwraca undefined
    -
    - -

    Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą że notacja z nawiasami -kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzycenia -błędu podczas odczytu nieistniejącej właściwości.

    - -

    Usuwanie właściwości

    - -

    Jedynym sposobem na faktycze usunięcie własności z obiektu jest użycie operatora -delete. Ustawienie własności na undefined lub null usunie tylko wartość -związaną z własnością, ale nie usunie to klucza (nazwy własności) z obiektu.

    - -
    var obj = {
    -    bar: 1,
    -    foo: 2,
    -    baz: 3
    -};
    -obj.bar = undefined;
    -obj.foo = null;
    -delete obj.baz;
    -
    -for(var i in obj) {
    -    if (obj.hasOwnProperty(i)) {
    -        console.log(i, '' + obj[i]);
    -    }
    -}
    -
    - -

    Powyższy kod wypisuje dwie linie bar undefined i foo null - tylko własność baz -została usunięta i dlatego nie została wypisana.

    - -

    Notacja właściwości

    - -
    var test = {
    -    'case': 'I am a keyword so I must be notated as a string',
    -    delete: 'I am a keyword too so me' // wyrzuca błąd SyntaxError
    -};
    -
    - -

    Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst(bez cudzysłowów -lub apostrofów) lub jako string (w cudzisłowach lub apostrofach). -Ze względu na kolejne niedociągnięcie w parserze JavaScript -powyższy kod wyrzuci błąd SyntaxError dla implementacji JavaScript ponizej ECMAScript 5.

    - -

    Ten błąd wynika z faktu, że delete jest słowem kluczowym, dlatego musi zostać -zapisany jako string (z cudzysłowami lub apostrofami), aby zapewnić, że zostanie -to poprawnie zinterpretowane przez starsze silniki języka JavaScript.

    Prototyp

    JavaScript nie posiada klasycznego modelu dziedziczenia, lecz zamiast tego -dziedziczenie jest realizowane poprzez prototypy.

    - -

    Choć jest to często uważane za jedną ze słabości języka JavaScript, -prototypowy model dziedziczenia, jest w rzeczywistości potężniejszy od klasycznego -modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowym -jest dość proste, podczas gdy zrobienie odwrotnie to już o wiele trudniejsze zadanie.

    - -

    Ze względu na fakt, że w JavaScript jest w zasadzie jedynym powszechnie stosowanym -językiem, któy posiada prototypowy model dziedziczenia, to wymaga troche czasu aby -dostosować się do różnic pomiędzy tymi dwoma modelami.

    - -

    Pierwszą znaczącą różnicą jest to, że dziedziczenie w JavaScript odbywa się za pomocą -tak zwanych łańcuchów prototypów.

    - - - -
    function Foo() {
    -    this.value = 42;
    -}
    -Foo.prototype = {
    -    method: function() {}
    -};
    -
    -function Bar() {}
    -
    -// Ustawienie prototypu Bar na nową instancję Foo
    -Bar.prototype = new Foo();
    -Bar.prototype.foo = 'Hello World';
    -
    -// Upewniamy się, że Bar jest ustawiony jako rzeczywisty konstruktor
    -Bar.prototype.constructor = Bar;
    -
    -var test = new Bar() // tworzymy nową instancję Bar
    -
    -// The resulting prototype chain
    -test [instance of Bar]
    -    Bar.prototype [instance of Foo] 
    -        { foo: 'Hello World' }
    -        Foo.prototype
    -            { method: ... }
    -            Object.prototype
    -                { toString: ... /* etc. */ }
    -
    - -

    W powyższym przykładzie obiekt test będzie dziedziczył z obydwu tj. -Bar.prototyp i Foo.prototyp, stąd będzie miał dostęp do funkcji method, -która była zdefiniowana w Foo. Ponadto obiekt będzie miał dostęp do -właściwości value, która jest jednyną instancją Foo i stała się jego prototypem. -Ważne jest, aby pamiętać new Bar nie tworzy nowej instancji Foo, -ale wykorzystuje instancje, którą jest przypisana do własności prototype. -Zatem Wszystkie instancje Bar będą dzieliły tą samą własność value.

    - - - -

    Wyszukiwanie własności

    - -

    Podczas dostępu do właściwości obiektu, JavaScript przejdzie w górę łańcucha -prototypów dopóki nie znajdzie właściwości z żądaną nazwą.

    - -

    Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha mianowicie Object.prototype -i nadal nie znajdzie określonej właściwości, to zwróci wartość -undefined.

    - -

    Właściwość prototype

    - -

    Podczas gdy właściwość prototype jest używana przez język do budowania łańcucha -prototypów, istnieje możliwość przypisania do niej dowolnej wartości. Jednakże -prymitywne typy będą po prostu ignorowanie, jeżeli zostaną ustawione jako prototype.

    - -
    function Foo() {}
    -Foo.prototype = 1; // nie ma wpływu
    -
    - -

    Przypisywanie obiektów, jak pokazano w powyższym przykładzie, zadziała i pozwala -na dynamiczne tworzenie łańcuchów prototypów.

    - -

    Wydajność

    - -

    Czas wyszukiwania właściwości, które są na końcu łańcucha prototypów może mieć -negatywny wpływ na wydajność krytycznych części kodu. Dodatkowo, próba dostępu -do nieistniejącej właściwości powoduje zawsze przeszukanie całego łańcucha prototypów.

    - -

    Również, podczas iteracji po właściwościach obiektu -każda właściwość, która znajduje się w łańcuchu prototypów niezależnie -na jakim znajduje się poziomie zostanie wyliczona.

    - -

    Rozszerzanie natywnych prototypów

    - -

    Rozszerzanie Object.prototype lub innego prototypu wbudowanych typów jest jednym z -najczęściej używanych niedoskonałej częsci języka JavaScript.

    - -

    Technika ta nazywana jest monkey patching i łamie zasady enkapsulacji. -Jednak jest szeroko rozpowszechniona w frameworkach takich jak Prototype. -Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez dodawanie do nich -niestandardowych funkcjonalności.

    - -

    Jedynym dobrym powodem do rozszerzania wbudowanych prototypów jest portowanie
    -funkcjonalności znajdujących sie w nowszych silnikach JavaScript np. Array.forEach

    - -

    Wnioski

    - -

    Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczanie -należy całkowicie rozumieć prototypowy model dziedziczenia. Ponadto należy uważać -na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń -aby uniknąć problemów z wydajnością. Natywne prototypy nie powinny nigdy być -rozszerzane, chyba że ze względu na wprowadzanie kompatybilności z nowszymi silnikami -JavaScript.

    hasOwnProperty

    W celu sprawdzenia czy dana właściwość została zdefiniowana w tym obiekcie a nie -w łańcuchu prototypów niezbędne jest skorzystanie z metody -hasOwnProperty, która wszystkie obiekty dziedziczą z Object.prototype.

    - - - -

    hasOwnProperty jest jedyna metodą w języku JavaScript która operuje na właściwościach -i nie przegląda całego łańcucha prototypów.

    - -
    // Zatrucie Object.prototype
    -Object.prototype.bar = 1; 
    -var foo = {goo: undefined};
    -
    -foo.bar; // 1
    -'bar' in foo; // true
    -
    -foo.hasOwnProperty('bar'); // false
    -foo.hasOwnProperty('goo'); // true
    -
    - -

    Tylko hasOwnProperty da prawidłowy i oczekiwany rezultat. Jest to istotne podczas -iteracji po właściwościach obiektu. Nie ma innego sposobu na ominięcie -właściwości, która nie została zdefiniowana przez ten konkretny obiekt, -ale gdzieś indziej w łańcuchu prototypów.

    - -

    hasOwnProperty jako właściwość

    - -

    JavaScript nie chroni właściwości o nazwie hasOwnProperty, zatem istnieje -możliwość, że obiekt może posiadać tak nazwaną właściwość. Konieczne jest użycie -zewnętrznego hasOwnProperty, aby otrzymać poprawne rezultaty.

    - -
    var foo = {
    -    hasOwnProperty: function() {
    -        return false;
    -    },
    -    bar: 'Here be dragons'
    -};
    -
    -foo.hasOwnProperty('bar'); // zawsze zwraca false
    -
    -// Została użyta metoda innego obiektu i wywołana z konkekstem 
    -// `this` ustawionym na foo
    -({}).hasOwnProperty.call(foo, 'bar'); // true
    -
    - -

    Wnioski

    - -

    Jedyną metodą służącą do sprawdzenia zdefiniowania jakiejś właściwości w konkretnym -obiekcie jest metoda hasOwnProperty. Zaleca się korzystać z hasOwnProperty jako część -każdej pętli for in, pozwoli to uniknąć błędów pochodzących z -rozszerzonych natywnych prototypów.

    The for in Loop

    Podobnie jak operator in, pętla for in przeszukuje łańcuch prototypów -podczas iteracji po właściwościach obiektu.

    - - - -
    // Zatrucie Object.prototype
    -Object.prototype.bar = 1;
    -
    -var foo = {moo: 2};
    -for(var i in foo) {
    -    console.log(i); // wyświetla obie właściwości: bar i moo
    -}
    -
    - -

    Ponieważ nie jest możliwe, aby zmienić zachowanie pętli for in to niezbędne -jest odfiltrowanie niechcianych właściwości wewnątrz ciała pętli, korzystając -z metody hasOwnProperty z Object.prototype.

    - - - -

    Korzystanie z hasOwnProperty do odfiltrowania

    - -
    // foo z przykładu powyżej
    -for(var i in foo) {
    -    if (foo.hasOwnProperty(i)) {
    -        console.log(i);
    -    }
    -}
    -
    - -

    To jest jedyna poprawna wersja, którą należy używać. Ze względu na użycie -hasOwnProperty zostanie wypisane jedynie moo. Gdy opuścimy hasOwnProperty -kod będzie podatny na błędy, gdy natywne prototypy np. Object.prototype -zostanie rozszerzony.

    - -

    Prototype jest jednym z szeroko rozpowszechniony frameworków, który dokonuje -takiego rozszerzenia. Używanie tego frameworku oraz nie używanie w pętle for in -metody hasOwnProperty gwarantuje błędy w wykonaniu.

    - -

    Wnioski

    - -

    Zaleca się aby zawsze używać metody hasOwnProperty. Nigdy nie powinno się dokonywać -żadnych założeń na temat środowiska, w którym kod będzie wykonywany i czy natywne -prototypy zostały rozszerzone czy nie.

    Funkcje

    Deklaracje funkcji i wyrażenia funkcyjne

    Funcje w języku JavaScript są typami pierwszoklasowymi. Co oznacza, że mogą -być przekazywane jak każda inna wartość. Jednym z typowych zastosowań tej cechy -jest przekazywanie anonimowej funkcji jako callback do innej, prawdopodobnie -asynchronicznej funkcji.

    - -

    Deklaracja funckcji

    - -
    function foo() {}
    -
    - -

    Powyższa funkcja zostaje wyniesiona zanim program wystartuje, dzięki temu -jest dostępna wszędzie w ramach zasięgu, w którym została zadeklarowana, -nawet jeżeli ta funkcja została wywołana przed faktyczną definicją w kodzie źródłowym.

    - -
    foo(); // Działa ponieważ definicja funkcji została wyniesiona 
    -       // na początek zasięgu przed uruchomieniem kodu
    -function foo() {}
    -
    - -

    Wyrażenie funkcyjne

    - -
    var foo = function() {};
    -
    - -

    Ten przykład przypisuje nienazwaną i anonimową funkcję do zmiennej foo.

    - -
    foo; // 'undefined'
    -foo(); // wyrzuca błąd TypeError
    -var foo = function() {};
    -
    - -

    Ze względu na fakt, że deklaracja var wynosi zmienną foo na początek zasięgu, -zanim kod faktycznie zostanie uruchomiony, foo będzie zdefiniowane kiedy skrypt -będzie wykonywany.

    - -

    Ale ponieważ przypisania robione są dopiero podczas wykonania, wartość foo będzie -ustawiona na domyślną wartość undefined zanim powyższy kod -zostanie uruchomiony.

    - -

    Nazwane wyrażenia funkdyjne

    - -

    Kolejnym specjalnym przypadkiem jest przypisanie nazwanej funkcji.

    - -
    var foo = function bar() {
    -    bar(); // Działa
    -}
    -bar(); // wyrzuca ReferenceError
    -
    - -

    W zewnętrznym zakresie bar nie będzie dostępne, ponieważ funkcja zostaje -przypisana do foo, jednakże w wewnętrznym zakresie bar będzie dostępna. -Jest to spowodowane tym, jak działa rozwiązywanie nazw -w języku JavaScript. Nazwa funkcji jest zawsze dostępna w lokalnym -zakresie tej funkcji.

    Jak działa this

    JavaScript posiada inną koncepcję odnośnie tego na co wskazuje specjalna -nazwa this, niż większość innych języków programowania. Istnieją dokładnie -pięć różnych sytuacji w których wartość this zostaje przypisana w języku JavaScript.

    - -

    JavaScript has a different concept of what the special name this refers to -than most other programming languages do. There are exactly five different -ways in which the value of this can be bound in the language.

    - -

    Zasięg globalny

    - -
    this;
    -
    - -

    Używanie this w globalnym zasięgu, zwróci po prostu referencje do obiektu global.

    - -

    Wywołanie funkcji

    - -
    foo();
    -
    - -

    Tutaj this również będzie wkazywało na obiekt global

    - - - -

    Wywoływanie metody

    - -
    test.foo(); 
    -
    - -

    W tym przypadku this będzie wskazywało na test.

    - -

    Wywołanie konstruktora

    - -
    new foo(); 
    -
    - -

    Wywołanie funkcji, które jest poprzedzone słowem kluczowym new zachowuje się -jak konstruktor. Wewnątrz funkcji this będzie -wskazywało na nowo utworzony obiekt.

    - -

    Jawne ustawienie this

    - -
    function foo(a, b, c) {}
    -
    -var bar = {};
    -foo.apply(bar, [1, 2, 3]); // tablica zostanie zamieniona w to co poniżej
    -foo.call(bar, 1, 2, 3); // rezultat a = 1, b = 2, c = 3
    -
    - -

    Używając metod call lub apply z prototypu Function.prototype, wartość this -wewnątrz wołanej funkcji zostanie jawnie ustawiona na pierwszy argument przekazany -podczas wywołania tych metod.

    - -

    Zatem w powyższym przykładzie przypadek Wywoływanie metody nie będzie miał -miejsca i this wewnątrz foo będzie wskazywać na bar.

    - - - -

    Częste pułapki

    - -

    Mimo iż Większość z tych przypadków ma sens, to pierwszy przypadek powinien być -traktorany jako błąd podczas projektowania języka i nigdy nie wykorzystywany -w praktyce.

    - -
    Foo.method = function() {
    -    function test() {
    -        // wewnątrz tej funkcji this wskazuje na obiekt global
    -    }
    -    test();
    -}
    -
    - -

    Powszechnym nieporozumieniem jest, że this wewnątrz test wskazuje na Foo, -podczas gdy w rzeczywistości tak nie jest.

    - -

    Aby uzyskać dostęp do Foo wewnątrz test niezbędne jest stworzenie wewnątrz -metody lokalnej zmiennej, która będzie wskazywała na Foo.

    - -
    Foo.method = function() {
    -    var that = this;
    -    function test() {
    -        // Należy używać that zamiast this wewnątrz tej funkcji
    -    }
    -    test();
    -}
    -
    - -

    that jest zwykłą zmienną, ale jest to powszechnie stosowana konwencja, aby otrzymać -wartość zewnętrznego this. W połączeniu z domknięciami(closures) -jest to sposób na przekazywanie wartości this wokoło.

    - -

    Metody przypisywania

    - -

    Kolejną rzeczą, która nie działa w języku JavaScript jest nadawanie aliasów -funkcjom, co oznacza przypisanie metody do zmiennej.

    - -
    var test = someObject.methodTest;
    -test();
    -
    - -

    Podobnie jak w pierwszym przypadku test zachowuje się jak wywołanie zwykłej -funkcji, a zatem wewnątrz funkcji this już nie będzie wskazywało someObject.

    - -

    Podczas gdy późne wiązanie this może się na początku wydawać złym pomysłem, -to w rzeczywistości jest to rzecz, która powoduje że -dziedziczenie prototypowe działa.

    - -
    function Foo() {}
    -Foo.prototype.method = function() {};
    -
    -function Bar() {}
    -Bar.prototype = Foo.prototype;
    -
    -new Bar().method();
    -
    - -

    Kiedy metoda method zostanie wywołana na instancji Bar, this będzie -wskazywało właśnie tą instancję.

    Domknięcia i referencje

    Jedną z najpotężniejszych funkcjonalności języka JavaScript są domknięcia, -oznacza to że zasięg zawsze posiada dostęp do zewnętrznego zasięgu w którym -został zdefiniowany. Ponieważ zasięg w JavaScript można definiować tylko poprzez -funckję, wszystkie funkcje domyślnie zachowują się jak domknięcia.

    - -

    Emulowanie prywatnych zmiennych

    - -
    function Counter(start) {
    -    var count = start;
    -    return {
    -        increment: function() {
    -            count++;
    -        },
    -
    -        get: function() {
    -            return count;
    -        }
    -    }
    -}
    -
    -var foo = Counter(4);
    -foo.increment();
    -foo.get(); // 5
    -
    - -

    Tutaj Counter zwraca dwa domknięcia: funkcję increment oraz funckję get. -Obie te funkcję trzymają referencję do zasięgu Counter a co za tym idzie -zawsze posiadają dostęp do zmiennej count tak, jakby ta zmienna była zdefiniowana -w zasięgu tych funkcji.

    - -

    Dlaczego zmienne przywatne działają

    - -

    Ponieważ nie ma możliwości wskazania lub przypisania zasięgu w JavaScript, to -nie istnieje sposób aby uzyskać dostęp do zmiennej count z zewnątrz. -Wykorzystanie tych dwóch domkinęć jest jedynym sposobem na interakcję z tą zmienną.

    - -
    var foo = new Counter(4);
    -foo.hack = function() {
    -    count = 1337;
    -};
    -
    - -

    Powyższy kod nie zmieni wartości zmiennej count wewnątrz zasięgu Counter, -ponieważ foo.hack nie została zadeklarowana wewnątrz tego konkretnego zasięgu. -Zamiast tego funkcja utworzy lub nadpisze globalną zmienną count.

    - -

    Domknięcia wewnątrz pętli

    - -

    Jednym z częstrzych błędów jest wykorzystywanie domknięć wewnątrz pętli, -aby wartość zmiennej po której odbywa się iteracja był kopiowana do -wewnętrznej funkcji.

    - -
    for(var i = 0; i < 10; i++) {
    -    setTimeout(function() {
    -        console.log(i);  
    -    }, 1000);
    -}
    -
    - -

    Powyższy kod nie wypisze numerów od 0 do 9, ale wypisze -dziesięć razy liczbę 10.

    - -

    Anonimowa funkcja trzyma wskaźnik do zmiennej i i podczas uruchomienia -console.log, pętla for już zakończyła działanie i wartość zmiennej i -została ustawiona na 10.

    - -

    Aby otrzymać zamierzony efekt, niezbędne jest skopiowanie wartości -zmiennej i.

    - -

    Unikanie problemu z referencją

    - -

    Aby skopiować wartość zmiennej, po której iterujemy w pętli, należy skorzystać -z anonimowego wrappera.

    - -
    for(var i = 0; i < 10; i++) {
    -    (function(e) {
    -        setTimeout(function() {
    -            console.log(e);  
    -        }, 1000);
    -    })(i);
    -}
    -
    - -

    Zewnętrzna anonimowa funkcja zostaje wywołana od razu z parametrem i -jako pierwszym argumentem i otrzyma kopię wartości zmiennej i jako -zmienną e.

    - -

    Anonimowa funkcja która zostaje przekazana do setTimeout teraz posiada -referencję do zmiennej e, która nie zostanie zmieniona przez pętle for.

    - -

    Istnieje jeszcze jeden sposób na osiągnięcie tego samego efektu. Należy zwrócic -fukcję z anonimowego wrappera, wówczas kod będzie zachowywał się jak ten -wcześniejszy.

    - -
    for(var i = 0; i < 10; i++) {
    -    setTimeout((function(e) {
    -        return function() {
    -            console.log(e);
    -        }
    -    })(i), 1000)
    -}
    -

    Obiekt arguments

    Każda zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej arguments. -Ta zmienna trzyma listę wszystkich argumentów przekazanych do funkcji.

    - - - -

    Obiekt arguments nie jest typu Array. Mimo, że posiada pewne cechy -semantyki tablic - właściwość length - to nie dziedziczy on z Array.prototype, -ale w rzeczywistości z Object.

    - -

    Ze względu na to nie można używać standardowych dla tablic metod takich jak -push, pop czy slice na obiekcie arguments. Mimo, że iteracja przy pomocy -pętli for działa dobrze, to aby skorzystać ze standardowych metod tablicowych -należy skonwertować arguments do prawdziwego obiekt Array.

    - -

    Konwersja do tablicy

    - -

    Poniższy kod zwróci nowy obiekt Array zawierający wszystkie elementy -obiektu arguments.

    - -
    Array.prototype.slice.call(arguments);
    -
    - -

    Jednakże konwersja ta jest wolna i nie jest zalecana w sekcjach, -które mają duży wpływ na wydajność.

    - -

    Przekazywanie argumentów

    - -

    Zalecany sposób przekazywania argumentów z jednej funkcji do następnej -wyglada następująco.

    - -
    function foo() {
    -    bar.apply(null, arguments);
    -}
    -function bar(a, b, c) {
    -    // do stuff here
    -}
    -
    - -

    Kolejną sztuczką jest użycie razem call i apply w celu stworzenia -szybkich i nieograniczonych wrapperów.

    - -
    function Foo() {}
    -
    -Foo.prototype.method = function(a, b, c) {
    -    console.log(this, a, b, c);
    -};
    -
    -// Stworzenie nieograniczoną wersję metody "method" 
    -// która przyjmuje parametry: this, arg1, arg2...argN
    -Foo.method = function() {
    -
    -    // Rezultat: Foo.prototype.method.call(this, arg1, arg2... argN)
    -    Function.call.apply(Foo.prototype.method, arguments);
    -};
    -
    - -

    Parametry formalne i indeksy argumentów

    - -

    Obiekt arguments tworzy funckje getter i setter nie tylko dla swoich -właściwości, ale również dla parametrów formalnych funkcji.

    - -

    W rezultacie zmiana wartości parametru formalnego zmieni również wartość -odpowiadającemu mu wpisowi w obiekcie arguments, zachodzi to również w drugą stronę.

    - -
    function foo(a, b, c) {
    -    arguments[0] = 2;
    -    a; // 2                                                           
    -
    -    b = 4;
    -    arguments[1]; // 4
    -
    -    var d = c;
    -    d = 9;
    -    c; // 3
    -}
    -foo(1, 2, 3);
    -
    - -

    Mity i prawdy o wydajności

    - -

    Obiekt arguments jest zawsze tworzony z wyjątkiem dwóch przypadków, gdy -zmienna o takiej nazwie jest zdefiniowana wewnątrz funkcji lub jeden z parametrów -formalnych funkcji ma taką nazwę. Nie ma znaczenia czy obiekt arguments jest -używany czy nie.

    - -

    Zarówno gettery jak i settery są zawsze tworzone, zatem używanie ich nie ma -praktycznie żadnego wpływu na wydajność. Zwłaszcza w rzeczywistym kodzie, który -wykorzystuje coś więcej niż tylko prosty dostęp do właściwości obiektu arguments.

    - - - -

    Jednakże, istnieje jeden przypadek w którym wydajność drastycznie spada w -nowoczesnych silnikach JavaScript. Ten przypadek to wykorzystanie -arguments.callee.

    - -
    function foo() {
    -    arguments.callee; // operowanie na obiekcie funkcji
    -    arguments.callee.caller; // i obiekcie funkcji wywołującej
    -}
    -
    -function bigLoop() {
    -    for(var i = 0; i < 100000; i++) {
    -        foo(); // Normalnie zostałaby wykorzystana metoda inline
    -    }
    -}
    -
    - -

    W powyższym przykładzie foo nie może zostać wykorzystana metoda inline -ponieważ potrzebne są nie tylko informacje na własny temat ale również -na temat funkcji wywołującej. Takie użycie nie tylko uniemożliwia -inlining i korzyści z niej wynikające, ale również stanowi złamanie -zasad enkapsulacji ponieważ ta funkcja jest zależna od kontekstu -w jakim została wywołana.

    - -

    Mocno zalecane jest aby nigdy nie korzystać z arguments.callee -i żadnej jej własności.

    - -

    Konstruktory

    Konstruktory w JavaScript również wyglądają inaczej niż innych języka. Każde -wywołanie funkcji, które jest poprzedone słowem kluczowym new zachowuje się -jak konstruktor.

    - -

    Wewnątrz konstruktora - wywoływanej fukcji - wartość this wskazuje na -nowo utworzony obiekt Object. Prototyp prototype tego -nowego obiektu będzie wskazywał na prototyp prototype obiektu fukcji, -która została wywołana jako konstruktor.

    - -

    Jeżeli wywołana funkcja nie posiada jawnej deklaracji return, wówczas -fukcja domyślnie zwraca wartość this - nowy obiekt.

    - -
    function Foo() {
    -    this.bla = 1;
    -}
    -
    -Foo.prototype.test = function() {
    -    console.log(this.bla);
    -};
    -
    -var test = new Foo();
    -
    - -

    Powyżej wywołanya została funkcja Foo jako konstruktor oraz ustawia -nowo utworzonemu obiektowi właściwość prototype na Foo.prototype.

    - -

    W tym przypadku jawna deklaracja return w funkcji zwraca wartość -ustawioną w deklaracji, ale tylko jeżeli zwracaną wartością jest -obiekt Object.

    - -
    function Bar() {
    -    return 2;
    -}
    -new Bar(); // nowy obiekt
    -
    -function Test() {
    -    this.value = 2;
    -
    -    return {
    -        foo: 1
    -    };
    -}
    -new Test(); // zwrócony obiekt
    -
    - -

    Jeżeli słowo kluczowe new zostanie pominięte funkcja nie zwróci nowego -obiektu.

    - -
    function Foo() {
    -    this.bla = 1; // zostanie ustawiona w obiekcie global
    -}
    -Foo(); // undefined
    -
    - -

    Mimo, że powyższy kod może zadziałać w pewnych przypadkach, w związku -z działaniem this w języku JavaScript to jako -wartość thiszostanie wykorzystany obiekt global.

    - -

    Fabryki

    - -

    Aby móc ominąć słowo kluczowe new konstruktor musi jawnie zwracać wartość.

    - -
    function Bar() {
    -    var value = 1;
    -    return {
    -        method: function() {
    -            return value;
    -        }
    -    }
    -}
    -Bar.prototype = {
    -    foo: function() {}
    -};
    -
    -new Bar();
    -Bar();
    -
    - -

    Oba wywołania Bar zwrócą tą samą rzecz, nowo utworzony obiekt, który posiada -właściwość nazwaną method w sobie, dla którego Bar jest Domknięciem.

    - -

    Należy również pamiętać, że wywołanie new Bar() nie ma wpływu na -prototyp zwróconego obiektu (prototypem będzie object.prototype a nie Bar.prototype). -Podczas gdy prototyp zostanie przypisany do nowo utworzonego obiektu, to jednak Bar -nidgy nie zwróci tego nowego obiektu Bar, ale literał obiektu, który jest po -słowie kluczowym return.

    - -

    W powyższym przykładzie nie ma żadnej różnicy w działaniu pomiędzy użyciem -i nieużyciem słowa kluczowego new.

    - -

    Tworzenie nowych obiektów korzystając z fabryk

    - -

    Często zaleca się nie korzystać z operatora new ponieważ zapominając -go zastosować może prowadzić do błędów.

    - -

    W celu stworzenia nowego obiektu, powinno się używać fabryki i konstruować -nowy obiekt wewnątrz tej fabryki.

    - -
    function Foo() {
    -    var obj = {};
    -    obj.value = 'blub';
    -
    -    var private = 2;
    -    obj.someMethod = function(value) {
    -        this.value = value;
    -    }
    -
    -    obj.getPrivate = function() {
    -        return private;
    -    }
    -    return obj;
    -}
    -
    - -

    Mimo, że powyższy kod jest odporny na brak słowa kluczowego new i ułatwia -korzystanie ze zmiennych prywatnych, to posiada -pewne wady. -While the above is robust against a missing new keyword and certainly makes -the use of private variables easier, it comes with some -downsides. - 1. Zużywa więcej pamięci, ponieważ tworzony obiekt nie współdzieli metod - poprzez prototyp - 2. Aby móc dziedziczyć fabryka musi skopiować wszystkie metody z dziedziczonego - obiektu lub przypisać ten obiekt, z którego się dziedziczy, jako prototyp - do nowo utworzonego obiektu. - 3. Porzucenie łańcucha prototypów tylko ze względu na opuszczone słowo kluczowe - new jest sprzeczne z duchem języka.

    - -

    Wnioski

    - -

    Pominięcie słowa kluczowego new może prowadzić do błędów, ale na pewno nie -powinno to być powodem odrzucenia używania prototypów w ogóle. Sprowadza się to -do wyboru rozwiązania, które bardziej pasuje do potrzeb aplikacji. Szczególnie -ważne jest aby wybrać określony styl tworzenia obiektów i się go trzymać.

    Zasięg zmiennych i przestrzenie nazw

    Mimo, że JavaScript radzi sobie dobrze ze składnią, opisującą dwa pasujące -nawiasy klamrowe jako blok, to jednak nie wspiera zasięgu blokowego. -Jedynym zasięgiem jaki istnieje w JavaScript jest zasięg funkcyjny.

    - -
    function test() { // definiuje zasięg (scope)
    -    for(var i = 0; i < 10; i++) { // nie definiuje zasięgu (scope)
    -        // count
    -    }
    -    console.log(i); // 10
    -}
    -
    - - - -

    W JavaScripcie nie ma również przestrzeni nazw, co oznacza, że wszystko jest -definiowane w jednej globalnie współdzielonej przestrzeni nazw.

    - -

    Za każdym razem gdy zmienna jest -Z każdym odwołaniem do zmiennej JavaScript przeszukuje w górę wszystkie zasięgi -dopóki nie znajdzie tej zmiennej. W przypadku gdy przeszukiwanie dotrze do globalnego -zasięgu i nadal nie znajdzie żądanej nazwy to wyrzuca błąd ReferenceError.

    - -

    Zmora globalnych zmiennych

    - -
    // script A
    -foo = '42';
    -
    -// script B
    -var foo = '42'
    -
    - -

    Powyższe dwa skrypty nie dają tego samego efektu. Skrypt A definiuje zmienna -nazwaną foo w globalnym zasięgu natomiast skrypt B definiuje foo -w aktualnym zasięgu.

    - -

    Jeszcze raz, to wcale nie daje tego samego efektu, nie użycie var może mieć -poważne konsekwencje.

    - -
    // globalny zasięg
    -var foo = 42;
    -function test() {
    -    // lokalny zasięg
    -    foo = 21;
    -}
    -test();
    -foo; // 21
    -
    - -

    Pominięcie słowa var w deklaracji wewnątrz funkcji test nadpisze wartość -zmiennej globalnej foo. Mimo, że nie wygląda to na początku jak duży problem, -to posiadając wiele tysięcy linii kodu w JavaScript i nie korzystanie z var -wprowadzi straszne i trudne do wyśledzenia błędy.

    - -
    // globalny zasięg 
    -var items = [/* jakaś lista */];
    -for(var i = 0; i < 10; i++) {
    -    subLoop();
    -}
    -
    -function subLoop() {
    -    // scope of subLoop
    -    for(i = 0; i < 10; i++) { // brakuje słowa var w deklaracji
    -        // do amazing stuff!
    -    }
    -}
    -
    - -

    Zewnętrzna pętla zakończy działanie po pierwszym wywołaniu subLoop, ponieważ -subLoop nadpisuje wartość globalnej zmiennej i. Użycie var w drugiej pętli -for pozwoliło by łatwo uniknąć problemu. Słowo kluczowe var nie powinno być -nigdy pominięte w deklaracji, chyba że pożądanym skutkiem jest wpłynięcie na -zewnętrzny zasięg.

    - -

    Lokalne zmienne

    - -

    Jedynym źródłem zmiennych lokalnych w JavaScripcie są parametry funkcji -oraz zmienne zadeklarowane poprzez deklaracje var wewnątrz funkcji.

    - -
    // globalny zasięg
    -var foo = 1;
    -var bar = 2;
    -var i = 2;
    -
    -function test(i) {
    -    // lokalny zasięg fukcji test
    -    i = 5;
    -
    -    var foo = 3;
    -    bar = 4;
    -}
    -test(10);
    -
    - -

    Zmienne foo oraz i są lokalnymi zmiennymi wewnątrz zasiegu funkcji test, -natomiast przypisanie wartości do bar nadpisze zmienną globalną o tej samej nazwie.

    - -

    "Hoisting" - wywindowanie, podnoszenie

    - -

    JavaScript winduje deklaracje. Oznacza to, że zarówno deklaracja ze słowem -kluczowym var jak i deklaracje funkcji function zostaną przeniesione na -początek otaczającego zasięgu.

    - -
    bar();
    -var bar = function() {};
    -var someValue = 42;
    -
    -test();
    -function test(data) {
    -    if (false) {
    -        goo = 1;
    -
    -    } else {
    -        var goo = 2;
    -    }
    -    for(var i = 0; i < 100; i++) {
    -        var e = data[i];
    -    }
    -}
    -
    - -

    Powyższy kod zostanie przekształcony przed rozpoczęciem wykonania. JavaScript -przeniesie deklarację zmiennej var oraz deklarację funkcji function na szczyt -najbliższego zasięgu.

    - -
    // deklaracje var zostaną przeniesione tutaj
    -var bar, someValue; // ustawione domyślnie na 'undefined'
    -
    -// deklaracje funkcji zostaną również przeniesione na górę
    -function test(data) {
    -    var goo, i, e; // brak blokowego zasięgu spowoduje przeniesienie tutaj
    -    if (false) {
    -        goo = 1;
    -
    -    } else {
    -        goo = 2;
    -    }
    -    for(i = 0; i < 100; i++) {
    -        e = data[i];
    -    }
    -}
    -
    -bar(); // powoduje błąd TypeError ponieważ bar jest nadal 'undefined'
    -someValue = 42; // przypisania nie zostają zmienione przez 'hoisting'
    -bar = function() {};
    -
    -test();
    -
    - -

    Brak blokowego zasięgu nie tylko przeniesie deklaracje var poza ciało pętle, -ale również spowoduje, że niektóre porównania if staną się nieintuicyjne.

    - -

    W oryginalnym kodzie wewnątrz deklaracja if zdaje się modyfikować zmienną -globalną goo, podczas gdy faktycznie modyfikuje ona zmienną lokalną - po tym -jak zostało zastosowane windowanie (hoisting).

    - -

    Bez wiedzy na temat podnoszenia (hoistingu), poniższy kod może wydawać się -wyrzucać błąd ReferenceError.

    - -
    // sprawdz czy SomeImportantThing zostało zainicjalizowane
    -if (!SomeImportantThing) {
    -    var SomeImportantThing = {};
    -}
    -
    - -

    Oczywiście powyższy kod działa ze względu na fakt, że deklaracja var zostanie -przeniesiona na początek globalnego zasięgu.

    - -
    var SomeImportantThing;
    -
    -// inny kod który może ale nie musi zainicjalizować SomeImportantThing
    -
    -// upewnienie sie że SomeImportantThing zostało zainicjalizowane
    -if (!SomeImportantThing) {
    -    SomeImportantThing = {};
    -}
    -
    - -

    Kolejność rozwiązywania nazw

    - -

    Wszystkie zasięgi w JavaScripcie, włączając globalny zasięg, posiadają -zdefiniowana wewnątrz specjalną nazwę this, która wskazuje -na aktualny obiekt.

    - -

    Zasięg funkcyjny posiada również zdefiniowaną wewnętrznie nazwę -arguments, która zawiera listę argumentów przekazaną do -funkcji.

    - -

    Na przykład, kiedy próbujemy odczytać zmienną foo wewnątrz zasięgu funkcji, -JavaScript będzie szukać nazwy w określonej kolejności: - 1. Jeżeli wewnątrz aktualnego zasięgu znajduje się deklaracja var foo skorzystaj z niej. - 2. Jeżeli jeden z parametrów fukcji został nazwany foo użyj go. - 3. Jeżeli fukcja została nazwana foo skorzystaj z tego. - 4. Przejdz do zewnętrznego zasięgu i przejdz do kroku #1.

    - - - -

    Przestrzenie nazw

    - -

    Powszechnym problemem posiadania tylko jednej globalnej przestrzeni nazw jest -prawdopodobieństwo wystąpienia kolizji nazw. W JavaScripcie, można łatwo uniknąć -tego problemu korzystając z anonimowych wrapperów.

    - -
    (function() {
    -    // autonomiczna "przestrzeń nazw"
    -
    -    window.foo = function() {
    -        // wyeksponowane domkniecie (closure)
    -    };
    -
    -})(); // natychmiastowe wykonanie funkcji
    -
    - -

    Nienazwane funkcje są rozpoznane jako wyrażenia, więc -aby mogły zostać wywołane muszą zostać zewaluowane.

    - -
    ( // zewaluowanie fukcji znajdującej się wewnątrz nawiasów
    -function() {}
    -) // zwrócenie obiektu funkcji
    -() // wywołanie rezultatu ewaluacji
    -
    - -

    Istnieją inne sposoby aby zewaluować i wykonać wyrażenie funkcyjne. Mimo, że -mają inną składnie, zachowują się dokładnie tak samo.

    - -
    // Trzy inne sposoby
    -!function(){}();
    -+function(){}();
    -(function(){}());
    -
    - -

    Wnioski

    - -

    Zaleca się aby zawsze używać anonimowych wrapperów do hermetyzacji kodu wewnątrz -jego własnej przestrzeni nazw. To nie tylko chroni kod przed kolizją nazw, ale -również wprowadza lepszą modularyzację programów.

    - -

    Ponadto, stosowanie zmiennych globalnych jest uznawane za złą praktykę. -Jakiekolwiek wykorzystanie zmiennych globalnych wskazuje na źle napisany kod, -który jest podatny na błędy i trudny do utrzymania.

    Tablice

    Iterowanie po tablicach oraz właściwości tablic

    Mimo, że tablice w JavaScript są obiektami, nie ma dobrych powodów do używania -pętli for in do iteracji po nich. W rzeczywiści istnieje -wiele dobrych powodów przeciwko wykorzystania for in na tablicach.

    - - - -

    Ponieważ pętla for in wylicza wszystkie właściwości, które są wewnątrz -łańcucha prototypów i jedynym sposobem aby wykluczyć te właściwości to użycie -hasOwnProperty, ale wówczas pętla staje się -dwadzieście razy wolniejsza od normalnej pętli for.

    - -

    Iteracja

    - -

    W celu osiągnięcia najlepszej wydajności podczas iteracji po tablicach należy -użyć klasycznej pętli for.

    - -
    var list = [1, 2, 3, 4, 5, ...... 100000000];
    -for(var i = 0, l = list.length; i < l; i++) {
    -    console.log(list[i]);
    -}
    -
    - -

    Jest tam jeszcze jeden dodatkowy haczyk w przykładzie powyżej. Jest to zbuforowanie -długości tablicy poprzez l = list.length.

    - -

    Mimo, że właściwość length jest zdefiniowana w wewnątrz tablicy, istnieje nadal -dodatkowy koszt na wyszukiwanie tej właściwości przy każdej iteracji w pętli. -Chociaż najnowsze silniki JavaScript mogą zastosować optymalizację w tym -przypadku. Nie ma jednak możliwość ustalenia czy kod będzie wykonywany w jednym -z tych nowych silników czy też nie.

    - -

    W rzeczywistości pomijając buforowanie długości tablicy może spowodować, że pętla -będzie tylko w połowie tak szybka jak ta z buforowaniem długości.

    - -

    Właściwość length

    - -

    Mimo, że getter właściwości length po prostu zwraca liczbę elementów, które są -zawarte w tablicy, to setter może być użyta do skracania tablicy.

    - -
    var foo = [1, 2, 3, 4, 5, 6];
    -foo.length = 3;
    -foo; // [1, 2, 3]
    -
    -foo.length = 6;
    -foo; // [1, 2, 3]
    -
    - -

    Przypisanie mniejszej długości spowoduje skrócenie tablicy, ale zwiększenie wartości -length nie ma żadnego wpływu na tablicę.

    - -

    Wnioski

    - -

    Aby uzyskać najlepszą wydajność zaleca się, aby zawsze używać zwykłej pętli for -i zbuforowanie właściwości length. Korzystanie z pętli for in na tablicy jest -znakiem źle napisanego kodu, który jest podatny na błędy i ma słabą wydajność.

    Konstruktor Array

    Zaleca się zawsze korzystać z literału tablicy - notacja [] - podczas tworzenia -nowych tablic, ponieważ konstruktor Array niejednoznacznie interpretuje -parametry do niego przekazane.

    - -
    [1, 2, 3]; // Rezultat: [1, 2, 3]
    -new Array(1, 2, 3); // Rezultat: [1, 2, 3]
    -
    -[3]; // Rezultat: [3]
    -new Array(3); // Rezultat: []
    -new Array('3') // Rezultat: ['3']
    -
    - -

    W przypadku gdy tylko jeden argument zostanie przekazany do kostruktora Array i -ten argument jest typu Number, konstruktor zwróci nową dziwną tablicę -z ustawioną właściwością length na wartość przekazaną jako argument. Należy -zauważyć, że tylko właściwość length zostanie ustawiona w ten sposób, -rzeczywiste indeksy w tej tablicy nie zostaną zainicjalizowane.

    - -
    var arr = new Array(3);
    -arr[1]; // undefined
    -1 in arr; // zwraca false, indeks nie został ustawiony
    -
    - -

    Możliwość ustanienia z góry długości tablicy jest użyteczna tylko w kilku -przypadkach, jak powtarzanie ciągu znaków, w którym unika się stosowania -pętli for.

    - -
    // count - ilosc powtorzen
    -// stringToRepeat - ciąg znaków do powtórzenia 
    -new Array(count + 1).join(stringToRepeat); 
    -
    - -

    Wnioski

    - -

    W miare możliwości należy unikać używania konstruktora Array. Literały są -zdecydowanie lepszym rozwiązaniem, są krótsze i mają bardziej precyzyjną składnię. -Zwiększają również czytelność kodu.

    Typy

    Równość i porównania

    JavaScript posiada dwa różne sposoby równościowego porównywania obiektów.

    - -

    Operator równości

    - -

    Operator równości składa się z dwóch znaków "równa się": ==

    - -

    JavaScript jest słabo typowanym językiem. Oznacza to, że operator równości -konwertuje typy (dokonuje koercji), aby wykonać porównanie.

    - -
    ""           ==   "0"           // false
    -0            ==   ""            // true
    -0            ==   "0"           // true
    -false        ==   "false"       // false
    -false        ==   "0"           // true
    -false        ==   undefined     // false
    -false        ==   null          // false
    -null         ==   undefined     // true
    -" \t\r\n"    ==   0             // true
    -
    - -

    Powyższa tabela przedstawia wyniki koercji typów. Nieprzewidywalne wyniki -porównania są głównym powodem, że stosowanie == jest powszechnie uważane za złą -praktykę. Skomplikowane reguły konwersji są powodem trudnych do wyśledzenia błędy.

    - -

    Ponadto koercja ma również wpływ na wydajność na przykład gdy typ String musi zostać -przekształcony na typ Number przed porównaniem z drugą liczbą.

    - -

    Operator ścisłej równości

    - -

    Operator ścisłej równości składa się z trzech znaków "równa się": ===

    - -

    Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem nie -dokonuje koercji typów przed porównaniem.

    - -
    ""           ===   "0"           // false
    -0            ===   ""            // false
    -0            ===   "0"           // false
    -false        ===   "false"       // false
    -false        ===   "0"           // false
    -false        ===   undefined     // false
    -false        ===   null          // false
    -null         ===   undefined     // false
    -" \t\r\n"    ===   0             // false
    -
    - -

    Powyższe rezultaty są o wiele bardziej przejrzyste. Powoduje to "ustatycznienie" -języka do pewnego stopnia oraz pozwala na wprowadzenie optymalizacji porównań -obiektów o różnych typach.

    - -

    Porównywanie obiektów

    - -

    Mimo, że oba operatory == i === nazywane są operatorami równościowymi, -to zachowują się różnie gdy jednym z operandów jest obiekt typu Object.

    - -
    {} === {};                   // false
    -new String('foo') === 'foo'; // false
    -new Number(10) === 10;       // false
    -var foo = {};
    -foo === foo;                 // true
    -
    - -

    Oba operatory porównują toższmość a nie równość, czyli będą porównywać czy -jeden i drugi operand jest tą samą instancją obiektu, podobnie jak operator -is w Pythonie i porównanie wskaźników w C.

    - -

    Wnioski

    - -

    Zaleca się aby używać tylko operatora ścisłej równości. W sytuacjach gdy -potrzebna jest koercja (porównanie obiektów różnych typów), konwersja powinna -być dokonana jawnie a nie pozostawiona trudnym regułom koercji -obowiązującym w języku.

    Operator typeof

    Operator typeof (razem z operatorem instanceof) jest -prawdopodobnie najwiekszą wadą konstrukcji języka JavaScript, jest on praktycznie -całkowicie wadliwy.

    - -

    Mimo, że instanceof ma swoje wady to nadal ma ograniczone zastosowanie w praktyce, -natomiast typeof ma tylko jeden praktyczny przypadek użycia, który na dodatek -nie jest związany z sprawdzaniem typu obiektu.

    - - - -

    Tablica typów JavaScript

    - -
    Wartość             Klasa      Typ
    --------------------------------------
    -"foo"               String     string
    -new String("foo")   String     object
    -1.2                 Number     number
    -new Number(1.2)     Number     object
    -true                Boolean    boolean
    -new Boolean(true)   Boolean    object
    -new Date()          Date       object
    -new Error()         Error      object
    -[1,2,3]             Array      object
    -new Array(1, 2, 3)  Array      object
    -new Function("")    Function   function
    -/abc/g              RegExp     object (function w Nitro i V8)
    -new RegExp("meow")  RegExp     object (function w Nitro i V8)
    -{}                  Object     object
    -new Object()        Object     object
    -
    - -

    W powyższej tabeli Typ odnosi się do wartości zwracanej przez operator typeof. -Wyraźnie widać, że zwracane wartości w ogóle nie są spójne.

    - -

    Klasa odnosi sie do wartości wewnętrznej właściwości [[Class]] obiektu.

    - - - -

    W celu uzyskania wartości właściwości [[Class]] trzeba skorzystać z metody -toString z Object.prototype.

    - -

    Klasa obiektu

    - -

    Specyfikacja zawiera dokładnie jeden sposób dostepu do wartości [[Class]], -wykorzystując Object.prototype.toString.

    - -
    function is(type, obj) {
    -    var clas = Object.prototype.toString.call(obj).slice(8, -1);
    -    return obj !== undefined && obj !== null && clas === type;
    -}
    -
    -is('String', 'test'); // true
    -is('String', new String('test')); // true
    -
    - -

    Powyższy przykład wywołuje Object.prototype.toString z wartością -this ustawioną na obiekt, dla której wartość właściwości -[[Class]] ma zostać odczytana.

    - - - -

    Testowanie niezdefiniowania zmiennej

    - -
    typeof foo !== 'undefined'
    -
    - -

    Powyższy kod sprawdza czy foo została faktycznie zadeklarowana czy też nie. -Próba odwołania się do zmiennej spowodowała by wyrzucenie błędu ReferenceError. -Jest to jedyne praktyczne wykorzystanie operatora typeof.

    - -

    Wnioski

    - -

    W celu sprawdzenia typu obiektu zalecane jest skorzystanie z -Object.prototype.toString, ponieważ jest to jedyny wiarygodny sposób. Jak -pokazano w powyższej tabeli typów, niektóre wartości zwracane przez typeof nie -są zdefiniowane w specyfikacji, co za tym idzie mogą się różnić w różnych -implementacjach.

    - -

    O ile nie operator typeof nie jest użyty do sprawdzania czy zmienna została -zdefiniowana, powinien być unikany o ile to tylko możliwe.

    Operator instanceof

    Operator instanceof porównuje konstruktory obiektów przekazanych jako operendy. -Jest on jedynie użyteczny do porównywania obiektów utworzonych klas. Stosowanie -go na wbudowanych typach jest praktycznie tak samo bezużyteczne jak operatora -typeof.

    - -

    Porównywanie obiektów utworzonych klas

    - -
    function Foo() {}
    -function Bar() {}
    -Bar.prototype = new Foo();
    -
    -new Bar() instanceof Bar; // true
    -new Bar() instanceof Foo; // true
    -
    -// Poniżej kod który przypisuje do Bar.prototype obiekt funkcji Foo
    -// a nie faktyczną instancję Foo
    -Bar.prototype = Foo;
    -new Bar() instanceof Foo; // false
    -
    - -

    Stosowanie instanceof na natywnych typach

    - -
    new String('foo') instanceof String; // true
    -new String('foo') instanceof Object; // true
    -
    -'foo' instanceof String; // false
    -'foo' instanceof Object; // false
    -
    - -

    Jedną ważną rzeczą, którą należy zauważyć jest to, że instanceof nie zadziała -na obiektach, które pochodzą z różnych kontekstów JavaScript (np. z różnych -dokumentów wewnątrz przeglądarki), ponieważ ich konstruktory nie będą tymi -samymi obiektami.

    - -

    Wnioski

    - -

    Operator instanceof powinien być tylko używany podczas korzystania z obiektów -klas utworzonych, które były zdefiniowane w tym samym kontekscie JavaScriptowym. -Podobnie jak operator typeof, należy unikać korzystania -z tego operatora w innych sytuacjach.

    Rzutowanie typów

    JavaScript jest językiem słabo typowanym, co za tym idzie będzie stosować koercję -typów gdziekolwiek jest to możliwe.

    - -
    // These are true
    -new Number(10) == 10; // Number.toString() zostanie przekształcone
    -                      // z powrotem do liczby
    -
    -10 == '10';           // Stringi zostaną przekształcone do typu Number
    -10 == '+10 ';         // Kolejne wariacje
    -10 == '010';          // i następne
    -isNaN(null) == false; // null zostanie przekształcony do 0
    -                      // który oczywiście nie jest NaN
    -
    -// Poniższe zwracają false
    -10 == 010;
    -10 == '-10';
    -
    - - - -

    Aby uniknąć powyższych problemów, należy koniecznie skorzystać ze -ściełego operatora równości. Mimo, że pozwala to uniknąć wiele -typowych problemów to nadal istnieje wiele innych, które powstają na bazie słabego -typowania języka JavaScript.

    - -

    Konstruktory typów wbudowanych

    - -

    Konstruktory typów wbudowanych takich, jak Number lub String zachowują się -inaczej jeżeli są poprzedzone słowem kluczowym new a inaczej jeżeli nie są.

    - -
    new Number(10) === 10;     // False, Object i Number
    -Number(10) === 10;         // True, Number i Number
    -new Number(10) + 0 === 10; // True, ponieważ dokonano jawnej konwersji
    -
    - -

    Korzystanie z wbudowanych typów jak Number jako konstruktor utworzy nowy obiekt -typu Number, natomiast opuszczenie słowa kluczowego new spowoduje, że funkcja -Number zachowa się jak konwerter.

    - -

    Ponadto, użycie literałów lub wartości nieobiektowych zaowocuje jeszcze większą -ilością rzutowań (koercją) typów.

    - -

    Najlepszym rozwiązaniem jest jawne rzutowanie do jednego z trzech typów.

    - -

    Rzutowanie do typu String

    - -
    '' + 10 === '10'; // true
    -
    - -

    Konkatenacja pustego stringu i wartości powoduje rzutowanie do typu String.

    - -

    Rzutowanie do typu Number

    - -
    +'10' === 10; // true
    -
    - -

    Zastosowanie unarnego operatora + spowoduje rzutowanie do typu Number.

    - -

    Rzutowanie do typu Boolean

    - -

    Używając dwukrotnie operatora negacji dowolna wartość może zostać zrzutowana -do typu Boolean

    - -
    !!'foo';   // true
    -!!'';      // false
    -!!'0';     // true
    -!!'1';     // true
    -!!'-1'     // true
    -!!{};      // true
    -!!true;    // true
    -

    Jądro

    Dlaczego nie należy używać eval

    Funkcja eval uruchomi podany string jako kod JavaScript w lokalnym zasięgu (scopie).

    - -
    var foo = 1;
    -function test() {
    -    var foo = 2;
    -    eval('foo = 3');
    -    return foo;
    -}
    -test(); // 3
    -foo; // 1
    -
    - -

    Niestaty eval zostanie wykonana w lokalnym zasięgu tylko jeżeli została wywołana -bezpośrednio i nazwa wołanej funkcji równa sie eval.

    - -
    var foo = 1;
    -function test() {
    -    var foo = 2;
    -    var bar = eval;
    -    bar('foo = 3');
    -    return foo;
    -}
    -test(); // 2
    -foo; // 3
    -
    - -

    Należy unikać stosowania eval o ile to tylko możliwe. W 99.9% przypadków można -osiągnąć ten sam efekt nie używając eval.

    - -

    eval w przebraniu

    - -

    Funkcje wykonywane po upływie czasu setTimeout i setInterval -mogą przyjąć string jako pierwszy argument. String ten zostanie zawsze wykonany -w globalnym zasięgu, ponieważ funkcja eval zostanie wywołana niebezpośrednio w tym -przypadku.

    - -

    Problemy z bezpieczeństwem

    - -

    Funkcja eval jest również problematyczna od strony bezpieczeństwa, ponieważ -wykonuje każdy kod, który zostanie do niej przekazany i nie należy nigdy -używać jej na stringach nieznanego lub niezaufanego pochodzenia.

    - -

    Wnioski

    - -

    Funkcja eval nie powinna być w ogole używana, każdy kod, który ją wykorzystuje -powinien zostać sprawdzony pod względem działania, wydajności i bezpieczeństwa. -W przypadku gdy użycie eval jest niezbędne do działania, wówczas taki kod -należy przemyśleć raz jeszcze i ulepszyć kod aby nie wymagał użycia eval.

    undefined i null

    JavaScript ma dwie różne wartości dla pustych wartości, bardziej użyteczną -z tych dwóch jest undefined.

    - -

    Wartość undefined

    - -

    undefined jest typem z dokładnie jedną wartością: undefined.

    - -

    Język również definiuje globalną zmienną, która ma wartość undefined, zmienna -ta jest nazwana undefined. Jednakże jest to zmienna a nie stała czy słowo -kluczowe w języku. Oznacza to że możliwe jest nadpisanie wartości tej zmiennej.

    - - - -

    Kilka przykładów kiedy wartość undefined jest zwracana:

    - -
      -
    • Dostęp do (niemodyfikowalnej) zmiennej globalnej undefined.
    • -
    • Wyjście z funkcji, która nie ma deklaracji return.
    • -
    • Deklaracja return, która nic jawnie nie zwraca.
    • -
    • Poszukiwanie nieistniejącej właściwości.
    • -
    • Parametr funkcji, który nie został jawnie przekazany podczas wywołania funkcji
    • -
    • Wszystko co zostało ustawione na wartość undefined
    • -
    - -

    Obsługa przypadku zmiany wartości undefined

    - -

    Ponieważ globalna zmienna undeined tylko zawiera kopię prawdziwej wartości typu -undefined, przypisanie nowej wartości do tej zmiennej nie zmienia wartości -typu undefined.

    - -

    Jednak, aby porównać coś do wartości undefined potrzebne jest odczytanie wartości -undefined.

    - -

    Aby uchronić swój kod przeciwko możliwemu nadpisaniu zmiennej undefined, korzysta -się z powszechnej techniki dodania dodatkowego parametru do -anonimowego wrappera, do którego nie zostanie przekazany -argument.

    - -
    var undefined = 123;
    -(function(something, foo, undefined) {
    -    // undefined lokalnym zasięgu znowu 
    -    // odnosi się do poprawnej wartości
    -
    -})('Hello World', 42);
    -
    - -

    Kolejnym sposobem aby osiągnąć ten sam efekt jest użycie deklaracji zmiennej -wewnątrz wrappera.

    - -
    var undefined = 123;
    -(function(something, foo) {
    -    var undefined;
    -    ...
    -
    -})('Hello World', 42);
    -
    - -

    Jedyną różnicą pomięcy tymi sposobami są dodatkowe 4 bajty przeznaczone na słowo -kluczowe var i spację po nim.

    - -

    Zastosowanie null

    - -

    Podczas gdy undefined w kontekście języka jest używany jak null w sensie -tradycyjnych języków, to null w JavaScript (jako literał i jako typ) jest po -prostu kolejnym typem danych.

    - -

    Jest wykorzystywany we wnętrzu JavaScript (np. deklaracji końca łańcucha prototypów -poprzez ustawienie Foo.prototype = null), ale prawie w każdym przypadku można go -zastąpić przez undefined.

    Automatyczne wstawianie średnika

    Mimo, że JavaScript ma składnię podobną do języka C, to nie wymusza stosowania -średników w kodzie źródłowym. Istnieje możliwość ich pominięcia.

    - -

    Lecz JavaScript nie jest językiem bez średników, tak na prawdę potrzebuje -średników aby zinterpretować kod źródłowy. Jednakże parser JavaScript -automatycznie wstawia średniki o ile napotka błąd parsowania związany z -brakiem średnika.

    - -
    var foo = function() {
    -} // błąd parsowania, oczekiwany był w tym miejscu średnik
    -test()
    -
    - -

    Parser dodaje średnik, i próbuje jeszcze raz sparsować skrypt.

    - -
    var foo = function() {
    -}; // bez błędu parser kontynuuje
    -test()
    -
    - -

    Automatyczne wstawianie średników jest uważane za jeden z największych błędów -konstrukcji języka, ponieważ może ono zachowanie kodu.

    - -

    Jak działa wstawianie

    - -

    Kod poniżej nie ma żadnych średników, więc parser zdecyduje, w których miejscach -je wstawi.

    - -
    (function(window, undefined) {
    -    function test(options) {
    -        log('testing!')
    -
    -        (options.list || []).forEach(function(i) {
    -
    -        })
    -
    -        options.value.test(
    -            'long string to pass here',
    -            'and another long string to pass'
    -        )
    -
    -        return
    -        {
    -            foo: function() {}
    -        }
    -    }
    -    window.test = test
    -
    -})(window)
    -
    -(function(window) {
    -    window.someLibrary = {}
    -
    -})(window)
    -
    - -

    Poniżej znajduje się rezultat "zgadywania" parsera.

    - -
    (function(window, undefined) {
    -    function test(options) {
    -
    -        // Nie wstaniony średnik, linie zostały połączone
    -        log('testing!')(options.list || []).forEach(function(i) {
    -
    -        }); // <- wstawiony
    -
    -        options.value.test(
    -            'long string to pass here',
    -            'and another long string to pass'
    -        ); // <- wstawiony
    -
    -        return; // <- wstawiony, psując deklarację return
    -        { // potraktowane jako definicja bloku
    -
    -            // etykieta oraz pojedyncze wyrażenie
    -            foo: function() {} 
    -        }; // <- wstawiony
    -    }
    -    window.test = test; // <- wstawiony
    -
    -// Kolejna połączona linia
    -})(window)(function(window) {
    -    window.someLibrary = {}; // <- wstawiony
    -
    -})(window); //<- wstawiony
    -
    - - - -

    Parser drastycznie zmienił działanie powyższego kodu, w niektórych przypadkach -zmienił go źle.

    - -

    Nawiasy

    - -

    W przypadku, gdy w następnej linii znajduje się nawias, parser nie wstawi -średnika.

    - -
    log('testing!')
    -(options.list || []).forEach(function(i) {})
    -
    - -

    Ten kod zostanie zmieniony w poniższą jedną linię.

    - -
    log('testing!')(options.list || []).forEach(function(i) {})
    -
    - -

    Jest bardzo prawdopodobne, że log nie zwróci fukcji, co za tym idzie -powyższy kod wyrzuci błąd TypeError oznajmując, że undefined is not a -function - undefined nie jest funkcją.

    - -

    Wnioski

    - -

    Zaleca się aby nigdy nie pomijać średników, pozostawiać nawias otwierający -w tej samej linii co odpowiadająca mu definicja i nigdy nie pozostawiać deklaracji -if / else bez nawiasów nawet jeżeli są jednolinijkowe. Wszystkie te uwagi nie -tylko pomagają poprawić spójność kodu, ale również zapobiegają parser JavaScript -przed zmianą działania kod.

    Inne

    setTimeout i setInterval

    Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania -funkcji korzystając z funkcji setTimeout i setInterval. -Since JavaScript is asynchronous, it is possible to schedule the execution of a -function by using the setTimeout and setInterval functions.

    - - - -
    function foo() {}
    -var id = setTimeout(foo, 1000); // zwraca licznę typu Number > 0
    -
    - -

    Powyższe wywołanie setTimeout zwraca ID budzika i planuje wywołanie foo za -około tysiąc milisekund. foo zostanie wykonana dokładnie jeden raz.

    - -

    Nie ma pewności, że kod zaplanowany do wykonania wykona się dokładnie po -upłynięciu zadanego czasu podanego jako parametr do setTimeout, ponieważ zależy -to od dokładności zegara w silniku JavaScript, który wykonuje kod oraz od tego, -że inny kawałek kodu może zablokować wątek, ponieważ JavaScript jest tylko -jedno wątkowy.

    - -

    Funkcja, która została przekazana jako pierwszy parametr zostanie wykonana w -globalnym zasięgu, co oznacza, że this wewnątrz tej funkcji -będzie wkazywać na obiekt global.

    - -
    function Foo() {
    -    this.value = 42;
    -    this.method = function() {
    -        // this wskazuje na obiekt global
    -        console.log(this.value); // wypisze undefined
    -    };
    -    setTimeout(this.method, 500);
    -}
    -new Foo();
    -
    - - - -

    Kolejkowanie wywołań z setInterval

    - -

    Podczas, gdy setTimeout wywołuje podaną funkcję tylko raz, setInterval - -jak wskazuje nazwa - będzie wykonywać funkcję w odstępach czasowych co X -milisekund. Jednakże korzystanie z tej funkcji jest odradzane.

    - -

    Kiedy wykonywany kod zablokuje możliwość uruchomienia zaplanowanej funkcji, -setInterval będzie próbować uruchamiać daną funkcję co będzie powodować -kolejkowanie wykonania tej samej funkcji kilkakrotnie. W szczególności może się -to wydarzyć przy krótkim interwale.

    - -
    function foo(){
    -    // coś co blokuje wykonanie na 1 sekundę 
    -}
    -setInterval(foo, 100);
    -
    - -

    W powyższym kodzie kod foo zostanie wywołany tylko raz i zablokuje wywołanie na -jedną sekundę.

    - -

    Podczas, gdy funkcja foo blokuje wykonanie setInterval będzie planować kolejne -wywołania foo. W momencie, gdy pierwsze wywołanie foo się zakończy, już -w kolejce do wywołania będą czekały kolejne dziesięć wywołań tej funkcji.

    - -

    Radzenie sobie z możliwymi blokadami

    - -

    Najprostrzym jak również najbardziej kontrolowaną sytuacją jest użycie setTimeout -wewnątrz wywoływanej funkcji.

    - -
    function foo(){
    -    // coś co blokuje wykonanie na 1 sekundę
    -    setTimeout(foo, 100);
    -}
    -foo();
    -
    - -

    Powyższy kod nie tylko hermetyzuje wywołanie setTimeout ale również zapobiega -kolejkowaniu wywołań fukcji i daje dodatkową kontrolę. W tym przypadku funkcja -foo może zdecydować czy powinna się wywołać ponownie czy też nie.

    - -

    Ręczne usuwanie budzików

    - -

    Usuwanie budzików i interwałów dokonywane jest przez przekazanie odpowiedniego ID -do clearTimeout lub clearInterval, w zależności z jakiej funkcji zostało -zwrócone ID.

    - -
    var id = setTimeout(foo, 1000);
    -clearTimeout(id);
    -
    - -

    Usuwanie wszystkich budzików

    - -

    Ponieważ nie istnieje wbudowana metoda na usunięcie wszystkich budzików i/lub -interwałów, konieczne jest użycie metody brute force aby osiągnąć ten efekt.

    - -
    // usunięcie "wszystkich" budzików 
    -for(var i = 1; i < 1000; i++) {
    -    clearTimeout(i);
    -}
    -
    - -

    Nadal może istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała, -ponieważ ID było z innego przedziału, dlatego zamiast korzystania z metody brute -force, zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć.

    - -

    Ukryte wykorzystanie eval

    - -

    Do setTimeout i setInterval można również przekazać string jako pierwszy -parametr zamiast obiektu funkcji, jednakże nigdy nie należy korzystać z tej -możliwości, ponieważ wewnętrznie setTimeout i setInterval wykorzystują eval.

    - - - -
    function foo() {
    -    // zostanie wykonane 
    -}
    -
    -function bar() {
    -    function foo() {
    -        // nigdy nie zostanie wywołane
    -    }
    -    setTimeout('foo()', 1000);
    -}
    -bar();
    -
    - -

    Ponieważ eval nie zostało wywołane wprost w tym przypadku, to -string przekazany do setTimeout zostanie uruchomiony w zasięgu globalnym, -co za tym idzie lokalna zmienna foo z zasięgu bar nie zostanie użyta.

    - -

    Kolejnym zaleceniem jest aby nie stosować stringów do przekazywania argumentów -do funkcji, która ma zostać wywołana przez budzik.

    - -
    function foo(a, b, c) {}
    -
    -// NIGDY nie należy tak robić 
    -setTimeout('foo(1,2, 3)', 1000)
    -
    -// Zamiast tego należy skorzystać z anonimowej funkcji
    -setTimeout(function() {
    -    foo(a, b, c);
    -}, 1000)
    -
    - - - -

    Wnioski

    - -

    Nie należy nigdy przekazywać stringu jako parametru do setTimeout lub -setInterval. Jest to wyraźną oznaką bardzo złego kodu, jeżeli potrzebne jest -przekazanie argumentów do funkcji należy skorzystać z anonimowej funkcji i -wewnątrz niej dokonać przekazania argumentów.

    - -

    Ponadto, należy unikać korzystanie z setInterval, ponieważ planista może -zablokować wykonanie JavaScriptu.

    \ No newline at end of file From 3e3e31da9e7937e85e61d54c16c17001d5297a62 Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 00:42:46 +0200 Subject: [PATCH 199/641] Corrections in Polish translation (sections Array and Core). --- doc/pl/array/constructor.md | 14 +++++++------- doc/pl/array/general.md | 28 ++++++++++++++-------------- doc/pl/core/eval.md | 19 +++++++++---------- doc/pl/core/semicolon.md | 22 +++++++++++----------- doc/pl/core/undefined.md | 31 +++++++++++++++---------------- 5 files changed, 56 insertions(+), 58 deletions(-) diff --git a/doc/pl/array/constructor.md b/doc/pl/array/constructor.md index 79772f43..7df61c1d 100644 --- a/doc/pl/array/constructor.md +++ b/doc/pl/array/constructor.md @@ -2,7 +2,7 @@ Zaleca się zawsze korzystać z literału tablicy - notacja `[]` - podczas tworzenia nowych tablic, ponieważ konstruktor `Array` niejednoznacznie interpretuje -parametry do niego przekazane. +przekazane do niego parametry. [1, 2, 3]; // Rezultat: [1, 2, 3] new Array(1, 2, 3); // Rezultat: [1, 2, 3] @@ -14,15 +14,15 @@ parametry do niego przekazane. W przypadku gdy tylko jeden argument zostanie przekazany do kostruktora `Array` i ten argument jest typu `Number`, konstruktor zwróci nową *dziwną* tablicę z ustawioną właściwością `length` na wartość przekazaną jako argument. Należy -zauważyć, że **tylko** właściwość `length` zostanie ustawiona w ten sposób, -rzeczywiste indeksy w tej tablicy nie zostaną zainicjalizowane. +zauważyć, że **tylko** właściwość `length` zostanie ustawiona w ten sposób. +Rzeczywiste indeksy w tej tablicy nie zostaną zainicjalizowane. var arr = new Array(3); arr[1]; // undefined 1 in arr; // zwraca false, indeks nie został ustawiony -Możliwość ustanienia z góry długości tablicy jest użyteczna tylko w kilku -przypadkach, jak powtarzanie ciągu znaków, w którym unika się stosowania +Możliwość ustalenia z góry długości tablicy jest użyteczna tylko w kilku +przypadkach, jak np. powtarzanie ciągu znaków, w którym unika się stosowania pętli `for`. // count - ilosc powtorzen @@ -31,7 +31,7 @@ pętli `for`. ### Wnioski -W miare możliwości należy unikać używania konstruktora `Array`. Literały są -zdecydowanie lepszym rozwiązaniem, są krótsze i mają bardziej precyzyjną składnię. +W miarę możliwości należy unikać używania konstruktora `Array`. Literały są +zdecydowanie lepszym rozwiązaniem. Są krótsze i mają bardziej precyzyjną składnię. Zwiększają również czytelność kodu. diff --git a/doc/pl/array/general.md b/doc/pl/array/general.md index c30056d7..6bd89bae 100644 --- a/doc/pl/array/general.md +++ b/doc/pl/array/general.md @@ -1,15 +1,15 @@ -## Iterowanie po tablicach oraz właściwości tablic +## Iterowanie po tablicach oraz właściwościach tablic -Mimo, że tablice w JavaScript są obiektami, nie ma dobrych powodów do używania -[`pętli for in`](#object.forinloop) do iteracji po nich. W rzeczywiści istnieje -wiele dobrych powodów **przeciwko** wykorzystania `for in` na tablicach. +Mimo że tablice w JavaScript są obiektami, nie ma dobrych powodów aby używać +[`pętli for in`](#object.forinloop) do iteracji po nich. W rzeczywstości istnieje +wiele dobrych powodów **przeciwko** wykorzystaniu `for in` na tablicach. > **Uwaga:** Tablice JavaScriptowe **nie** są *tablicami asocjacyjnymi*. JavaScript > posiada tylko [obiekty](#object.general) do mapowania kluczy do wartości. Jednakże > tablice asocjacyjne **zachowują** porządek, natomiast obiekty **nie zachowują**. Ponieważ pętla `for in` wylicza wszystkie właściwości, które są wewnątrz -łańcucha prototypów i jedynym sposobem aby wykluczyć te właściwości to użycie +łańcucha prototypów i jedynym sposobem aby wykluczyć te właściwości jest użycie [`hasOwnProperty`](#object.hasownproperty), ale wówczas pętla staje się **dwadzieście razy** wolniejsza od normalnej pętli `for`. @@ -23,22 +23,22 @@ użyć klasycznej pętli `for`. console.log(list[i]); } -Jest tam jeszcze jeden dodatkowy haczyk w przykładzie powyżej. Jest to zbuforowanie +W powyższym przykładzie jest jeszcze jeden dodatkowy haczyk. Jest to zbuforowanie długości tablicy poprzez `l = list.length`. -Mimo, że właściwość `length` jest zdefiniowana w wewnątrz tablicy, istnieje nadal +Mimo że właściwość `length` jest zdefiniowana wewnątrz tablicy, istnieje nadal dodatkowy koszt na wyszukiwanie tej właściwości przy każdej iteracji w pętli. -Chociaż najnowsze silniki JavaScript **mogą** zastosować optymalizację w tym -przypadku. Nie ma jednak możliwość ustalenia czy kod będzie wykonywany w jednym -z tych nowych silników czy też nie. +Chociaż najnowsze silniki JavaScript **mogą** zastosować w tym +przypadku optymalizację. Nie ma jednak możliwość ustalenia czy kod będzie wykonywany w jednym +z tych nowych silników, czy też nie. -W rzeczywistości pomijając buforowanie długości tablicy może spowodować, że pętla +W rzeczywistości pominięcie buforowania długości tablicy może spowodować, że pętla będzie tylko **w połowie tak szybka** jak ta z buforowaniem długości. ### Właściwość `length` -Mimo, że *getter* właściwości `length` po prostu zwraca liczbę elementów, które są -zawarte w tablicy, to *setter* może być użyta do **skracania** tablicy. +Mimo, że *getter* właściwości `length` zwraca po prostu liczbę elementów, które są +zawarte w tablicy, to *setter* może być użyty do **skracania** tablicy. var foo = [1, 2, 3, 4, 5, 6]; foo.length = 3; @@ -54,5 +54,5 @@ Przypisanie mniejszej długości spowoduje skrócenie tablicy, ale zwiększenie Aby uzyskać najlepszą wydajność zaleca się, aby zawsze używać zwykłej pętli `for` i zbuforowanie właściwości `length`. Korzystanie z pętli `for in` na tablicy jest -znakiem źle napisanego kodu, który jest podatny na błędy i ma słabą wydajność. +oznaką źle napisanego kodu, który jest podatny na błędy i ma słabą wydajność. diff --git a/doc/pl/core/eval.md b/doc/pl/core/eval.md index 6b66b37c..1da5c812 100644 --- a/doc/pl/core/eval.md +++ b/doc/pl/core/eval.md @@ -1,4 +1,4 @@ -## Dlaczego nie należy używać `eval` +## Dlaczego nie należy używać `eval`? Funkcja `eval` uruchomi podany string jako kod JavaScript w lokalnym zasięgu (scopie). @@ -11,8 +11,8 @@ Funkcja `eval` uruchomi podany string jako kod JavaScript w lokalnym zasięgu (s test(); // 3 foo; // 1 -Niestaty `eval` zostanie wykonana w lokalnym zasięgu tylko jeżeli została wywołana -**bezpośrednio** *i* nazwa wołanej funkcji równa sie `eval`. +Niestaty, `eval` zostanie wykonana w lokalnym zasięgu tylko wtedy, gdy zostanie wywołana +**bezpośrednio** *i* nazwa wywoływanej funkcji równa sie `eval`. var foo = 1; function test() { @@ -30,20 +30,19 @@ osiągnąć ten sam efekt **nie** używając `eval`. ### `eval` w przebraniu [Funkcje wykonywane po upływie czasu](#other.timeouts) `setTimeout` i `setInterval` -mogą przyjąć string jako pierwszy argument. String ten zostanie **zawsze** wykonany -w globalnym zasięgu, ponieważ funkcja `eval` zostanie wywołana niebezpośrednio w tym -przypadku. +mogą przyjąć string jako pierwszy argument. String ten **zawsze** będzie wykonywany +w globalnym zasięgu, ponieważ funkcja `eval` jest w tym wypadku wywoływana pośrednio. ### Problemy z bezpieczeństwem Funkcja `eval` jest również problematyczna od strony bezpieczeństwa, ponieważ -wykonuje **każdy** kod, który zostanie do niej przekazany i nie należy **nigdy** -używać jej na stringach nieznanego lub niezaufanego pochodzenia. +wykonuje **każdy** kod, który zostanie do niej przekazany i **nigdy** nie należy +jej używać na stringach nieznanego lub niezaufanego pochodzenia. ### Wnioski -Funkcja `eval` nie powinna być w ogole używana, każdy kod, który ją wykorzystuje +Funkcja `eval` nie powinna być w ogóle używana. Każdy kod, który jej używa powinien zostać sprawdzony pod względem działania, wydajności i bezpieczeństwa. W przypadku gdy użycie `eval` jest niezbędne do działania, wówczas taki kod -należy przemyśleć raz jeszcze i *ulepszyć* kod aby nie wymagał użycia `eval`. +należy ponownie przemyśleć i *ulepszyć* aby nie wymagał użycia `eval`. diff --git a/doc/pl/core/semicolon.md b/doc/pl/core/semicolon.md index 6dcaf9cb..7f1410cf 100644 --- a/doc/pl/core/semicolon.md +++ b/doc/pl/core/semicolon.md @@ -1,9 +1,9 @@ ## Automatyczne wstawianie średnika -Mimo, że JavaScript ma składnię podobną do języka C, to **nie** wymusza stosowania +Mimo że JavaScript ma składnię podobną do języka C, to **nie** wymusza stosowania średników w kodzie źródłowym. Istnieje możliwość ich pominięcia. -Lecz JavaScript nie jest językiem bez średników, tak na prawdę potrzebuje +JavaScript nie jest językiem bez średników, tak na prawdę potrzebuje średników aby zinterpretować kod źródłowy. Jednakże parser JavaScript **automatycznie** wstawia średniki o ile napotka błąd parsowania związany z brakiem średnika. @@ -19,7 +19,7 @@ Parser dodaje średnik, i próbuje jeszcze raz sparsować skrypt. test() Automatyczne wstawianie średników jest uważane za jeden z **największych** błędów -konstrukcji języka, ponieważ *może* ono zachowanie kodu. +konstrukcji języka, ponieważ *może* ono zmienić zachowanie kodu. ### Jak działa wstawianie @@ -84,11 +84,11 @@ Poniżej znajduje się rezultat "zgadywania" parsera. })(window); //<- wstawiony > **Uwaga:** Parser JavaScript nie potrafił "odpowiednio" zinterpretować -> deklaracji return, po którje został dodany znak nowej linii. Mimo, że +> deklaracji return, po której został dodany znak nowej linii. Mimo że > niekoniecznie jest to błąd automatycznego wstawiania średników, to może to > jednak powodować niechciane efekty uboczne -Parser drastycznie zmienił działanie powyższego kodu, w niektórych przypadkach +Parser drastycznie zmienił działanie powyższego kodu. W niektórych przypadkach **zmienił go źle**. ### Nawiasy @@ -99,19 +99,19 @@ W przypadku, gdy w następnej linii znajduje się nawias, parser **nie** wstawi log('testing!') (options.list || []).forEach(function(i) {}) -Ten kod zostanie zmieniony w poniższą jedną linię. +Kod ten zostanie zmieniony w poniższą linię. log('testing!')(options.list || []).forEach(function(i) {}) -Jest **bardzo** prawdopodobne, że `log` **nie** zwróci fukcji, co za tym idzie +Jest **bardzo** prawdopodobne, że `log` **nie** zwróci fukcji. Co za tym idzie powyższy kod wyrzuci błąd `TypeError` oznajmując, że `undefined is not a function` - `undefined` nie jest funkcją. ### Wnioski -Zaleca się aby **nigdy** nie pomijać średników, pozostawiać nawias otwierający +Zaleca się, aby **nigdy** nie pomijać średników, pozostawiać nawias otwierający w tej samej linii co odpowiadająca mu definicja i nigdy nie pozostawiać deklaracji -`if` / `else` bez nawiasów nawet jeżeli są jednolinijkowe. Wszystkie te uwagi nie -tylko pomagają poprawić spójność kodu, ale również zapobiegają parser JavaScript -przed zmianą działania kod. +`if` / `else` bez nawiasów - nawet, jeżeli są jednolinijkowe. Wszystkie te uwagi nie +tylko pomagają poprawić spójność kodu, ale też zapobiegają zmianie działania +kodu przez parser JavaScript. diff --git a/doc/pl/core/undefined.md b/doc/pl/core/undefined.md index b938ead3..7e1d056e 100644 --- a/doc/pl/core/undefined.md +++ b/doc/pl/core/undefined.md @@ -8,44 +8,43 @@ z tych dwóch jest `undefined`. `undefined` jest typem z dokładnie jedną wartością: `undefined`. Język również definiuje globalną zmienną, która ma wartość `undefined`, zmienna -ta jest nazwana `undefined`. Jednakże jest to zmienna a **nie** stała czy słowo -kluczowe w języku. Oznacza to że możliwe jest nadpisanie *wartości* tej zmiennej. +ta jest nazwana `undefined`. Jednakże jest to zmienna a **nie** stała, czy słowo +kluczowe. Oznacza to, że możliwe jest nadpisanie *wartości* tej zmiennej. > Uwaga ES55: `undefined` w ECMAScript 5 **nie będzie już** *nadpisywalna* w trybie -> strict mode, ale jej nazwa może zostać przysłonona przez na przykład funkcję o +> strict mode, ale jej nazwa może zostać przesłoniona przez na przykład funkcję o > nazwie `undefined`. Kilka przykładów kiedy wartość `undefined` jest zwracana: - - Dostęp do (niemodyfikowalnej) zmiennej globalnej `undefined`. - - Wyjście z funkcji, która nie ma deklaracji `return`. - - Deklaracja `return`, która nic jawnie nie zwraca. - - Poszukiwanie nieistniejącej właściwości. - - Parametr funkcji, który nie został jawnie przekazany podczas wywołania funkcji - - Wszystko co zostało ustawione na wartość `undefined` + - dostęp do (niemodyfikowalnej) zmiennej globalnej `undefined`, + - wyjście z funkcji, która nie ma deklaracji `return`, + - deklaracja `return`, która nic jawnie nie zwraca, + - poszukiwanie nieistniejącej właściwości, + - parametr funkcji, który nie został jawnie przekazany podczas wywołania funkcji, + - wszystko czemu została przypisana wartość `undefined`. ### Obsługa przypadku zmiany wartości `undefined` -Ponieważ globalna zmienna `undeined` tylko zawiera kopię prawdziwej *wartości* typu +Ponieważ globalna zmienna `undefined` zawiera tylko kopię prawdziwej *wartości* typu `undefined`, przypisanie nowej wartości do tej zmiennej **nie** zmienia wartości *typu* `undefined`. -Jednak, aby porównać coś do wartości `undefined` potrzebne jest odczytanie wartości -`undefined`. +Jednak aby porównać coś z wartością `undefined`, trzeba odczytać wartość `undefined`. -Aby uchronić swój kod przeciwko możliwemu nadpisaniu zmiennej `undefined`, korzysta +Aby uchronić swój kod przed możliwym nadpisaniem zmiennej `undefined`, korzysta się z powszechnej techniki dodania dodatkowego parametru do [anonimowego wrappera](#function.scopes), do którego nie zostanie przekazany argument. var undefined = 123; (function(something, foo, undefined) { - // undefined lokalnym zasięgu znowu + // undefined o lokalnym zasięgu znowu // odnosi się do poprawnej wartości })('Hello World', 42); -Kolejnym sposobem aby osiągnąć ten sam efekt jest użycie deklaracji zmiennej +Kolejnym sposobem na osiągnięcie tego samego efektu jest użycie deklaracji zmiennej wewnątrz wrappera. var undefined = 123; @@ -55,7 +54,7 @@ wewnątrz wrappera. })('Hello World', 42); -Jedyną różnicą pomięcy tymi sposobami są dodatkowe 4 bajty przeznaczone na słowo +Jedyną różnicą pomiędzy tymi sposobami są dodatkowe 4 bajty przeznaczone na słowo kluczowe `var` i spację po nim. ### Zastosowanie `null` From 51baba21de57c1661d7ab8d8679aeb803327b946 Mon Sep 17 00:00:00 2001 From: HIRAKI Satoru Date: Mon, 4 Jul 2011 08:13:43 +0900 Subject: [PATCH 200/641] fix representation --- doc/ja/intro/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ja/intro/index.md b/doc/ja/intro/index.md index b2fcfb3c..0f7ad2c4 100644 --- a/doc/ja/intro/index.md +++ b/doc/ja/intro/index.md @@ -1,13 +1,13 @@ ## 前書き -**JavaScript Garden** はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 -このドキュメントはJavaScriptという言語に対して不慣れなプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。 +**JavaScript Garden** はJavaScriptというプログラム言語の一番奇妙な部分についてのドキュメント集です。 +このドキュメントはJavaScriptという言語に慣れていないプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。 JavaScript GardenはJavaScriptを教える事を**目的にしていません**。このガイドの項目を理解する為には、この言語に対する前提知識がある事を推奨します。この言語の基礎部分についてはMozilla Developer Networkの[ガイド][1] がオススメです。 ## 著者 -このガイドは2人の愛すべき[Stack Overflow][2]ユーザーである[Ivo Wetzel][3] +このガイドは愛すべき[Stack Overflow][2]の2人のユーザー[Ivo Wetzel][3] (執筆)と[Zhang Yi Jiang][4] (デザイン)によって作られました。 ## 貢献者 From 0cc2e8d1e0c88a0389d89b55bb77ae46dc7c2533 Mon Sep 17 00:00:00 2001 From: HIRAKI Satoru Date: Mon, 4 Jul 2011 08:18:04 +0900 Subject: [PATCH 201/641] fix representation of index.json --- doc/ja/index.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ja/index.json b/doc/ja/index.json index cea3fb99..70be20fc 100644 --- a/doc/ja/index.json +++ b/doc/ja/index.json @@ -1,7 +1,7 @@ { "title": "JavaScript Garden", "langTitle": "JavaScript Garden in Japanese", - "description": "JavaScriptの奇抜さと欠陥についてのガイドライン", + "description": "JavaScriptの奇妙さと欠陥についてのガイドライン", "sections": [ { "title": "前書き", From bd4a74bf8132c6cc420f754ab8142737ee058236 Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 16:15:32 +0200 Subject: [PATCH 202/641] Corrections in Polish translation (Function and Intro sections). --- doc/pl/core/undefined.md | 4 +-- doc/pl/function/arguments.md | 27 ++++++++-------- doc/pl/function/closures.md | 22 ++++++------- doc/pl/function/constructors.md | 30 +++++++++--------- doc/pl/function/general.md | 14 ++++----- doc/pl/function/scopes.md | 55 ++++++++++++++++----------------- doc/pl/function/this.md | 26 ++++++++-------- doc/pl/intro/license.md | 2 +- doc/pl/intro/translators.md | 4 ++- 9 files changed, 92 insertions(+), 92 deletions(-) diff --git a/doc/pl/core/undefined.md b/doc/pl/core/undefined.md index 7e1d056e..6fb9ac95 100644 --- a/doc/pl/core/undefined.md +++ b/doc/pl/core/undefined.md @@ -7,7 +7,7 @@ z tych dwóch jest `undefined`. `undefined` jest typem z dokładnie jedną wartością: `undefined`. -Język również definiuje globalną zmienną, która ma wartość `undefined`, zmienna +Język również definiuje globalną zmienną, która ma wartość `undefined` - zmienna ta jest nazwana `undefined`. Jednakże jest to zmienna a **nie** stała, czy słowo kluczowe. Oznacza to, że możliwe jest nadpisanie *wartości* tej zmiennej. @@ -60,7 +60,7 @@ kluczowe `var` i spację po nim. ### Zastosowanie `null` Podczas gdy `undefined` w kontekście języka jest używany jak *null* w sensie -tradycyjnych języków, to `null` w JavaScript (jako literał i jako typ) jest po +tradycyjnych języków, `null` w JavaScript (jako literał i jako typ) jest po prostu kolejnym typem danych. Jest wykorzystywany we wnętrzu JavaScript (np. deklaracji końca łańcucha prototypów diff --git a/doc/pl/function/arguments.md b/doc/pl/function/arguments.md index 9f5d7476..098c2de9 100644 --- a/doc/pl/function/arguments.md +++ b/doc/pl/function/arguments.md @@ -1,18 +1,18 @@ ## Obiekt `arguments` -Każda zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej `arguments`. +Każdy zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej `arguments`. Ta zmienna trzyma listę wszystkich argumentów przekazanych do funkcji. -> **Uwaga:** W przypadku gdy `arguments` zostanie zadeklarowana wewnatrz funkcji +> **Uwaga:** W przypadku gdy `arguments` zostanie zadeklarowana wewnątrz funkcji > poprzez `var` lub jako nazwa jednego z formalnych parametrów, obiekt `arguments` > nie zostanie utworzony. -Obiekt `arguments` **nie** jest typu `Array`. Mimo, że posiada pewne cechy -semantyki tablic - właściwość `length` - to nie dziedziczy on z `Array.prototype`, -ale w rzeczywistości z `Object`. +Obiekt `arguments` **nie** jest typu `Array`. Mimo że posiada pewne cechy +semantyki tablic - właściwość `length` - to w rzeczywistości nie dziedziczy +on z `Array.prototype`, tylko z `Object`. -Ze względu na to **nie** można używać standardowych dla tablic metod takich jak -`push`, `pop` czy `slice` na obiekcie `arguments`. Mimo, że iteracja przy pomocy +Ze względu na to, na obiekcie `arguments` **nie** można używać standardowych dla tablic metod, +takich jak `push`, `pop` czy `slice`. Mimo że iteracja przy pomocy pętli `for` działa dobrze, to aby skorzystać ze standardowych metod tablicowych należy skonwertować `arguments` do prawdziwego obiekt `Array`. @@ -29,7 +29,7 @@ które mają duży wpływ na wydajność. ### Przekazywanie argumentów Zalecany sposób przekazywania argumentów z jednej funkcji do następnej -wyglada następująco. +wyglada następująco: function foo() { bar.apply(null, arguments); @@ -58,11 +58,11 @@ szybkich i nieograniczonych wrapperów. ### Parametry formalne i indeksy argumentów -Obiekt `arguments` tworzy funckje *getter* i *setter* nie tylko dla swoich +Obiekt `arguments` tworzy funkcje *getter* i *setter* nie tylko dla swoich właściwości, ale również dla parametrów formalnych funkcji. W rezultacie zmiana wartości parametru formalnego zmieni również wartość -odpowiadającemu mu wpisowi w obiekcie `arguments`, zachodzi to również w drugą stronę. +odpowiadającemu mu wpisowi w obiekcie `arguments`. Zachodzi to również w drugą stronę. function foo(a, b, c) { arguments[0] = 2; @@ -79,7 +79,7 @@ odpowiadającemu mu wpisowi w obiekcie `arguments`, zachodzi to również w drug ### Mity i prawdy o wydajności -Obiekt `arguments` jest zawsze tworzony z wyjątkiem dwóch przypadków, gdy +Obiekt `arguments` jest tworzony zawsze, z wyjątkiem dwóch przypadków, gdy zmienna o takiej nazwie jest zdefiniowana wewnątrz funkcji lub jeden z parametrów formalnych funkcji ma taką nazwę. Nie ma znaczenia czy obiekt `arguments` jest używany czy nie. @@ -108,9 +108,8 @@ nowoczesnych silnikach JavaScript. Ten przypadek to wykorzystanie W powyższym przykładzie `foo` nie może zostać wykorzystana metoda [inline][1] ponieważ potrzebne są nie tylko informacje na własny temat ale również na temat funkcji wywołującej. Takie użycie nie tylko uniemożliwia -inlining i korzyści z niej wynikające, ale również stanowi złamanie -zasad enkapsulacji ponieważ ta funkcja jest zależna od kontekstu -w jakim została wywołana. +inlining i korzyści z niego wynikające, ale też łamie zasady enkapsulacji, +ponieważ ta funkcja jest zależna od kontekstu w jakim została wywołana. **Mocno zalecane** jest aby **nigdy** nie korzystać z `arguments.callee` i żadnej jej własności. diff --git a/doc/pl/function/closures.md b/doc/pl/function/closures.md index c8e45b99..8cb6fb79 100644 --- a/doc/pl/function/closures.md +++ b/doc/pl/function/closures.md @@ -1,7 +1,7 @@ ## Domknięcia i referencje -Jedną z najpotężniejszych funkcjonalności języka JavaScript są *domknięcia*, -oznacza to że zasięg **zawsze** posiada dostęp do zewnętrznego zasięgu w którym +Jedną z najpotężniejszych funkcjonalności języka JavaScript są *domknięcia*. +Oznacza to że zasięg **zawsze** posiada dostęp do zewnętrznego zasięgu, w którym został zdefiniowany. Ponieważ zasięg w JavaScript można definiować tylko poprzez [funckję](#function.scopes), wszystkie funkcje domyślnie zachowują się jak domknięcia. @@ -24,16 +24,16 @@ został zdefiniowany. Ponieważ zasięg w JavaScript można definiować tylko po foo.increment(); foo.get(); // 5 -Tutaj `Counter` zwraca **dwa** domknięcia: funkcję `increment` oraz funckję `get`. -Obie te funkcję trzymają **referencję** do zasięgu `Counter` a co za tym idzie +Tutaj `Counter` zwraca **dwa** domknięcia: funkcję `increment` oraz funkcję `get`. +Obie te funkcje trzymają **referencję** do zasięgu `Counter`, a co za tym idzie zawsze posiadają dostęp do zmiennej `count` tak, jakby ta zmienna była zdefiniowana w zasięgu tych funkcji. -### Dlaczego zmienne przywatne działają +### Dlaczego zmienne prywatne działają? -Ponieważ nie ma możliwości wskazania lub przypisania zasięgu w JavaScript, to -**nie** istnieje sposób aby uzyskać dostęp do zmiennej `count` z zewnątrz. -Wykorzystanie tych dwóch domkinęć jest jedynym sposobem na interakcję z tą zmienną. +Ponieważ nie ma możliwości wskazania lub przypisania zasięgu w JavaScript, +**nie** istnieje sposób, aby uzyskać dostęp do zmiennej `count` z zewnątrz. +Wykorzystanie tych dwóch domknięć jest jedynym sposobem na interakcję z tą zmienną. var foo = new Counter(4); foo.hack = function() { @@ -48,7 +48,7 @@ Zamiast tego funkcja utworzy lub nadpisze *globalną* zmienną `count`. ### Domknięcia wewnątrz pętli Jednym z częstrzych błędów jest wykorzystywanie domknięć wewnątrz pętli, -aby wartość zmiennej po której odbywa się iteracja był kopiowana do +aby wartość zmiennej po której odbywa się iteracja była kopiowana do wewnętrznej funkcji. for(var i = 0; i < 10; i++) { @@ -80,8 +80,8 @@ z [anonimowego wrappera](#function.scopes). })(i); } -Zewnętrzna anonimowa funkcja zostaje wywołana od razu z parametrem `i` -jako pierwszym argumentem i otrzyma kopię **wartości** zmiennej `i` jako +Zewnętrzna anonimowa funkcja zostanie wywołana od razu z parametrem `i` +jako pierwszym argumentem oraz otrzyma kopię **wartości** zmiennej `i` jako zmienną `e`. Anonimowa funkcja która zostaje przekazana do `setTimeout` teraz posiada diff --git a/doc/pl/function/constructors.md b/doc/pl/function/constructors.md index 4cb7bd12..4e0f502e 100644 --- a/doc/pl/function/constructors.md +++ b/doc/pl/function/constructors.md @@ -1,7 +1,7 @@ ## Konstruktory -Konstruktory w JavaScript również wyglądają inaczej niż innych języka. Każde -wywołanie funkcji, które jest poprzedone słowem kluczowym `new` zachowuje się +Konstruktory w JavaScript również wyglądają inaczej niż innych językach. Każde +wywołanie funkcji, które jest poprzedone słowem kluczowym `new`, zachowuje się jak konstruktor. Wewnątrz konstruktora - wywoływanej fukcji - wartość `this` wskazuje na @@ -43,7 +43,7 @@ obiekt `Object`. } new Test(); // zwrócony obiekt -Jeżeli słowo kluczowe `new` zostanie pominięte funkcja **nie** zwróci nowego +Jeżeli słowo kluczowe `new` zostanie pominięte, funkcja **nie** zwróci nowego obiektu. function Foo() { @@ -51,13 +51,13 @@ obiektu. } Foo(); // undefined -Mimo, że powyższy kod może zadziałać w pewnych przypadkach, w związku -z działaniem [`this`](#function.this) w języku JavaScript to jako +Mimo że powyższy kod może zadziałać w pewnych przypadkach, w związku +z działaniem [`this`](#function.this) w języku JavaScript, to jako wartość `this`zostanie wykorzystany **obiekt global**. ### Fabryki -Aby móc ominąć słowo kluczowe `new` konstruktor musi jawnie zwracać wartość. +Aby móc ominąć słowo kluczowe `new`, konstruktor musi jawnie zwracać wartość. function Bar() { var value = 1; @@ -74,13 +74,13 @@ Aby móc ominąć słowo kluczowe `new` konstruktor musi jawnie zwracać wartoś new Bar(); Bar(); -Oba wywołania `Bar` zwrócą tą samą rzecz, nowo utworzony obiekt, który posiada -właściwość nazwaną `method` w sobie, dla którego `Bar` jest [Domknięciem](#function.closures). +Oba wywołania `Bar` zwrócą tę samą rzecz, nowo utworzony obiekt, który posiada +właściwość nazwaną `method` i dla którego `Bar` jest [Domknięciem](#function.closures). Należy również pamiętać, że wywołanie `new Bar()` **nie** ma wpływu na prototyp zwróconego obiektu (prototypem będzie `object.prototype` a nie `Bar.prototype`). -Podczas gdy prototyp zostanie przypisany do nowo utworzonego obiektu, to jednak `Bar` -nidgy nie zwróci tego nowego obiektu `Bar`, ale literał obiektu, który jest po +Kiedy prototyp zostanie przypisany do nowo utworzonego obiektu, `Bar` nidgy +nie zwróci tego nowego obiektu `Bar`, tylko literał obiektu, który jest po słowie kluczowym `return`. W powyższym przykładzie nie ma żadnej różnicy w działaniu pomiędzy użyciem @@ -88,8 +88,8 @@ i nieużyciem słowa kluczowego `new`. ### Tworzenie nowych obiektów korzystając z fabryk -Często zaleca się **nie** korzystać z operatora `new` ponieważ zapominając -go zastosować może prowadzić do błędów. +Często zaleca się **nie** korzystać z operatora `new`, ponieważ zapominanie +o jego stosowaniu może prowadzić do błędów. W celu stworzenia nowego obiektu, powinno się używać fabryki i konstruować nowy obiekt wewnątrz tej fabryki. @@ -110,14 +110,14 @@ nowy obiekt wewnątrz tej fabryki. } -Mimo, że powyższy kod jest odporny na brak słowa kluczowego `new` i ułatwia +Mimo że powyższy kod jest odporny na brak słowa kluczowego `new` i ułatwia korzystanie ze [zmiennych prywatnych](#function.closures), to posiada pewne wady. While the above is robust against a missing `new` keyword and certainly makes the use of [private variables](#function.closures) easier, it comes with some downsides. 1. Zużywa więcej pamięci, ponieważ tworzony obiekt **nie** współdzieli metod - poprzez prototyp + poprzez prototyp. 2. Aby móc dziedziczyć fabryka musi skopiować wszystkie metody z dziedziczonego obiektu lub przypisać ten obiekt, z którego się dziedziczy, jako prototyp do nowo utworzonego obiektu. @@ -129,5 +129,5 @@ downsides. Pominięcie słowa kluczowego `new` może prowadzić do błędów, ale na pewno nie powinno to być powodem odrzucenia używania prototypów w ogóle. Sprowadza się to do wyboru rozwiązania, które bardziej pasuje do potrzeb aplikacji. Szczególnie -ważne jest aby wybrać określony styl tworzenia obiektów i się go **trzymać**. +ważne jest, aby wybrać określony styl tworzenia obiektów i **trzymać** się go. diff --git a/doc/pl/function/general.md b/doc/pl/function/general.md index d55153c3..e3f035b2 100644 --- a/doc/pl/function/general.md +++ b/doc/pl/function/general.md @@ -1,6 +1,6 @@ ## Deklaracje funkcji i wyrażenia funkcyjne -Funcje w języku JavaScript są [typami pierwszoklasowymi][1]. Co oznacza, że mogą +Funcje w języku JavaScript są [typami pierwszoklasowymi][1], co oznacza, że mogą być przekazywane jak każda inna wartość. Jednym z typowych zastosowań tej cechy jest przekazywanie *anonimowej funkcji* jako callback do innej, prawdopodobnie asynchronicznej funkcji. @@ -9,9 +9,9 @@ asynchronicznej funkcji. function foo() {} -Powyższa funkcja zostaje [wyniesiona](#function.scopes) zanim program wystartuje, dzięki temu +Powyższa funkcja zostaje [wyniesiona](#function.scopes) zanim program wystartuje. Dzięki temu jest dostępna *wszędzie* w ramach zasięgu, w którym została *zadeklarowana*, -nawet jeżeli ta funkcja została wywołana przed faktyczną definicją w kodzie źródłowym. +nawet, jeżeli ta funkcja została wywołana przed faktyczną definicją w kodzie źródłowym. foo(); // Działa ponieważ definicja funkcji została wyniesiona // na początek zasięgu przed uruchomieniem kodu @@ -27,7 +27,7 @@ Ten przykład przypisuje nienazwaną i *anonimową* funkcję do zmiennej `foo`. foo(); // wyrzuca błąd TypeError var foo = function() {}; -Ze względu na fakt, że deklaracja `var` wynosi zmienną `foo` na początek zasięgu, +Ze względu na fakt, że deklaracja `var` wynosi zmienną `foo` na początek zasięgu zanim kod faktycznie zostanie uruchomiony, `foo` będzie zdefiniowane kiedy skrypt będzie wykonywany. @@ -35,7 +35,7 @@ Ale ponieważ przypisania robione są dopiero podczas wykonania, wartość `foo` ustawiona na domyślną wartość [undefined](#core.undefined) zanim powyższy kod zostanie uruchomiony. -### Nazwane wyrażenia funkdyjne +### Nazwane wyrażenia funkcyjne Kolejnym specjalnym przypadkiem jest przypisanie nazwanej funkcji. @@ -44,10 +44,10 @@ Kolejnym specjalnym przypadkiem jest przypisanie nazwanej funkcji. } bar(); // wyrzuca ReferenceError -W zewnętrznym zakresie `bar` nie będzie dostępne, ponieważ funkcja zostaje +W zewnętrznym zakresie `bar` nie będzie dostępna, ponieważ funkcja zostaje przypisana do `foo`, jednakże w wewnętrznym zakresie `bar` będzie dostępna. Jest to spowodowane tym, jak działa [rozwiązywanie nazw](#function.scopes) w języku JavaScript. Nazwa funkcji jest *zawsze* dostępna w lokalnym zakresie tej funkcji. -[1]: http://pl.wikipedia.org/wiki/Typ_pierwszoklasowy \ No newline at end of file +[1]: http://pl.wikipedia.org/wiki/Typ_pierwszoklasowy diff --git a/doc/pl/function/scopes.md b/doc/pl/function/scopes.md index a06eba05..52126570 100644 --- a/doc/pl/function/scopes.md +++ b/doc/pl/function/scopes.md @@ -1,6 +1,6 @@ ## Zasięg zmiennych i przestrzenie nazw -Mimo, że JavaScript radzi sobie dobrze ze składnią, opisującą dwa pasujące +Mimo że JavaScript radzi sobie dobrze ze składnią opisującą dwa pasujące nawiasy klamrowe jako blok, to jednak **nie** wspiera zasięgu blokowego. Jedynym zasięgiem jaki istnieje w JavaScript jest *zasięg funkcyjny*. @@ -12,17 +12,16 @@ Jedynym zasięgiem jaki istnieje w JavaScript jest *zasięg funkcyjny*. } > **Uwaga:** Jeżeli notacja `{...}` nie jest użyta w przypisaniu, deklaracji return -> lub jako argument funkcji to zostanie zinterpretowana jako deklaracja bloku, a -> **nie** jako literał obiektu. W połączeniu z [automatycznym wstawianiem średnika](#core.semicolon), +> lub jako argument funkcji, to zostanie zinterpretowana jako deklaracja bloku, +> a **nie** jako literał obiektu. W połączeniu z [automatycznym wstawianiem średnika](#core.semicolon), > może prowadzić do subtelnych błędów. W JavaScripcie nie ma również przestrzeni nazw, co oznacza, że wszystko jest definiowane w jednej *globalnie współdzielonej* przestrzeni nazw. -Za każdym razem gdy zmienna jest -Z każdym odwołaniem do zmiennej JavaScript przeszukuje w górę wszystkie zasięgi -dopóki nie znajdzie tej zmiennej. W przypadku gdy przeszukiwanie dotrze do globalnego -zasięgu i nadal nie znajdzie żądanej nazwy to wyrzuca błąd `ReferenceError`. +Z każdym odwołaniem do zmiennej, JavaScript przeszukuje w górę wszystkie zasięgi +dopóki nie znajdzie tej zmiennej. W przypadku, gdy przeszukiwanie dotrze do globalnego +zasięgu i nadal nie znajdzie żądanej nazwy, to wyrzuca błąd `ReferenceError`. ### Zmora globalnych zmiennych @@ -32,11 +31,11 @@ zasięgu i nadal nie znajdzie żądanej nazwy to wyrzuca błąd `ReferenceError` // script B var foo = '42' -Powyższe dwa skrypty **nie** dają tego samego efektu. Skrypt A definiuje zmienna -nazwaną `foo` w *globalnym* zasięgu natomiast skrypt B definiuje `foo` +Powyższe dwa skrypty **nie** dają tego samego efektu. Skrypt A definiuje zmienną +nazwaną `foo` w *globalnym* zasięgu, natomiast skrypt B definiuje `foo` w *aktualnym* zasięgu. -Jeszcze raz, to wcale nie daje *tego samego efektu*, nie użycie `var` może mieć +Jeszcze raz, to wcale nie daje *tego samego efektu*. Nie użycie `var` może mieć poważne konsekwencje. // globalny zasięg @@ -49,8 +48,8 @@ poważne konsekwencje. foo; // 21 Pominięcie słowa `var` w deklaracji wewnątrz funkcji `test` nadpisze wartość -zmiennej globalnej `foo`. Mimo, że nie wygląda to na początku jak duży problem, -to posiadając wiele tysięcy linii kodu w JavaScript i nie korzystanie z `var` +zmiennej globalnej `foo`. Mimo że nie wygląda to na początku na duży problem, +posiadanie wielu tysięcy linii kodu w JavaScript i nie korzystanie z `var` wprowadzi straszne i trudne do wyśledzenia błędy. // globalny zasięg @@ -68,7 +67,7 @@ wprowadzi straszne i trudne do wyśledzenia błędy. Zewnętrzna pętla zakończy działanie po pierwszym wywołaniu `subLoop`, ponieważ `subLoop` nadpisuje wartość globalnej zmiennej `i`. Użycie `var` w drugiej pętli -`for` pozwoliło by łatwo uniknąć problemu. Słowo kluczowe `var` nie powinno być +`for` pozwoliłoby łatwo uniknąć problemu. Słowo kluczowe `var` nie powinno być **nigdy** pominięte w deklaracji, chyba że *pożądanym skutkiem* jest wpłynięcie na zewnętrzny zasięg. @@ -144,15 +143,15 @@ najbliższego zasięgu. test(); -Brak blokowego zasięgu nie tylko przeniesie deklaracje `var` poza ciało pętle, +Brak blokowego zasięgu nie tylko przeniesie deklaracje `var` poza ciało pętli, ale również spowoduje, że niektóre porównania `if` staną się nieintuicyjne. -W oryginalnym kodzie wewnątrz deklaracja `if` zdaje się modyfikować *zmienną +W oryginalnym kodzie instrukcja warunkowa `if` zdaje się modyfikować *zmienną globalną* `goo`, podczas gdy faktycznie modyfikuje ona *zmienną lokalną* - po tym jak zostało zastosowane windowanie (hoisting). -Bez wiedzy na temat podnoszenia (hoistingu), poniższy kod może wydawać się -wyrzucać błąd `ReferenceError`. +Bez wiedzy na temat podnoszenia (hoistingu), poniższy kod może sprawiać wrażenie, +że zobaczymy błąd `ReferenceError`. // sprawdz czy SomeImportantThing zostało zainicjalizowane if (!SomeImportantThing) { @@ -166,7 +165,7 @@ przeniesiona na początek *globalnego zasięgu*. // inny kod który może ale nie musi zainicjalizować SomeImportantThing - // upewnienie sie że SomeImportantThing zostało zainicjalizowane + // upewnienie sie, że SomeImportantThing zostało zainicjalizowane if (!SomeImportantThing) { SomeImportantThing = {}; } @@ -174,7 +173,7 @@ przeniesiona na początek *globalnego zasięgu*. ### Kolejność rozwiązywania nazw Wszystkie zasięgi w JavaScripcie, włączając *globalny zasięg*, posiadają -zdefiniowana wewnątrz specjalną nazwę [`this`](#function.this), która wskazuje +zdefiniowaną wewnątrz specjalną nazwę [`this`](#function.this), która wskazuje na *aktualny obiekt*. Zasięg funkcyjny posiada również zdefiniowaną wewnętrznie nazwę @@ -188,8 +187,8 @@ JavaScript będzie szukać nazwy w określonej kolejności: 3. Jeżeli fukcja została nazwana `foo` skorzystaj z tego. 4. Przejdz do zewnętrznego zasięgu i przejdz do kroku **#1**. -> **Uwaga:** Jeżeli jeden z parametrów fukcji został nazwany `arguments` zapobiegnie -> to utworzenia domyślnego obiektu `arguments`. +> **Uwaga:** Jeżeli jeden z parametrów fukcji został nazwany `arguments`, zapobiegnie +> to utworzeniu domyślnego obiektu `arguments`. ### Przestrzenie nazw @@ -206,16 +205,16 @@ tego problemu korzystając z *anonimowych wrapperów*. })(); // natychmiastowe wykonanie funkcji -Nienazwane funkcje są rozpoznane jako [wyrażenia](#function.general), więc +Anonimowe funkcje są rozpoznane jako [wyrażenia](#function.general), więc aby mogły zostać wywołane muszą zostać zewaluowane. - ( // zewaluowanie fukcji znajdującej się wewnątrz nawiasów + ( // zewaluowanie funkcji znajdującej się wewnątrz nawiasów function() {} ) // zwrócenie obiektu funkcji () // wywołanie rezultatu ewaluacji -Istnieją inne sposoby aby zewaluować i wykonać wyrażenie funkcyjne. Mimo, że -mają inną składnie, zachowują się dokładnie tak samo. +Istnieją inne sposoby aby zewaluować i wykonać wyrażenie funkcyjne. Mimo że +mają inną składnię, zachowują się dokładnie tak samo. // Trzy inne sposoby !function(){}(); @@ -224,11 +223,11 @@ mają inną składnie, zachowują się dokładnie tak samo. ### Wnioski -Zaleca się aby zawsze używać *anonimowych wrapperów* do hermetyzacji kodu wewnątrz +Zaleca się, aby zawsze używać *anonimowych wrapperów* do hermetyzacji kodu wewnątrz jego własnej przestrzeni nazw. To nie tylko chroni kod przed kolizją nazw, ale również wprowadza lepszą modularyzację programów. Ponadto, stosowanie zmiennych globalnych jest uznawane za złą praktykę. -Jakiekolwiek wykorzystanie zmiennych globalnych wskazuje na źle napisany kod, -który jest podatny na błędy i trudny do utrzymania. +Wykorzystanie zmiennych globalnych wskazuje na źle napisany kod, który +jest podatny na błędy i trudny do utrzymania. diff --git a/doc/pl/function/this.md b/doc/pl/function/this.md index 8fc8564d..89a5a1e5 100644 --- a/doc/pl/function/this.md +++ b/doc/pl/function/this.md @@ -1,8 +1,8 @@ ## Jak działa `this` -JavaScript posiada inną koncepcję odnośnie tego na co wskazuje specjalna -nazwa `this`, niż większość innych języków programowania. Istnieją dokładnie -**pięć** różnych sytuacji w których wartość `this` zostaje przypisana w języku JavaScript. +JavaScript posiada inną koncepcję odnośnie tego na co wskazuje słowo kluczowe +`this`, niż większość innych języków programowania. Istnieje dokładnie +**pięć** różnych sytuacji, w których wartość `this` jest przypisana w języku JavaScript. JavaScript has a different concept of what the special name `this` refers to than most other programming languages do. There are exactly **five** different @@ -12,7 +12,7 @@ ways in which the value of `this` can be bound in the language. this; -Używanie `this` w globalnym zasięgu, zwróci po prostu referencje do obiektu *global*. +Używanie `this` w globalnym zasięgu, zwróci po prostu referencję do obiektu *global*. ### Wywołanie funkcji @@ -34,7 +34,7 @@ W tym przypadku `this` będzie wskazywało na `test`. new foo(); -Wywołanie funkcji, które jest poprzedzone słowem kluczowym `new` zachowuje się +Wywołanie funkcji, które jest poprzedzone słowem kluczowym `new`, zachowuje się jak [konstruktor](#function.constructors). Wewnątrz funkcji `this` będzie wskazywało na *nowo utworzony* obiekt. @@ -70,10 +70,10 @@ w praktyce. test(); } -Powszechnym nieporozumieniem jest, że `this` wewnątrz `test` wskazuje na `Foo`, +Powszechnym błędem jest myślenie, że `this` wewnątrz `test` wskazuje na `Foo`, podczas gdy w rzeczywistości tak **nie jest**. -Aby uzyskać dostęp do `Foo` wewnątrz `test` niezbędne jest stworzenie wewnątrz +Aby uzyskać dostęp do `Foo` wewnątrz `test`, niezbędne jest stworzenie wewnątrz metody lokalnej zmiennej, która będzie wskazywała na `Foo`. Foo.method = function() { @@ -84,13 +84,13 @@ metody lokalnej zmiennej, która będzie wskazywała na `Foo`. test(); } -`that` jest zwykłą zmienną, ale jest to powszechnie stosowana konwencja, aby otrzymać -wartość zewnętrznego `this`. W połączeniu z [domknięciami(closures)](#function.closures) -jest to sposób na przekazywanie wartości `this` wokoło. +`that` jest zwykłą zmienną, ale jest to powszechnie stosowana konwencja otrzymywania +wartości zewnętrznego `this`. W połączeniu z [domknięciami(closures)](#function.closures), +jest to sposób na przekazywanie wartości `this` wokół. ### Metody przypisywania -Kolejną rzeczą, która **nie** działa w języku JavaScript jest nadawanie aliasów +Kolejną rzeczą, która **nie** działa w języku JavaScript, jest nadawanie aliasów funkcjom, co oznacza **przypisanie** metody do zmiennej. var test = someObject.methodTest; @@ -100,7 +100,7 @@ Podobnie jak w pierwszym przypadku `test` zachowuje się jak wywołanie zwykłej funkcji, a zatem wewnątrz funkcji `this` już nie będzie wskazywało `someObject`. Podczas gdy późne wiązanie `this` może się na początku wydawać złym pomysłem, -to w rzeczywistości jest to rzecz, która powoduje że +to w rzeczywistości jest to rzecz, która sprawia, że [dziedziczenie prototypowe](#object.prototype) działa. function Foo() {} @@ -112,5 +112,5 @@ to w rzeczywistości jest to rzecz, która powoduje że new Bar().method(); Kiedy metoda `method` zostanie wywołana na instancji `Bar`, `this` będzie -wskazywało właśnie tą instancję. +wskazywało właśnie tę instancję. diff --git a/doc/pl/intro/license.md b/doc/pl/intro/license.md index 7e2c77d0..c9329aab 100644 --- a/doc/pl/intro/license.md +++ b/doc/pl/intro/license.md @@ -2,7 +2,7 @@ JavaScript Garden jest publikowany w ramach [licencji MIT] [1] i kod źródłowy znajduje się na serwerze [GitHub] [2]. Jeśli znajdziesz jakieś błędy lub literówek zgłoś proszę -[problem] [3] lub rozwiązag go i zglosić pull request ze swojego repozytorium. +[problem] [3] lub rozwiąż go i zgloś pull request ze swojego repozytorium. Możesz nas także znaleźć w pokoju [JavaScript] [4] na chacie Stack Overflow. [1]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE diff --git a/doc/pl/intro/translators.md b/doc/pl/intro/translators.md index 60b3626e..5197a9fd 100644 --- a/doc/pl/intro/translators.md +++ b/doc/pl/intro/translators.md @@ -1,5 +1,7 @@ ## Tłumaczenie - [Łukasz Kufel][1] + - [Maciej Ciemborowicz][2] -[1]: http://qfel13.pl \ No newline at end of file +[1]: http://qfel13.pl +[2]: http://blog.ciemborowicz.pl From e2c62e07e100e6fa8e5e4a5f55778ea5edd584f7 Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 18:02:42 +0200 Subject: [PATCH 203/641] Corrections in Polish translation (Object section). --- doc/pl/object/forinloop.md | 30 +- doc/pl/object/general.md | 36 +- doc/pl/object/hasownproperty.md | 22 +- doc/pl/object/prototype.md | 58 +- site/pl/index.html | 1911 +++++++++++++++++++++++++++++++ 5 files changed, 1984 insertions(+), 73 deletions(-) create mode 100644 site/pl/index.html diff --git a/doc/pl/object/forinloop.md b/doc/pl/object/forinloop.md index a2e2b5fe..9a6654a6 100644 --- a/doc/pl/object/forinloop.md +++ b/doc/pl/object/forinloop.md @@ -1,11 +1,11 @@ -## The `for in` Loop +## Pętla `for in` Podobnie jak operator `in`, pętla `for in` przeszukuje łańcuch prototypów podczas iteracji po właściwościach obiektu. > **Uwaga:** pętla `for in` **nie** będzie iterować po właściwościach, które -> mają ustawiony atrybut `enumerable` na `false` na przykład właściwość -> `length` tablicy. +> mają ustawiony atrybut `enumerable` na `false` (na przykład właściwość +> `length` tablicy). // Zatrucie Object.prototype Object.prototype.bar = 1; @@ -15,15 +15,15 @@ podczas iteracji po właściwościach obiektu. console.log(i); // wyświetla obie właściwości: bar i moo } -Ponieważ nie jest możliwe, aby zmienić zachowanie pętli `for in` to niezbędne +Ponieważ zmiana zachowania pętli `for in` nie jest możliwa, niezbędne jest odfiltrowanie niechcianych właściwości wewnątrz ciała pętli, korzystając z metody [`hasOwnProperty`](#object.hasownproperty) z `Object.prototype`. -> **Uwaga:** Ponieważ pętla `for in` zawsze przeszukuje cały łańcuch prototypów +> **Uwaga:** Ponieważ pętla `for in` zawsze przeszukuje cały łańcuch prototypów, > będzie się ona stawała coraz wolniejsza przy dodaniu każdej kolejnej warstwy > dziedziczenia do obiektu. -### Korzystanie z `hasOwnProperty` do odfiltrowania +### Filtrowania przy użyciu `hasOwnProperty` // foo z przykładu powyżej for(var i in foo) { @@ -32,19 +32,19 @@ z metody [`hasOwnProperty`](#object.hasownproperty) z `Object.prototype`. } } -To jest jedyna poprawna wersja, którą należy używać. Ze względu na użycie -`hasOwnProperty` zostanie wypisane **jedynie** `moo`. Gdy opuścimy `hasOwnProperty` -kod będzie podatny na błędy, gdy natywne prototypy np. `Object.prototype` -zostanie rozszerzony. +To jest jedyna poprawna wersja, której należy używać. Ze względu na użycie +`hasOwnProperty` zostanie wypisane **jedynie** `moo`. Gdy opuścimy `hasOwnProperty`, +kod będzie podatny na błędy, gdy natywne prototypy (np. `Object.prototype`) +zostaną rozszerzone. -[Prototype][1] jest jednym z szeroko rozpowszechniony frameworków, który dokonuje -takiego rozszerzenia. Używanie tego frameworku oraz nie używanie w pętle `for in` +[Prototype][1] jest jednym z popularniejszych frameworków, które dokonują +takiego rozszerzenia. Używanie tego frameworku oraz nie stosowanie w pętli `for in` metody `hasOwnProperty` gwarantuje błędy w wykonaniu. ### Wnioski -Zaleca się aby zawsze używać metody `hasOwnProperty`. Nigdy nie powinno się dokonywać -żadnych założeń na temat środowiska, w którym kod będzie wykonywany i czy natywne -prototypy zostały rozszerzone czy nie. +Zaleca się, aby zawsze używać metody `hasOwnProperty`. Nigdy nie powinno się dokonywać +żadnych założeń na temat środowiska, w którym kod będzie wykonywany ani tego, czy +natywne prototypy zostały rozszerzone, czy nie. [1]: http://www.prototypejs.org/ diff --git a/doc/pl/object/general.md b/doc/pl/object/general.md index 24a074e3..e4ff6a96 100644 --- a/doc/pl/object/general.md +++ b/doc/pl/object/general.md @@ -10,9 +10,9 @@ Wszystko w JavaScripcie zachowuje sie jak obiekt, z dwoma wyjątkami Foo.bar = 1; Foo.bar; // 1 -Popularnym błędem jest wykorzystanie literałów liczbowych jako obiektu. -Spowodowanie jest to usterką w parserze JavaScript, który interpretuje kropkę -po literale liczbowym jako rozdzielenie części całkowitej od części po przecinku +Popularnym błędem jest traktowanie literałów liczbowych jak obiektu. +Spowodowane jest to specyfiką parsera JavaScript, który interpretuje kropkę +po literale liczbowym jako rozdzielenie części całkowitej od części ułamkowej liczby. 2.toString(); // wyrzuca błąd SyntaxError @@ -22,27 +22,27 @@ jak obiekt. 2..toString(); // druga kropka jest poprawnie rozpoznana 2 .toString(); // zauważ, że pozostawiona jest spacja przed kropką - (2).toString(); // 2 zostanie zewaluowane najpiewr + (2).toString(); // 2 zostanie najpierw zewaluowane ### Obiekty jako typy danych -Obiekty w języku JavaScript mogą być używana jako [*tablice asocjacyjne*][1]. -Ponieważ obiekty głównie składają się z mapowań pomiędzy nazwanymi właściwościami (kluczami) +Obiekty w języku JavaScript mogą być używana jako [*tablice asocjacyjne*][1], +ponieważ obiekty składają się głównie z mapowań pomiędzy nazwanymi właściwościami (kluczami) a wartościami dla tych atrybutów. -Używając literału obiektu - notacji `{}` - istnieje możliwość stworzenie obiektu prostego. +Używając literału obiektu - notacji `{}` - istnieje możliwość stworzenia obiektu prostego. Ten nowy obiekt bedzie [dziedziczył](#object.prototype) z `Object.prototype` oraz -nie bedzie posiadał żadnych [własnych właściwości](#object.hasownproperty) zdefiniowanych w sobie. +nie bedzie posiadał żadnych [własnych właściwości](#object.hasownproperty). - var foo = {}; // nowy pusty obiekt + var foo = {}; // nowy, pusty obiekt // nowy obiekt z właściwością test o wartości 12 var bar = {test: 12}; ### Dostęp do właściwości -Właściwości obiektu można uzyskać na dwa sposoby, poprzez notację z kropką -lub notacje z nawiasami kwadratowymi. +Właściwości obiektu można uzyskać na dwa sposoby - poprzez notację z kropką +lub z nawiasami kwadratowymi. var foo = {name: 'Kitten'} foo.name; // kitten @@ -54,8 +54,8 @@ lub notacje z nawiasami kwadratowymi. foo.1234; // wyrzuca błąd SyntaxError foo['1234']; // działa, zwraca undefined -Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą że notacja z nawiasami -kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzycenia +Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą, że notacja z nawiasami +kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzucenia błędu podczas odczytu nieistniejącej właściwości. ### Usuwanie właściwości @@ -79,19 +79,19 @@ związaną z własnością, ale nie usunie to *klucza* (nazwy własności) z obi } } -Powyższy kod wypisuje dwie linie `bar undefined` i `foo null` - tylko własność `baz` +Powyższy kod wypisuje dwie linie - `bar undefined` i `foo null`. Tylko własność `baz` została usunięta i dlatego nie została wypisana. ### Notacja właściwości var test = { - 'case': 'I am a keyword so I must be notated as a string', - delete: 'I am a keyword too so me' // wyrzuca błąd SyntaxError + 'case': 'jestem zastrzeżonym słowem kluczowym, więc muszę być w cudzysłowie', + delete: 'tak samo jak ja' // wyrzuca błąd SyntaxError }; -Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst(bez cudzysłowów +Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst (bez cudzysłowów lub apostrofów) lub jako string (w cudzisłowach lub apostrofach). -Ze względu na kolejne niedociągnięcie w parserze JavaScript +Ze względu na kolejne niedociągnięcie w parserze JavaScript, powyższy kod wyrzuci błąd `SyntaxError` dla implementacji JavaScript ponizej ECMAScript 5. Ten błąd wynika z faktu, że `delete` jest *słowem kluczowym*, dlatego musi zostać diff --git a/doc/pl/object/hasownproperty.md b/doc/pl/object/hasownproperty.md index 5c3429e6..d9366447 100644 --- a/doc/pl/object/hasownproperty.md +++ b/doc/pl/object/hasownproperty.md @@ -1,13 +1,13 @@ ## `hasOwnProperty` -W celu sprawdzenia czy dana właściwość została zdefiniowana *w tym* obiekcie a **nie** -w [łańcuchu prototypów](#object.prototype) niezbędne jest skorzystanie z metody -`hasOwnProperty`, która wszystkie obiekty dziedziczą z `Object.prototype`. +W celu sprawdzenia, czy dana właściwość została zdefiniowana *w tym* obiekcie, a **nie** +w [łańcuchu prototypów](#object.prototype), niezbędne jest skorzystanie z metody +`hasOwnProperty`, której wszystkie obiekty dziedziczą z `Object.prototype`. -> **Uwaga:** **Nie** jest wystarczające, by sprawdzić, czy właściwość jest `undefined`. -> Ponieważ właściwość może istnieć, ale jej wartość być ustawiona na `undefined`. +> **Uwaga:** **Nie** wystarczy sprawdzić, czy właściwość jest `undefined`, +> ponieważ właściwość może istnieć, ale jej wartość być ustawiona na `undefined`. -`hasOwnProperty` jest jedyna metodą w języku JavaScript która operuje na właściwościach +`hasOwnProperty` jest jedyną metodą w języku JavaScript, która operuje na właściwościach i **nie** przegląda całego łańcucha prototypów. // Zatrucie Object.prototype @@ -28,7 +28,7 @@ ale gdzieś indziej w łańcuchu prototypów. ### `hasOwnProperty` jako właściwość JavaScript **nie** chroni właściwości o nazwie `hasOwnProperty`, zatem istnieje -możliwość, że obiekt może posiadać tak nazwaną właściwość. Konieczne jest użycie +możliwość, że obiekt będzie posiadać tak nazwaną właściwość. Konieczne jest użycie *zewnętrznego* `hasOwnProperty`, aby otrzymać poprawne rezultaty. var foo = { @@ -46,8 +46,8 @@ możliwość, że obiekt może posiadać tak nazwaną właściwość. Konieczne ### Wnioski -**Jedyną** metodą służącą do sprawdzenia zdefiniowania jakiejś właściwości w konkretnym -obiekcie jest metoda `hasOwnProperty`. Zaleca się korzystać z `hasOwnProperty` jako część -**każdej** [pętli `for in`](#object.forinloop), pozwoli to uniknąć błędów pochodzących z -rozszerzonych natywnych [prototypów](#object.prototype). +**Jedyną** metodą służącą do sprawdzenia istnienia jakiejś właściwości w konkretnym +obiekcie jest metoda `hasOwnProperty`. Zaleca się korzystać z `hasOwnProperty` w +**każdej** [pętli `for in`](#object.forinloop). Pozwoli to uniknąć błędów pochodzących +z rozszerzonych natywnych [prototypów](#object.prototype). diff --git a/doc/pl/object/prototype.md b/doc/pl/object/prototype.md index e79a45cb..85dbdfb7 100644 --- a/doc/pl/object/prototype.md +++ b/doc/pl/object/prototype.md @@ -1,23 +1,23 @@ ## Prototyp -JavaScript nie posiada klasycznego modelu dziedziczenia, lecz zamiast tego +JavaScript nie posiada klasycznego modelu dziedziczenia. Zamiast tego dziedziczenie jest realizowane poprzez *prototypy*. Choć jest to często uważane za jedną ze słabości języka JavaScript, prototypowy model dziedziczenia, jest w rzeczywistości potężniejszy od klasycznego -modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowym -jest dość proste, podczas gdy zrobienie odwrotnie to już o wiele trudniejsze zadanie. +modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowego +jest dość proste, podczas gdy zrobienie odwrotnego przekształcenie to o wiele trudniejsze zadanie. Ze względu na fakt, że w JavaScript jest w zasadzie jedynym powszechnie stosowanym -językiem, któy posiada prototypowy model dziedziczenia, to wymaga troche czasu aby -dostosować się do różnic pomiędzy tymi dwoma modelami. +językiem, któy posiada prototypowy model dziedziczenia, dostosowanie się do różnic pomiędzy +tymi dwoma modelami wymaga trochę czasu. Pierwszą znaczącą różnicą jest to, że dziedziczenie w JavaScript odbywa się za pomocą tak zwanych *łańcuchów prototypów*. -> **Uwaga:** Używanie po prosstu `Bar.prototype = Foo.prototype` spowoduje, że oba obiekty -> będą korzystały z **tego samego** prototypu. W związku z tym zmiany na prototypie jednego -> obiektu będą również zmieniały prototyp drugiego obiektu, co jest w wiekszości przypadków +> **Uwaga:** Używanie po prostu `Bar.prototype = Foo.prototype` spowoduje, że oba obiekty +> będą korzystały z **tego samego** prototypu. W związku z tym zmiany w prototypie jednego +> obiektu będą również zmieniały prototyp drugiego obiektu, co jest ,w wiekszości przypadków, > niepożądanym efektem. function Foo() { @@ -47,25 +47,25 @@ tak zwanych *łańcuchów prototypów*. Object.prototype { toString: ... /* etc. */ } -W powyższym przykładzie obiekt `test` będzie dziedziczył z obydwu tj. +W powyższym przykładzie obiekt `test` będzie dziedziczył z obydwu, tj. `Bar.prototyp` i `Foo.prototyp`, stąd będzie miał dostęp do funkcji `method`, która była zdefiniowana w `Foo`. Ponadto obiekt będzie miał dostęp do właściwości `value`, która jest jednyną instancją `Foo` i stała się jego prototypem. -Ważne jest, aby pamiętać `new Bar` **nie** tworzy nowej instancji `Foo`, -ale wykorzystuje instancje, którą jest przypisana do własności `prototype`. +Należy pamiętać, że `new Bar` **nie** tworzy nowej instancji `Foo`, +tylko wykorzystuje instancję, która jest przypisana do własności `prototype`. Zatem Wszystkie instancje `Bar` będą dzieliły tą samą własność `value`. > **Uwaga:** **Nie** należy używać konstrukcji `Bar.prototype = Foo`, -> ponieważ nie spowoduje ona przypisania prototypu `Foo` a raczej obiektu +> ponieważ nie spowoduje ona przypisania prototypu `Foo` tylko obiektu > funckji `Foo`. Zatem łańcuch prototypów nie bedzie zawierał `Foo.prototype`, -> ale `Function.prototype`, więc metoda `method` nie będzie w łańcuchu prototypów. +> tylko `Function.prototype`, więc metoda `method` nie będzie w łańcuchu prototypów. ### Wyszukiwanie własności -Podczas dostępu do właściwości obiektu, JavaScript przejdzie w górę łańcucha -prototypów dopóki nie znajdzie właściwości z żądaną nazwą. +Podczas dostępu do właściwości obiektu JavaScript przejdzie w górę łańcucha +prototypów, dopóki nie znajdzie właściwości bez nazwy. -Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha mianowicie `Object.prototype` +Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha, mianowicie `Object.prototype` i nadal nie znajdzie określonej właściwości, to zwróci wartość [undefined](#core.undefined). @@ -85,31 +85,31 @@ na dynamiczne tworzenie łańcuchów prototypów. Czas wyszukiwania właściwości, które są na końcu łańcucha prototypów może mieć negatywny wpływ na wydajność krytycznych części kodu. Dodatkowo, próba dostępu -do nieistniejącej właściwości powoduje zawsze przeszukanie całego łańcucha prototypów. +do nieistniejącej właściwości zawsze spowoduje przeszukanie całego łańcucha prototypów. -Również, podczas [iteracji](#object.forinloop) po właściwościach obiektu -**każda** właściwość, która znajduje się w łańcuchu prototypów niezależnie -na jakim znajduje się poziomie zostanie wyliczona. +Również podczas [iteracji](#object.forinloop) po właściwościach obiektu +**każda** właściwość, która znajduje się w łańcuchu prototypów (niezależnie +na jakim znajduje się poziomie) zostanie wyliczona. ### Rozszerzanie natywnych prototypów Rozszerzanie `Object.prototype` lub innego prototypu wbudowanych typów jest jednym z -najczęściej używanych niedoskonałej częsci języka JavaScript. +najczęściej nadużywanej częsci języka JavaScript. Technika ta nazywana jest [monkey patching][1] i łamie zasady *enkapsulacji*. -Jednak jest szeroko rozpowszechniona w frameworkach takich jak [Prototype][2]. -Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez dodawanie do nich -*niestandardowych* funkcjonalności. +Mimo to jest szeroko rozpowszechniona w frameworkach takich jak [Prototype][2]. +Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez wzbogacanie ich o +*niestandardowe* funkcjonalności. **Jedynym** dobrym powodem do rozszerzania wbudowanych prototypów jest portowanie -funkcjonalności znajdujących sie w nowszych silnikach JavaScript np. [`Array.forEach`][3] +funkcjonalności znajdujących sie w nowszych silnikach JavaScript, np. [`Array.forEach`][3] ### Wnioski -Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczanie -należy **całkowicie** rozumieć prototypowy model dziedziczenia. Ponadto należy uważać -na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń -aby uniknąć problemów z wydajnością. Natywne prototypy nie powinny **nigdy** być +Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczania, +należy **całkowicie** zrozumieć prototypowy model dziedziczenia. Ponadto trzeba uważać +na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń, +aby uniknąć problemów z wydajnością. Natywne prototypy **nigdy** nie powinny być rozszerzane, chyba że ze względu na wprowadzanie kompatybilności z nowszymi silnikami JavaScript. diff --git a/site/pl/index.html b/site/pl/index.html new file mode 100644 index 00000000..872f9cd0 --- /dev/null +++ b/site/pl/index.html @@ -0,0 +1,1911 @@ +JavaScript Garden +

    Wstęp

    JavaScript Garden jest rosnącą kolekcją dokumentów o najdziwniejszych +częściach języka JavaScript. Dokumentacja pomaga uniknąć najczęściej popełnianych +błędów, sybtelnych bugów, problemów wydajnościowych oraz złych praktyk, na które +niedoświadczeni programiści JavaScript mogą natrafić próbując poznać tajniki tego +języka.

    + +

    JavaScript Garden nie ma na celu nauczyć Cię języka JavaScript. Podstawowa +wiedza na temat języka jest wymagana do zrozumienia zagadnień poruszanych w tym +przewodniku. Aby nauczyć się podstaw jezyka JavaScript, odwiedź znakomity +przewodnik na stronach Mozilla Developer Network.

    Licencja

    JavaScript Garden jest publikowany w ramach licencji MIT i kod źródłowy znajduje +się na serwerze GitHub. Jeśli znajdziesz jakieś błędy lub literówek zgłoś proszę +problem lub rozwiąż go i zgloś pull request ze swojego repozytorium. +Możesz nas także znaleźć w pokoju JavaScript na chacie Stack Overflow.

    Obiekty

    Wykorzystanie obiektów i ich właściwości

    Wszystko w JavaScripcie zachowuje sie jak obiekt, z dwoma wyjątkami +null oraz undefined.

    + +
    false.toString() // 'false'
    +[1, 2, 3].toString(); // '1,2,3'
    +
    +function Foo(){}
    +Foo.bar = 1;
    +Foo.bar; // 1
    +
    + +

    Popularnym błędem jest wykorzystanie literałów liczbowych jako obiektu. +Spowodowanie jest to usterką w parserze JavaScript, który interpretuje kropkę +po literale liczbowym jako rozdzielenie części całkowitej od części po przecinku +liczby.

    + +
    2.toString(); // wyrzuca błąd SyntaxError
    +
    + +

    Istnieje kilka rozwiązań, dzieki którym literał liczbowy będzie zachowywał się +jak obiekt.

    + +
    2..toString(); // druga kropka jest poprawnie rozpoznana
    +2 .toString(); // zauważ, że pozostawiona jest spacja przed kropką
    +(2).toString(); // 2 zostanie zewaluowane najpiewr
    +
    + +

    Obiekty jako typy danych

    + +

    Obiekty w języku JavaScript mogą być używana jako tablice asocjacyjne. +Ponieważ obiekty głównie składają się z mapowań pomiędzy nazwanymi właściwościami (kluczami) +a wartościami dla tych atrybutów.

    + +

    Używając literału obiektu - notacji {} - istnieje możliwość stworzenie obiektu prostego. +Ten nowy obiekt bedzie dziedziczył z Object.prototype oraz +nie bedzie posiadał żadnych własnych właściwości zdefiniowanych w sobie.

    + +
    var foo = {}; // nowy pusty obiekt
    +
    +// nowy obiekt z właściwością test o wartości 12
    +var bar = {test: 12}; 
    +
    + +

    Dostęp do właściwości

    + +

    Właściwości obiektu można uzyskać na dwa sposoby, poprzez notację z kropką +lub notacje z nawiasami kwadratowymi.

    + +
    var foo = {name: 'Kitten'}
    +foo.name; // kitten
    +foo['name']; // kitten
    +
    +var get = 'name';
    +foo[get]; // kitten
    +
    +foo.1234; // wyrzuca błąd SyntaxError
    +foo['1234']; // działa, zwraca undefined
    +
    + +

    Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą że notacja z nawiasami +kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzycenia +błędu podczas odczytu nieistniejącej właściwości.

    + +

    Usuwanie właściwości

    + +

    Jedynym sposobem na faktycze usunięcie własności z obiektu jest użycie operatora +delete. Ustawienie własności na undefined lub null usunie tylko wartość +związaną z własnością, ale nie usunie to klucza (nazwy własności) z obiektu.

    + +
    var obj = {
    +    bar: 1,
    +    foo: 2,
    +    baz: 3
    +};
    +obj.bar = undefined;
    +obj.foo = null;
    +delete obj.baz;
    +
    +for(var i in obj) {
    +    if (obj.hasOwnProperty(i)) {
    +        console.log(i, '' + obj[i]);
    +    }
    +}
    +
    + +

    Powyższy kod wypisuje dwie linie bar undefined i foo null - tylko własność baz +została usunięta i dlatego nie została wypisana.

    + +

    Notacja właściwości

    + +
    var test = {
    +    'case': 'I am a keyword so I must be notated as a string',
    +    delete: 'I am a keyword too so me' // wyrzuca błąd SyntaxError
    +};
    +
    + +

    Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst(bez cudzysłowów +lub apostrofów) lub jako string (w cudzisłowach lub apostrofach). +Ze względu na kolejne niedociągnięcie w parserze JavaScript +powyższy kod wyrzuci błąd SyntaxError dla implementacji JavaScript ponizej ECMAScript 5.

    + +

    Ten błąd wynika z faktu, że delete jest słowem kluczowym, dlatego musi zostać +zapisany jako string (z cudzysłowami lub apostrofami), aby zapewnić, że zostanie +to poprawnie zinterpretowane przez starsze silniki języka JavaScript.

    Prototyp

    JavaScript nie posiada klasycznego modelu dziedziczenia, lecz zamiast tego +dziedziczenie jest realizowane poprzez prototypy.

    + +

    Choć jest to często uważane za jedną ze słabości języka JavaScript, +prototypowy model dziedziczenia, jest w rzeczywistości potężniejszy od klasycznego +modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowym +jest dość proste, podczas gdy zrobienie odwrotnie to już o wiele trudniejsze zadanie.

    + +

    Ze względu na fakt, że w JavaScript jest w zasadzie jedynym powszechnie stosowanym +językiem, któy posiada prototypowy model dziedziczenia, to wymaga troche czasu aby +dostosować się do różnic pomiędzy tymi dwoma modelami.

    + +

    Pierwszą znaczącą różnicą jest to, że dziedziczenie w JavaScript odbywa się za pomocą +tak zwanych łańcuchów prototypów.

    + + + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Ustawienie prototypu Bar na nową instancję Foo
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// Upewniamy się, że Bar jest ustawiony jako rzeczywisty konstruktor
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar() // tworzymy nową instancję Bar
    +
    +// The resulting prototype chain
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo] 
    +        { foo: 'Hello World' }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* etc. */ }
    +
    + +

    W powyższym przykładzie obiekt test będzie dziedziczył z obydwu tj. +Bar.prototyp i Foo.prototyp, stąd będzie miał dostęp do funkcji method, +która była zdefiniowana w Foo. Ponadto obiekt będzie miał dostęp do +właściwości value, która jest jednyną instancją Foo i stała się jego prototypem. +Ważne jest, aby pamiętać new Bar nie tworzy nowej instancji Foo, +ale wykorzystuje instancje, którą jest przypisana do własności prototype. +Zatem Wszystkie instancje Bar będą dzieliły tą samą własność value.

    + + + +

    Wyszukiwanie własności

    + +

    Podczas dostępu do właściwości obiektu, JavaScript przejdzie w górę łańcucha +prototypów dopóki nie znajdzie właściwości z żądaną nazwą.

    + +

    Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha mianowicie Object.prototype +i nadal nie znajdzie określonej właściwości, to zwróci wartość +undefined.

    + +

    Właściwość prototype

    + +

    Podczas gdy właściwość prototype jest używana przez język do budowania łańcucha +prototypów, istnieje możliwość przypisania do niej dowolnej wartości. Jednakże +prymitywne typy będą po prostu ignorowanie, jeżeli zostaną ustawione jako prototype.

    + +
    function Foo() {}
    +Foo.prototype = 1; // nie ma wpływu
    +
    + +

    Przypisywanie obiektów, jak pokazano w powyższym przykładzie, zadziała i pozwala +na dynamiczne tworzenie łańcuchów prototypów.

    + +

    Wydajność

    + +

    Czas wyszukiwania właściwości, które są na końcu łańcucha prototypów może mieć +negatywny wpływ na wydajność krytycznych części kodu. Dodatkowo, próba dostępu +do nieistniejącej właściwości powoduje zawsze przeszukanie całego łańcucha prototypów.

    + +

    Również, podczas iteracji po właściwościach obiektu +każda właściwość, która znajduje się w łańcuchu prototypów niezależnie +na jakim znajduje się poziomie zostanie wyliczona.

    + +

    Rozszerzanie natywnych prototypów

    + +

    Rozszerzanie Object.prototype lub innego prototypu wbudowanych typów jest jednym z +najczęściej używanych niedoskonałej częsci języka JavaScript.

    + +

    Technika ta nazywana jest monkey patching i łamie zasady enkapsulacji. +Jednak jest szeroko rozpowszechniona w frameworkach takich jak Prototype. +Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez dodawanie do nich +niestandardowych funkcjonalności.

    + +

    Jedynym dobrym powodem do rozszerzania wbudowanych prototypów jest portowanie
    +funkcjonalności znajdujących sie w nowszych silnikach JavaScript np. Array.forEach

    + +

    Wnioski

    + +

    Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczanie +należy całkowicie rozumieć prototypowy model dziedziczenia. Ponadto należy uważać +na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń +aby uniknąć problemów z wydajnością. Natywne prototypy nie powinny nigdy być +rozszerzane, chyba że ze względu na wprowadzanie kompatybilności z nowszymi silnikami +JavaScript.

    hasOwnProperty

    W celu sprawdzenia czy dana właściwość została zdefiniowana w tym obiekcie a nie +w łańcuchu prototypów niezbędne jest skorzystanie z metody +hasOwnProperty, która wszystkie obiekty dziedziczą z Object.prototype.

    + + + +

    hasOwnProperty jest jedyna metodą w języku JavaScript która operuje na właściwościach +i nie przegląda całego łańcucha prototypów.

    + +
    // Zatrucie Object.prototype
    +Object.prototype.bar = 1; 
    +var foo = {goo: undefined};
    +
    +foo.bar; // 1
    +'bar' in foo; // true
    +
    +foo.hasOwnProperty('bar'); // false
    +foo.hasOwnProperty('goo'); // true
    +
    + +

    Tylko hasOwnProperty da prawidłowy i oczekiwany rezultat. Jest to istotne podczas +iteracji po właściwościach obiektu. Nie ma innego sposobu na ominięcie +właściwości, która nie została zdefiniowana przez ten konkretny obiekt, +ale gdzieś indziej w łańcuchu prototypów.

    + +

    hasOwnProperty jako właściwość

    + +

    JavaScript nie chroni właściwości o nazwie hasOwnProperty, zatem istnieje +możliwość, że obiekt może posiadać tak nazwaną właściwość. Konieczne jest użycie +zewnętrznego hasOwnProperty, aby otrzymać poprawne rezultaty.

    + +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // zawsze zwraca false
    +
    +// Została użyta metoda innego obiektu i wywołana z konkekstem 
    +// `this` ustawionym na foo
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +
    + +

    Wnioski

    + +

    Jedyną metodą służącą do sprawdzenia zdefiniowania jakiejś właściwości w konkretnym +obiekcie jest metoda hasOwnProperty. Zaleca się korzystać z hasOwnProperty jako część +każdej pętli for in, pozwoli to uniknąć błędów pochodzących z +rozszerzonych natywnych prototypów.

    The for in Loop

    Podobnie jak operator in, pętla for in przeszukuje łańcuch prototypów +podczas iteracji po właściwościach obiektu.

    + + + +
    // Zatrucie Object.prototype
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // wyświetla obie właściwości: bar i moo
    +}
    +
    + +

    Ponieważ nie jest możliwe, aby zmienić zachowanie pętli for in to niezbędne +jest odfiltrowanie niechcianych właściwości wewnątrz ciała pętli, korzystając +z metody hasOwnProperty z Object.prototype.

    + + + +

    Korzystanie z hasOwnProperty do odfiltrowania

    + +
    // foo z przykładu powyżej
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +
    + +

    To jest jedyna poprawna wersja, którą należy używać. Ze względu na użycie +hasOwnProperty zostanie wypisane jedynie moo. Gdy opuścimy hasOwnProperty +kod będzie podatny na błędy, gdy natywne prototypy np. Object.prototype +zostanie rozszerzony.

    + +

    Prototype jest jednym z szeroko rozpowszechniony frameworków, który dokonuje +takiego rozszerzenia. Używanie tego frameworku oraz nie używanie w pętle for in +metody hasOwnProperty gwarantuje błędy w wykonaniu.

    + +

    Wnioski

    + +

    Zaleca się aby zawsze używać metody hasOwnProperty. Nigdy nie powinno się dokonywać +żadnych założeń na temat środowiska, w którym kod będzie wykonywany i czy natywne +prototypy zostały rozszerzone czy nie.

    Funkcje

    Deklaracje funkcji i wyrażenia funkcyjne

    Funcje w języku JavaScript są typami pierwszoklasowymi, co oznacza, że mogą +być przekazywane jak każda inna wartość. Jednym z typowych zastosowań tej cechy +jest przekazywanie anonimowej funkcji jako callback do innej, prawdopodobnie +asynchronicznej funkcji.

    + +

    Deklaracja funckcji

    + +
    function foo() {}
    +
    + +

    Powyższa funkcja zostaje wyniesiona zanim program wystartuje. Dzięki temu +jest dostępna wszędzie w ramach zasięgu, w którym została zadeklarowana, +nawet, jeżeli ta funkcja została wywołana przed faktyczną definicją w kodzie źródłowym.

    + +
    foo(); // Działa ponieważ definicja funkcji została wyniesiona 
    +       // na początek zasięgu przed uruchomieniem kodu
    +function foo() {}
    +
    + +

    Wyrażenie funkcyjne

    + +
    var foo = function() {};
    +
    + +

    Ten przykład przypisuje nienazwaną i anonimową funkcję do zmiennej foo.

    + +
    foo; // 'undefined'
    +foo(); // wyrzuca błąd TypeError
    +var foo = function() {};
    +
    + +

    Ze względu na fakt, że deklaracja var wynosi zmienną foo na początek zasięgu +zanim kod faktycznie zostanie uruchomiony, foo będzie zdefiniowane kiedy skrypt +będzie wykonywany.

    + +

    Ale ponieważ przypisania robione są dopiero podczas wykonania, wartość foo będzie +ustawiona na domyślną wartość undefined zanim powyższy kod +zostanie uruchomiony.

    + +

    Nazwane wyrażenia funkcyjne

    + +

    Kolejnym specjalnym przypadkiem jest przypisanie nazwanej funkcji.

    + +
    var foo = function bar() {
    +    bar(); // Działa
    +}
    +bar(); // wyrzuca ReferenceError
    +
    + +

    W zewnętrznym zakresie bar nie będzie dostępna, ponieważ funkcja zostaje +przypisana do foo, jednakże w wewnętrznym zakresie bar będzie dostępna. +Jest to spowodowane tym, jak działa rozwiązywanie nazw +w języku JavaScript. Nazwa funkcji jest zawsze dostępna w lokalnym +zakresie tej funkcji.

    Jak działa this

    JavaScript posiada inną koncepcję odnośnie tego na co wskazuje słowo kluczowe +this, niż większość innych języków programowania. Istnieje dokładnie +pięć różnych sytuacji, w których wartość this jest przypisana w języku JavaScript.

    + +

    JavaScript has a different concept of what the special name this refers to +than most other programming languages do. There are exactly five different +ways in which the value of this can be bound in the language.

    + +

    Zasięg globalny

    + +
    this;
    +
    + +

    Używanie this w globalnym zasięgu, zwróci po prostu referencję do obiektu global.

    + +

    Wywołanie funkcji

    + +
    foo();
    +
    + +

    Tutaj this również będzie wkazywało na obiekt global

    + + + +

    Wywoływanie metody

    + +
    test.foo(); 
    +
    + +

    W tym przypadku this będzie wskazywało na test.

    + +

    Wywołanie konstruktora

    + +
    new foo(); 
    +
    + +

    Wywołanie funkcji, które jest poprzedzone słowem kluczowym new, zachowuje się +jak konstruktor. Wewnątrz funkcji this będzie +wskazywało na nowo utworzony obiekt.

    + +

    Jawne ustawienie this

    + +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // tablica zostanie zamieniona w to co poniżej
    +foo.call(bar, 1, 2, 3); // rezultat a = 1, b = 2, c = 3
    +
    + +

    Używając metod call lub apply z prototypu Function.prototype, wartość this +wewnątrz wołanej funkcji zostanie jawnie ustawiona na pierwszy argument przekazany +podczas wywołania tych metod.

    + +

    Zatem w powyższym przykładzie przypadek Wywoływanie metody nie będzie miał +miejsca i this wewnątrz foo będzie wskazywać na bar.

    + + + +

    Częste pułapki

    + +

    Mimo iż Większość z tych przypadków ma sens, to pierwszy przypadek powinien być +traktorany jako błąd podczas projektowania języka i nigdy nie wykorzystywany +w praktyce.

    + +
    Foo.method = function() {
    +    function test() {
    +        // wewnątrz tej funkcji this wskazuje na obiekt global
    +    }
    +    test();
    +}
    +
    + +

    Powszechnym błędem jest myślenie, że this wewnątrz test wskazuje na Foo, +podczas gdy w rzeczywistości tak nie jest.

    + +

    Aby uzyskać dostęp do Foo wewnątrz test, niezbędne jest stworzenie wewnątrz +metody lokalnej zmiennej, która będzie wskazywała na Foo.

    + +
    Foo.method = function() {
    +    var that = this;
    +    function test() {
    +        // Należy używać that zamiast this wewnątrz tej funkcji
    +    }
    +    test();
    +}
    +
    + +

    that jest zwykłą zmienną, ale jest to powszechnie stosowana konwencja otrzymywania
    +wartości zewnętrznego this. W połączeniu z domknięciami(closures), +jest to sposób na przekazywanie wartości this wokół.

    + +

    Metody przypisywania

    + +

    Kolejną rzeczą, która nie działa w języku JavaScript, jest nadawanie aliasów +funkcjom, co oznacza przypisanie metody do zmiennej.

    + +
    var test = someObject.methodTest;
    +test();
    +
    + +

    Podobnie jak w pierwszym przypadku test zachowuje się jak wywołanie zwykłej +funkcji, a zatem wewnątrz funkcji this już nie będzie wskazywało someObject.

    + +

    Podczas gdy późne wiązanie this może się na początku wydawać złym pomysłem, +to w rzeczywistości jest to rzecz, która sprawia, że +dziedziczenie prototypowe działa.

    + +
    function Foo() {}
    +Foo.prototype.method = function() {};
    +
    +function Bar() {}
    +Bar.prototype = Foo.prototype;
    +
    +new Bar().method();
    +
    + +

    Kiedy metoda method zostanie wywołana na instancji Bar, this będzie +wskazywało właśnie tę instancję.

    Domknięcia i referencje

    Jedną z najpotężniejszych funkcjonalności języka JavaScript są domknięcia. +Oznacza to że zasięg zawsze posiada dostęp do zewnętrznego zasięgu, w którym +został zdefiniowany. Ponieważ zasięg w JavaScript można definiować tylko poprzez +funckję, wszystkie funkcje domyślnie zachowują się jak domknięcia.

    + +

    Emulowanie prywatnych zmiennych

    + +
    function Counter(start) {
    +    var count = start;
    +    return {
    +        increment: function() {
    +            count++;
    +        },
    +
    +        get: function() {
    +            return count;
    +        }
    +    }
    +}
    +
    +var foo = Counter(4);
    +foo.increment();
    +foo.get(); // 5
    +
    + +

    Tutaj Counter zwraca dwa domknięcia: funkcję increment oraz funkcję get. +Obie te funkcje trzymają referencję do zasięgu Counter, a co za tym idzie +zawsze posiadają dostęp do zmiennej count tak, jakby ta zmienna była zdefiniowana +w zasięgu tych funkcji.

    + +

    Dlaczego zmienne prywatne działają?

    + +

    Ponieważ nie ma możliwości wskazania lub przypisania zasięgu w JavaScript, +nie istnieje sposób, aby uzyskać dostęp do zmiennej count z zewnątrz. +Wykorzystanie tych dwóch domknięć jest jedynym sposobem na interakcję z tą zmienną.

    + +
    var foo = new Counter(4);
    +foo.hack = function() {
    +    count = 1337;
    +};
    +
    + +

    Powyższy kod nie zmieni wartości zmiennej count wewnątrz zasięgu Counter, +ponieważ foo.hack nie została zadeklarowana wewnątrz tego konkretnego zasięgu. +Zamiast tego funkcja utworzy lub nadpisze globalną zmienną count.

    + +

    Domknięcia wewnątrz pętli

    + +

    Jednym z częstrzych błędów jest wykorzystywanie domknięć wewnątrz pętli, +aby wartość zmiennej po której odbywa się iteracja była kopiowana do +wewnętrznej funkcji.

    + +
    for(var i = 0; i < 10; i++) {
    +    setTimeout(function() {
    +        console.log(i);  
    +    }, 1000);
    +}
    +
    + +

    Powyższy kod nie wypisze numerów od 0 do 9, ale wypisze +dziesięć razy liczbę 10.

    + +

    Anonimowa funkcja trzyma wskaźnik do zmiennej i i podczas uruchomienia +console.log, pętla for już zakończyła działanie i wartość zmiennej i +została ustawiona na 10.

    + +

    Aby otrzymać zamierzony efekt, niezbędne jest skopiowanie wartości +zmiennej i.

    + +

    Unikanie problemu z referencją

    + +

    Aby skopiować wartość zmiennej, po której iterujemy w pętli, należy skorzystać +z anonimowego wrappera.

    + +
    for(var i = 0; i < 10; i++) {
    +    (function(e) {
    +        setTimeout(function() {
    +            console.log(e);  
    +        }, 1000);
    +    })(i);
    +}
    +
    + +

    Zewnętrzna anonimowa funkcja zostanie wywołana od razu z parametrem i +jako pierwszym argumentem oraz otrzyma kopię wartości zmiennej i jako +zmienną e.

    + +

    Anonimowa funkcja która zostaje przekazana do setTimeout teraz posiada +referencję do zmiennej e, która nie zostanie zmieniona przez pętle for.

    + +

    Istnieje jeszcze jeden sposób na osiągnięcie tego samego efektu. Należy zwrócic +fukcję z anonimowego wrappera, wówczas kod będzie zachowywał się jak ten +wcześniejszy.

    + +
    for(var i = 0; i < 10; i++) {
    +    setTimeout((function(e) {
    +        return function() {
    +            console.log(e);
    +        }
    +    })(i), 1000)
    +}
    +

    Obiekt arguments

    Każdy zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej arguments. +Ta zmienna trzyma listę wszystkich argumentów przekazanych do funkcji.

    + + + +

    Obiekt arguments nie jest typu Array. Mimo że posiada pewne cechy +semantyki tablic - właściwość length - to w rzeczywistości nie dziedziczy +on z Array.prototype, tylko z Object.

    + +

    Ze względu na to, na obiekcie arguments nie można używać standardowych dla tablic metod, +takich jak push, pop czy slice. Mimo że iteracja przy pomocy +pętli for działa dobrze, to aby skorzystać ze standardowych metod tablicowych +należy skonwertować arguments do prawdziwego obiekt Array.

    + +

    Konwersja do tablicy

    + +

    Poniższy kod zwróci nowy obiekt Array zawierający wszystkie elementy +obiektu arguments.

    + +
    Array.prototype.slice.call(arguments);
    +
    + +

    Jednakże konwersja ta jest wolna i nie jest zalecana w sekcjach, +które mają duży wpływ na wydajność.

    + +

    Przekazywanie argumentów

    + +

    Zalecany sposób przekazywania argumentów z jednej funkcji do następnej +wyglada następująco:

    + +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // do stuff here
    +}
    +
    + +

    Kolejną sztuczką jest użycie razem call i apply w celu stworzenia +szybkich i nieograniczonych wrapperów.

    + +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// Stworzenie nieograniczoną wersję metody "method" 
    +// która przyjmuje parametry: this, arg1, arg2...argN
    +Foo.method = function() {
    +
    +    // Rezultat: Foo.prototype.method.call(this, arg1, arg2... argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +};
    +
    + +

    Parametry formalne i indeksy argumentów

    + +

    Obiekt arguments tworzy funkcje getter i setter nie tylko dla swoich +właściwości, ale również dla parametrów formalnych funkcji.

    + +

    W rezultacie zmiana wartości parametru formalnego zmieni również wartość +odpowiadającemu mu wpisowi w obiekcie arguments. Zachodzi to również w drugą stronę.

    + +
    function foo(a, b, c) {
    +    arguments[0] = 2;
    +    a; // 2                                                           
    +
    +    b = 4;
    +    arguments[1]; // 4
    +
    +    var d = c;
    +    d = 9;
    +    c; // 3
    +}
    +foo(1, 2, 3);
    +
    + +

    Mity i prawdy o wydajności

    + +

    Obiekt arguments jest tworzony zawsze, z wyjątkiem dwóch przypadków, gdy +zmienna o takiej nazwie jest zdefiniowana wewnątrz funkcji lub jeden z parametrów +formalnych funkcji ma taką nazwę. Nie ma znaczenia czy obiekt arguments jest +używany czy nie.

    + +

    Zarówno gettery jak i settery są zawsze tworzone, zatem używanie ich nie ma +praktycznie żadnego wpływu na wydajność. Zwłaszcza w rzeczywistym kodzie, który +wykorzystuje coś więcej niż tylko prosty dostęp do właściwości obiektu arguments.

    + + + +

    Jednakże, istnieje jeden przypadek w którym wydajność drastycznie spada w +nowoczesnych silnikach JavaScript. Ten przypadek to wykorzystanie +arguments.callee.

    + +
    function foo() {
    +    arguments.callee; // operowanie na obiekcie funkcji
    +    arguments.callee.caller; // i obiekcie funkcji wywołującej
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // Normalnie zostałaby wykorzystana metoda inline
    +    }
    +}
    +
    + +

    W powyższym przykładzie foo nie może zostać wykorzystana metoda inline +ponieważ potrzebne są nie tylko informacje na własny temat ale również +na temat funkcji wywołującej. Takie użycie nie tylko uniemożliwia +inlining i korzyści z niego wynikające, ale też łamie zasady enkapsulacji, +ponieważ ta funkcja jest zależna od kontekstu w jakim została wywołana.

    + +

    Mocno zalecane jest aby nigdy nie korzystać z arguments.callee +i żadnej jej własności.

    + +

    Konstruktory

    Konstruktory w JavaScript również wyglądają inaczej niż innych językach. Każde +wywołanie funkcji, które jest poprzedone słowem kluczowym new, zachowuje się +jak konstruktor.

    + +

    Wewnątrz konstruktora - wywoływanej fukcji - wartość this wskazuje na +nowo utworzony obiekt Object. Prototyp prototype tego +nowego obiektu będzie wskazywał na prototyp prototype obiektu fukcji, +która została wywołana jako konstruktor.

    + +

    Jeżeli wywołana funkcja nie posiada jawnej deklaracji return, wówczas +fukcja domyślnie zwraca wartość this - nowy obiekt.

    + +
    function Foo() {
    +    this.bla = 1;
    +}
    +
    +Foo.prototype.test = function() {
    +    console.log(this.bla);
    +};
    +
    +var test = new Foo();
    +
    + +

    Powyżej wywołanya została funkcja Foo jako konstruktor oraz ustawia +nowo utworzonemu obiektowi właściwość prototype na Foo.prototype.

    + +

    W tym przypadku jawna deklaracja return w funkcji zwraca wartość +ustawioną w deklaracji, ale tylko jeżeli zwracaną wartością jest +obiekt Object.

    + +
    function Bar() {
    +    return 2;
    +}
    +new Bar(); // nowy obiekt
    +
    +function Test() {
    +    this.value = 2;
    +
    +    return {
    +        foo: 1
    +    };
    +}
    +new Test(); // zwrócony obiekt
    +
    + +

    Jeżeli słowo kluczowe new zostanie pominięte, funkcja nie zwróci nowego +obiektu.

    + +
    function Foo() {
    +    this.bla = 1; // zostanie ustawiona w obiekcie global
    +}
    +Foo(); // undefined
    +
    + +

    Mimo że powyższy kod może zadziałać w pewnych przypadkach, w związku +z działaniem this w języku JavaScript, to jako +wartość thiszostanie wykorzystany obiekt global.

    + +

    Fabryki

    + +

    Aby móc ominąć słowo kluczowe new, konstruktor musi jawnie zwracać wartość.

    + +
    function Bar() {
    +    var value = 1;
    +    return {
    +        method: function() {
    +            return value;
    +        }
    +    }
    +}
    +Bar.prototype = {
    +    foo: function() {}
    +};
    +
    +new Bar();
    +Bar();
    +
    + +

    Oba wywołania Bar zwrócą tę samą rzecz, nowo utworzony obiekt, który posiada +właściwość nazwaną method i dla którego Bar jest Domknięciem.

    + +

    Należy również pamiętać, że wywołanie new Bar() nie ma wpływu na +prototyp zwróconego obiektu (prototypem będzie object.prototype a nie Bar.prototype). +Kiedy prototyp zostanie przypisany do nowo utworzonego obiektu, Bar nidgy +nie zwróci tego nowego obiektu Bar, tylko literał obiektu, który jest po +słowie kluczowym return.

    + +

    W powyższym przykładzie nie ma żadnej różnicy w działaniu pomiędzy użyciem +i nieużyciem słowa kluczowego new.

    + +

    Tworzenie nowych obiektów korzystając z fabryk

    + +

    Często zaleca się nie korzystać z operatora new, ponieważ zapominanie +o jego stosowaniu może prowadzić do błędów.

    + +

    W celu stworzenia nowego obiektu, powinno się używać fabryki i konstruować +nowy obiekt wewnątrz tej fabryki.

    + +
    function Foo() {
    +    var obj = {};
    +    obj.value = 'blub';
    +
    +    var private = 2;
    +    obj.someMethod = function(value) {
    +        this.value = value;
    +    }
    +
    +    obj.getPrivate = function() {
    +        return private;
    +    }
    +    return obj;
    +}
    +
    + +

    Mimo że powyższy kod jest odporny na brak słowa kluczowego new i ułatwia +korzystanie ze zmiennych prywatnych, to posiada +pewne wady. +While the above is robust against a missing new keyword and certainly makes +the use of private variables easier, it comes with some +downsides. + 1. Zużywa więcej pamięci, ponieważ tworzony obiekt nie współdzieli metod + poprzez prototyp. + 2. Aby móc dziedziczyć fabryka musi skopiować wszystkie metody z dziedziczonego + obiektu lub przypisać ten obiekt, z którego się dziedziczy, jako prototyp + do nowo utworzonego obiektu. + 3. Porzucenie łańcucha prototypów tylko ze względu na opuszczone słowo kluczowe + new jest sprzeczne z duchem języka.

    + +

    Wnioski

    + +

    Pominięcie słowa kluczowego new może prowadzić do błędów, ale na pewno nie +powinno to być powodem odrzucenia używania prototypów w ogóle. Sprowadza się to +do wyboru rozwiązania, które bardziej pasuje do potrzeb aplikacji. Szczególnie +ważne jest, aby wybrać określony styl tworzenia obiektów i trzymać się go.

    Zasięg zmiennych i przestrzenie nazw

    Mimo że JavaScript radzi sobie dobrze ze składnią opisującą dwa pasujące +nawiasy klamrowe jako blok, to jednak nie wspiera zasięgu blokowego. +Jedynym zasięgiem jaki istnieje w JavaScript jest zasięg funkcyjny.

    + +
    function test() { // definiuje zasięg (scope)
    +    for(var i = 0; i < 10; i++) { // nie definiuje zasięgu (scope)
    +        // count
    +    }
    +    console.log(i); // 10
    +}
    +
    + + + +

    W JavaScripcie nie ma również przestrzeni nazw, co oznacza, że wszystko jest +definiowane w jednej globalnie współdzielonej przestrzeni nazw.

    + +

    Z każdym odwołaniem do zmiennej, JavaScript przeszukuje w górę wszystkie zasięgi +dopóki nie znajdzie tej zmiennej. W przypadku, gdy przeszukiwanie dotrze do globalnego +zasięgu i nadal nie znajdzie żądanej nazwy, to wyrzuca błąd ReferenceError.

    + +

    Zmora globalnych zmiennych

    + +
    // script A
    +foo = '42';
    +
    +// script B
    +var foo = '42'
    +
    + +

    Powyższe dwa skrypty nie dają tego samego efektu. Skrypt A definiuje zmienną +nazwaną foo w globalnym zasięgu, natomiast skrypt B definiuje foo +w aktualnym zasięgu.

    + +

    Jeszcze raz, to wcale nie daje tego samego efektu. Nie użycie var może mieć +poważne konsekwencje.

    + +
    // globalny zasięg
    +var foo = 42;
    +function test() {
    +    // lokalny zasięg
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +
    + +

    Pominięcie słowa var w deklaracji wewnątrz funkcji test nadpisze wartość +zmiennej globalnej foo. Mimo że nie wygląda to na początku na duży problem, +posiadanie wielu tysięcy linii kodu w JavaScript i nie korzystanie z var +wprowadzi straszne i trudne do wyśledzenia błędy.

    + +
    // globalny zasięg 
    +var items = [/* jakaś lista */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // scope of subLoop
    +    for(i = 0; i < 10; i++) { // brakuje słowa var w deklaracji
    +        // do amazing stuff!
    +    }
    +}
    +
    + +

    Zewnętrzna pętla zakończy działanie po pierwszym wywołaniu subLoop, ponieważ +subLoop nadpisuje wartość globalnej zmiennej i. Użycie var w drugiej pętli +for pozwoliłoby łatwo uniknąć problemu. Słowo kluczowe var nie powinno być +nigdy pominięte w deklaracji, chyba że pożądanym skutkiem jest wpłynięcie na +zewnętrzny zasięg.

    + +

    Lokalne zmienne

    + +

    Jedynym źródłem zmiennych lokalnych w JavaScripcie są parametry funkcji +oraz zmienne zadeklarowane poprzez deklaracje var wewnątrz funkcji.

    + +
    // globalny zasięg
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // lokalny zasięg fukcji test
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +
    + +

    Zmienne foo oraz i są lokalnymi zmiennymi wewnątrz zasiegu funkcji test, +natomiast przypisanie wartości do bar nadpisze zmienną globalną o tej samej nazwie.

    + +

    "Hoisting" - wywindowanie, podnoszenie

    + +

    JavaScript winduje deklaracje. Oznacza to, że zarówno deklaracja ze słowem +kluczowym var jak i deklaracje funkcji function zostaną przeniesione na +początek otaczającego zasięgu.

    + +
    bar();
    +var bar = function() {};
    +var someValue = 42;
    +
    +test();
    +function test(data) {
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        var goo = 2;
    +    }
    +    for(var i = 0; i < 100; i++) {
    +        var e = data[i];
    +    }
    +}
    +
    + +

    Powyższy kod zostanie przekształcony przed rozpoczęciem wykonania. JavaScript +przeniesie deklarację zmiennej var oraz deklarację funkcji function na szczyt +najbliższego zasięgu.

    + +
    // deklaracje var zostaną przeniesione tutaj
    +var bar, someValue; // ustawione domyślnie na 'undefined'
    +
    +// deklaracje funkcji zostaną również przeniesione na górę
    +function test(data) {
    +    var goo, i, e; // brak blokowego zasięgu spowoduje przeniesienie tutaj
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // powoduje błąd TypeError ponieważ bar jest nadal 'undefined'
    +someValue = 42; // przypisania nie zostają zmienione przez 'hoisting'
    +bar = function() {};
    +
    +test();
    +
    + +

    Brak blokowego zasięgu nie tylko przeniesie deklaracje var poza ciało pętli, +ale również spowoduje, że niektóre porównania if staną się nieintuicyjne.

    + +

    W oryginalnym kodzie instrukcja warunkowa if zdaje się modyfikować zmienną +globalną goo, podczas gdy faktycznie modyfikuje ona zmienną lokalną - po tym +jak zostało zastosowane windowanie (hoisting).

    + +

    Bez wiedzy na temat podnoszenia (hoistingu), poniższy kod może sprawiać wrażenie, +że zobaczymy błąd ReferenceError.

    + +
    // sprawdz czy SomeImportantThing zostało zainicjalizowane
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +
    + +

    Oczywiście powyższy kod działa ze względu na fakt, że deklaracja var zostanie +przeniesiona na początek globalnego zasięgu.

    + +
    var SomeImportantThing;
    +
    +// inny kod który może ale nie musi zainicjalizować SomeImportantThing
    +
    +// upewnienie sie, że SomeImportantThing zostało zainicjalizowane
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +
    + +

    Kolejność rozwiązywania nazw

    + +

    Wszystkie zasięgi w JavaScripcie, włączając globalny zasięg, posiadają +zdefiniowaną wewnątrz specjalną nazwę this, która wskazuje +na aktualny obiekt.

    + +

    Zasięg funkcyjny posiada również zdefiniowaną wewnętrznie nazwę +arguments, która zawiera listę argumentów przekazaną do +funkcji.

    + +

    Na przykład, kiedy próbujemy odczytać zmienną foo wewnątrz zasięgu funkcji, +JavaScript będzie szukać nazwy w określonej kolejności: + 1. Jeżeli wewnątrz aktualnego zasięgu znajduje się deklaracja var foo skorzystaj z niej. + 2. Jeżeli jeden z parametrów fukcji został nazwany foo użyj go. + 3. Jeżeli fukcja została nazwana foo skorzystaj z tego. + 4. Przejdz do zewnętrznego zasięgu i przejdz do kroku #1.

    + + + +

    Przestrzenie nazw

    + +

    Powszechnym problemem posiadania tylko jednej globalnej przestrzeni nazw jest +prawdopodobieństwo wystąpienia kolizji nazw. W JavaScripcie, można łatwo uniknąć +tego problemu korzystając z anonimowych wrapperów.

    + +
    (function() {
    +    // autonomiczna "przestrzeń nazw"
    +
    +    window.foo = function() {
    +        // wyeksponowane domkniecie (closure)
    +    };
    +
    +})(); // natychmiastowe wykonanie funkcji
    +
    + +

    Anonimowe funkcje są rozpoznane jako wyrażenia, więc +aby mogły zostać wywołane muszą zostać zewaluowane.

    + +
    ( // zewaluowanie funkcji znajdującej się wewnątrz nawiasów
    +function() {}
    +) // zwrócenie obiektu funkcji
    +() // wywołanie rezultatu ewaluacji
    +
    + +

    Istnieją inne sposoby aby zewaluować i wykonać wyrażenie funkcyjne. Mimo że +mają inną składnię, zachowują się dokładnie tak samo.

    + +
    // Trzy inne sposoby
    +!function(){}();
    ++function(){}();
    +(function(){}());
    +
    + +

    Wnioski

    + +

    Zaleca się, aby zawsze używać anonimowych wrapperów do hermetyzacji kodu wewnątrz +jego własnej przestrzeni nazw. To nie tylko chroni kod przed kolizją nazw, ale +również wprowadza lepszą modularyzację programów.

    + +

    Ponadto, stosowanie zmiennych globalnych jest uznawane za złą praktykę. +Wykorzystanie zmiennych globalnych wskazuje na źle napisany kod, który +jest podatny na błędy i trudny do utrzymania.

    Tablice

    Iterowanie po tablicach oraz właściwościach tablic

    Mimo że tablice w JavaScript są obiektami, nie ma dobrych powodów aby używać +pętli for in do iteracji po nich. W rzeczywstości istnieje +wiele dobrych powodów przeciwko wykorzystaniu for in na tablicach.

    + + + +

    Ponieważ pętla for in wylicza wszystkie właściwości, które są wewnątrz +łańcucha prototypów i jedynym sposobem aby wykluczyć te właściwości jest użycie +hasOwnProperty, ale wówczas pętla staje się +dwadzieście razy wolniejsza od normalnej pętli for.

    + +

    Iteracja

    + +

    W celu osiągnięcia najlepszej wydajności podczas iteracji po tablicach należy +użyć klasycznej pętli for.

    + +
    var list = [1, 2, 3, 4, 5, ...... 100000000];
    +for(var i = 0, l = list.length; i < l; i++) {
    +    console.log(list[i]);
    +}
    +
    + +

    W powyższym przykładzie jest jeszcze jeden dodatkowy haczyk. Jest to zbuforowanie +długości tablicy poprzez l = list.length.

    + +

    Mimo że właściwość length jest zdefiniowana wewnątrz tablicy, istnieje nadal +dodatkowy koszt na wyszukiwanie tej właściwości przy każdej iteracji w pętli. +Chociaż najnowsze silniki JavaScript mogą zastosować w tym +przypadku optymalizację. Nie ma jednak możliwość ustalenia czy kod będzie wykonywany w jednym +z tych nowych silników, czy też nie.

    + +

    W rzeczywistości pominięcie buforowania długości tablicy może spowodować, że pętla +będzie tylko w połowie tak szybka jak ta z buforowaniem długości.

    + +

    Właściwość length

    + +

    Mimo, że getter właściwości length zwraca po prostu liczbę elementów, które są +zawarte w tablicy, to setter może być użyty do skracania tablicy.

    + +
    var foo = [1, 2, 3, 4, 5, 6];
    +foo.length = 3;
    +foo; // [1, 2, 3]
    +
    +foo.length = 6;
    +foo; // [1, 2, 3]
    +
    + +

    Przypisanie mniejszej długości spowoduje skrócenie tablicy, ale zwiększenie wartości +length nie ma żadnego wpływu na tablicę.

    + +

    Wnioski

    + +

    Aby uzyskać najlepszą wydajność zaleca się, aby zawsze używać zwykłej pętli for +i zbuforowanie właściwości length. Korzystanie z pętli for in na tablicy jest +oznaką źle napisanego kodu, który jest podatny na błędy i ma słabą wydajność.

    Konstruktor Array

    Zaleca się zawsze korzystać z literału tablicy - notacja [] - podczas tworzenia +nowych tablic, ponieważ konstruktor Array niejednoznacznie interpretuje +przekazane do niego parametry.

    + +
    [1, 2, 3]; // Rezultat: [1, 2, 3]
    +new Array(1, 2, 3); // Rezultat: [1, 2, 3]
    +
    +[3]; // Rezultat: [3]
    +new Array(3); // Rezultat: []
    +new Array('3') // Rezultat: ['3']
    +
    + +

    W przypadku gdy tylko jeden argument zostanie przekazany do kostruktora Array i +ten argument jest typu Number, konstruktor zwróci nową dziwną tablicę +z ustawioną właściwością length na wartość przekazaną jako argument. Należy +zauważyć, że tylko właściwość length zostanie ustawiona w ten sposób. +Rzeczywiste indeksy w tej tablicy nie zostaną zainicjalizowane.

    + +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // zwraca false, indeks nie został ustawiony
    +
    + +

    Możliwość ustalenia z góry długości tablicy jest użyteczna tylko w kilku +przypadkach, jak np. powtarzanie ciągu znaków, w którym unika się stosowania +pętli for.

    + +
    // count - ilosc powtorzen
    +// stringToRepeat - ciąg znaków do powtórzenia 
    +new Array(count + 1).join(stringToRepeat); 
    +
    + +

    Wnioski

    + +

    W miarę możliwości należy unikać używania konstruktora Array. Literały są +zdecydowanie lepszym rozwiązaniem. Są krótsze i mają bardziej precyzyjną składnię. +Zwiększają również czytelność kodu.

    Typy

    Równość i porównania

    JavaScript posiada dwa różne sposoby równościowego porównywania obiektów.

    + +

    Operator równości

    + +

    Operator równości składa się z dwóch znaków "równa się": ==

    + +

    JavaScript jest słabo typowanym językiem. Oznacza to, że operator równości +konwertuje typy (dokonuje koercji), aby wykonać porównanie.

    + +
    ""           ==   "0"           // false
    +0            ==   ""            // true
    +0            ==   "0"           // true
    +false        ==   "false"       // false
    +false        ==   "0"           // true
    +false        ==   undefined     // false
    +false        ==   null          // false
    +null         ==   undefined     // true
    +" \t\r\n"    ==   0             // true
    +
    + +

    Powyższa tabela przedstawia wyniki koercji typów. Nieprzewidywalne wyniki +porównania są głównym powodem, że stosowanie == jest powszechnie uważane za złą +praktykę. Skomplikowane reguły konwersji są powodem trudnych do wyśledzenia błędy.

    + +

    Ponadto koercja ma również wpływ na wydajność na przykład gdy typ String musi zostać +przekształcony na typ Number przed porównaniem z drugą liczbą.

    + +

    Operator ścisłej równości

    + +

    Operator ścisłej równości składa się z trzech znaków "równa się": ===

    + +

    Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem nie +dokonuje koercji typów przed porównaniem.

    + +
    ""           ===   "0"           // false
    +0            ===   ""            // false
    +0            ===   "0"           // false
    +false        ===   "false"       // false
    +false        ===   "0"           // false
    +false        ===   undefined     // false
    +false        ===   null          // false
    +null         ===   undefined     // false
    +" \t\r\n"    ===   0             // false
    +
    + +

    Powyższe rezultaty są o wiele bardziej przejrzyste. Powoduje to "ustatycznienie" +języka do pewnego stopnia oraz pozwala na wprowadzenie optymalizacji porównań +obiektów o różnych typach.

    + +

    Porównywanie obiektów

    + +

    Mimo, że oba operatory == i === nazywane są operatorami równościowymi, +to zachowują się różnie gdy jednym z operandów jest obiekt typu Object.

    + +
    {} === {};                   // false
    +new String('foo') === 'foo'; // false
    +new Number(10) === 10;       // false
    +var foo = {};
    +foo === foo;                 // true
    +
    + +

    Oba operatory porównują toższmość a nie równość, czyli będą porównywać czy +jeden i drugi operand jest tą samą instancją obiektu, podobnie jak operator +is w Pythonie i porównanie wskaźników w C.

    + +

    Wnioski

    + +

    Zaleca się aby używać tylko operatora ścisłej równości. W sytuacjach gdy +potrzebna jest koercja (porównanie obiektów różnych typów), konwersja powinna +być dokonana jawnie a nie pozostawiona trudnym regułom koercji +obowiązującym w języku.

    Operator typeof

    Operator typeof (razem z operatorem instanceof) jest +prawdopodobnie najwiekszą wadą konstrukcji języka JavaScript, jest on praktycznie +całkowicie wadliwy.

    + +

    Mimo, że instanceof ma swoje wady to nadal ma ograniczone zastosowanie w praktyce, +natomiast typeof ma tylko jeden praktyczny przypadek użycia, który na dodatek +nie jest związany z sprawdzaniem typu obiektu.

    + + + +

    Tablica typów JavaScript

    + +
    Wartość             Klasa      Typ
    +-------------------------------------
    +"foo"               String     string
    +new String("foo")   String     object
    +1.2                 Number     number
    +new Number(1.2)     Number     object
    +true                Boolean    boolean
    +new Boolean(true)   Boolean    object
    +new Date()          Date       object
    +new Error()         Error      object
    +[1,2,3]             Array      object
    +new Array(1, 2, 3)  Array      object
    +new Function("")    Function   function
    +/abc/g              RegExp     object (function w Nitro i V8)
    +new RegExp("meow")  RegExp     object (function w Nitro i V8)
    +{}                  Object     object
    +new Object()        Object     object
    +
    + +

    W powyższej tabeli Typ odnosi się do wartości zwracanej przez operator typeof. +Wyraźnie widać, że zwracane wartości w ogóle nie są spójne.

    + +

    Klasa odnosi sie do wartości wewnętrznej właściwości [[Class]] obiektu.

    + + + +

    W celu uzyskania wartości właściwości [[Class]] trzeba skorzystać z metody +toString z Object.prototype.

    + +

    Klasa obiektu

    + +

    Specyfikacja zawiera dokładnie jeden sposób dostepu do wartości [[Class]], +wykorzystując Object.prototype.toString.

    + +
    function is(type, obj) {
    +    var clas = Object.prototype.toString.call(obj).slice(8, -1);
    +    return obj !== undefined && obj !== null && clas === type;
    +}
    +
    +is('String', 'test'); // true
    +is('String', new String('test')); // true
    +
    + +

    Powyższy przykład wywołuje Object.prototype.toString z wartością +this ustawioną na obiekt, dla której wartość właściwości +[[Class]] ma zostać odczytana.

    + + + +

    Testowanie niezdefiniowania zmiennej

    + +
    typeof foo !== 'undefined'
    +
    + +

    Powyższy kod sprawdza czy foo została faktycznie zadeklarowana czy też nie. +Próba odwołania się do zmiennej spowodowała by wyrzucenie błędu ReferenceError. +Jest to jedyne praktyczne wykorzystanie operatora typeof.

    + +

    Wnioski

    + +

    W celu sprawdzenia typu obiektu zalecane jest skorzystanie z +Object.prototype.toString, ponieważ jest to jedyny wiarygodny sposób. Jak +pokazano w powyższej tabeli typów, niektóre wartości zwracane przez typeof nie +są zdefiniowane w specyfikacji, co za tym idzie mogą się różnić w różnych +implementacjach.

    + +

    O ile nie operator typeof nie jest użyty do sprawdzania czy zmienna została +zdefiniowana, powinien być unikany o ile to tylko możliwe.

    Operator instanceof

    Operator instanceof porównuje konstruktory obiektów przekazanych jako operendy. +Jest on jedynie użyteczny do porównywania obiektów utworzonych klas. Stosowanie +go na wbudowanych typach jest praktycznie tak samo bezużyteczne jak operatora +typeof.

    + +

    Porównywanie obiektów utworzonych klas

    + +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // true
    +new Bar() instanceof Foo; // true
    +
    +// Poniżej kod który przypisuje do Bar.prototype obiekt funkcji Foo
    +// a nie faktyczną instancję Foo
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // false
    +
    + +

    Stosowanie instanceof na natywnych typach

    + +
    new String('foo') instanceof String; // true
    +new String('foo') instanceof Object; // true
    +
    +'foo' instanceof String; // false
    +'foo' instanceof Object; // false
    +
    + +

    Jedną ważną rzeczą, którą należy zauważyć jest to, że instanceof nie zadziała +na obiektach, które pochodzą z różnych kontekstów JavaScript (np. z różnych +dokumentów wewnątrz przeglądarki), ponieważ ich konstruktory nie będą tymi +samymi obiektami.

    + +

    Wnioski

    + +

    Operator instanceof powinien być tylko używany podczas korzystania z obiektów +klas utworzonych, które były zdefiniowane w tym samym kontekscie JavaScriptowym. +Podobnie jak operator typeof, należy unikać korzystania +z tego operatora w innych sytuacjach.

    Rzutowanie typów

    JavaScript jest językiem słabo typowanym, co za tym idzie będzie stosować koercję +typów gdziekolwiek jest to możliwe.

    + +
    // These are true
    +new Number(10) == 10; // Number.toString() zostanie przekształcone
    +                      // z powrotem do liczby
    +
    +10 == '10';           // Stringi zostaną przekształcone do typu Number
    +10 == '+10 ';         // Kolejne wariacje
    +10 == '010';          // i następne
    +isNaN(null) == false; // null zostanie przekształcony do 0
    +                      // który oczywiście nie jest NaN
    +
    +// Poniższe zwracają false
    +10 == 010;
    +10 == '-10';
    +
    + + + +

    Aby uniknąć powyższych problemów, należy koniecznie skorzystać ze +ściełego operatora równości. Mimo, że pozwala to uniknąć wiele +typowych problemów to nadal istnieje wiele innych, które powstają na bazie słabego +typowania języka JavaScript.

    + +

    Konstruktory typów wbudowanych

    + +

    Konstruktory typów wbudowanych takich, jak Number lub String zachowują się +inaczej jeżeli są poprzedzone słowem kluczowym new a inaczej jeżeli nie są.

    + +
    new Number(10) === 10;     // False, Object i Number
    +Number(10) === 10;         // True, Number i Number
    +new Number(10) + 0 === 10; // True, ponieważ dokonano jawnej konwersji
    +
    + +

    Korzystanie z wbudowanych typów jak Number jako konstruktor utworzy nowy obiekt +typu Number, natomiast opuszczenie słowa kluczowego new spowoduje, że funkcja +Number zachowa się jak konwerter.

    + +

    Ponadto, użycie literałów lub wartości nieobiektowych zaowocuje jeszcze większą +ilością rzutowań (koercją) typów.

    + +

    Najlepszym rozwiązaniem jest jawne rzutowanie do jednego z trzech typów.

    + +

    Rzutowanie do typu String

    + +
    '' + 10 === '10'; // true
    +
    + +

    Konkatenacja pustego stringu i wartości powoduje rzutowanie do typu String.

    + +

    Rzutowanie do typu Number

    + +
    +'10' === 10; // true
    +
    + +

    Zastosowanie unarnego operatora + spowoduje rzutowanie do typu Number.

    + +

    Rzutowanie do typu Boolean

    + +

    Używając dwukrotnie operatora negacji dowolna wartość może zostać zrzutowana +do typu Boolean

    + +
    !!'foo';   // true
    +!!'';      // false
    +!!'0';     // true
    +!!'1';     // true
    +!!'-1'     // true
    +!!{};      // true
    +!!true;    // true
    +

    Jądro

    Dlaczego nie należy używać eval?

    Funkcja eval uruchomi podany string jako kod JavaScript w lokalnym zasięgu (scopie).

    + +
    var foo = 1;
    +function test() {
    +    var foo = 2;
    +    eval('foo = 3');
    +    return foo;
    +}
    +test(); // 3
    +foo; // 1
    +
    + +

    Niestaty, eval zostanie wykonana w lokalnym zasięgu tylko wtedy, gdy zostanie wywołana +bezpośrednio i nazwa wywoływanej funkcji równa sie eval.

    + +
    var foo = 1;
    +function test() {
    +    var foo = 2;
    +    var bar = eval;
    +    bar('foo = 3');
    +    return foo;
    +}
    +test(); // 2
    +foo; // 3
    +
    + +

    Należy unikać stosowania eval o ile to tylko możliwe. W 99.9% przypadków można +osiągnąć ten sam efekt nie używając eval.

    + +

    eval w przebraniu

    + +

    Funkcje wykonywane po upływie czasu setTimeout i setInterval +mogą przyjąć string jako pierwszy argument. String ten zawsze będzie wykonywany +w globalnym zasięgu, ponieważ funkcja eval jest w tym wypadku wywoływana pośrednio.

    + +

    Problemy z bezpieczeństwem

    + +

    Funkcja eval jest również problematyczna od strony bezpieczeństwa, ponieważ +wykonuje każdy kod, który zostanie do niej przekazany i nigdy nie należy +jej używać na stringach nieznanego lub niezaufanego pochodzenia.

    + +

    Wnioski

    + +

    Funkcja eval nie powinna być w ogóle używana. Każdy kod, który jej używa +powinien zostać sprawdzony pod względem działania, wydajności i bezpieczeństwa. +W przypadku gdy użycie eval jest niezbędne do działania, wówczas taki kod +należy ponownie przemyśleć i ulepszyć aby nie wymagał użycia eval.

    undefined i null

    JavaScript ma dwie różne wartości dla pustych wartości, bardziej użyteczną +z tych dwóch jest undefined.

    + +

    Wartość undefined

    + +

    undefined jest typem z dokładnie jedną wartością: undefined.

    + +

    Język również definiuje globalną zmienną, która ma wartość undefined - zmienna +ta jest nazwana undefined. Jednakże jest to zmienna a nie stała, czy słowo +kluczowe. Oznacza to, że możliwe jest nadpisanie wartości tej zmiennej.

    + + + +

    Kilka przykładów kiedy wartość undefined jest zwracana:

    + +
      +
    • dostęp do (niemodyfikowalnej) zmiennej globalnej undefined,
    • +
    • wyjście z funkcji, która nie ma deklaracji return,
    • +
    • deklaracja return, która nic jawnie nie zwraca,
    • +
    • poszukiwanie nieistniejącej właściwości,
    • +
    • parametr funkcji, który nie został jawnie przekazany podczas wywołania funkcji,
    • +
    • wszystko czemu została przypisana wartość undefined.
    • +
    + +

    Obsługa przypadku zmiany wartości undefined

    + +

    Ponieważ globalna zmienna undefined zawiera tylko kopię prawdziwej wartości typu +undefined, przypisanie nowej wartości do tej zmiennej nie zmienia wartości +typu undefined.

    + +

    Jednak aby porównać coś z wartością undefined, trzeba odczytać wartość undefined.

    + +

    Aby uchronić swój kod przed możliwym nadpisaniem zmiennej undefined, korzysta +się z powszechnej techniki dodania dodatkowego parametru do +anonimowego wrappera, do którego nie zostanie przekazany +argument.

    + +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // undefined o lokalnym zasięgu znowu 
    +    // odnosi się do poprawnej wartości
    +
    +})('Hello World', 42);
    +
    + +

    Kolejnym sposobem na osiągnięcie tego samego efektu jest użycie deklaracji zmiennej +wewnątrz wrappera.

    + +
    var undefined = 123;
    +(function(something, foo) {
    +    var undefined;
    +    ...
    +
    +})('Hello World', 42);
    +
    + +

    Jedyną różnicą pomiędzy tymi sposobami są dodatkowe 4 bajty przeznaczone na słowo +kluczowe var i spację po nim.

    + +

    Zastosowanie null

    + +

    Podczas gdy undefined w kontekście języka jest używany jak null w sensie +tradycyjnych języków, null w JavaScript (jako literał i jako typ) jest po +prostu kolejnym typem danych.

    + +

    Jest wykorzystywany we wnętrzu JavaScript (np. deklaracji końca łańcucha prototypów +poprzez ustawienie Foo.prototype = null), ale prawie w każdym przypadku można go +zastąpić przez undefined.

    Automatyczne wstawianie średnika

    Mimo że JavaScript ma składnię podobną do języka C, to nie wymusza stosowania +średników w kodzie źródłowym. Istnieje możliwość ich pominięcia.

    + +

    JavaScript nie jest językiem bez średników, tak na prawdę potrzebuje +średników aby zinterpretować kod źródłowy. Jednakże parser JavaScript +automatycznie wstawia średniki o ile napotka błąd parsowania związany z +brakiem średnika.

    + +
    var foo = function() {
    +} // błąd parsowania, oczekiwany był w tym miejscu średnik
    +test()
    +
    + +

    Parser dodaje średnik, i próbuje jeszcze raz sparsować skrypt.

    + +
    var foo = function() {
    +}; // bez błędu parser kontynuuje
    +test()
    +
    + +

    Automatyczne wstawianie średników jest uważane za jeden z największych błędów +konstrukcji języka, ponieważ może ono zmienić zachowanie kodu.

    + +

    Jak działa wstawianie

    + +

    Kod poniżej nie ma żadnych średników, więc parser zdecyduje, w których miejscach +je wstawi.

    + +
    (function(window, undefined) {
    +    function test(options) {
    +        log('testing!')
    +
    +        (options.list || []).forEach(function(i) {
    +
    +        })
    +
    +        options.value.test(
    +            'long string to pass here',
    +            'and another long string to pass'
    +        )
    +
    +        return
    +        {
    +            foo: function() {}
    +        }
    +    }
    +    window.test = test
    +
    +})(window)
    +
    +(function(window) {
    +    window.someLibrary = {}
    +
    +})(window)
    +
    + +

    Poniżej znajduje się rezultat "zgadywania" parsera.

    + +
    (function(window, undefined) {
    +    function test(options) {
    +
    +        // Nie wstaniony średnik, linie zostały połączone
    +        log('testing!')(options.list || []).forEach(function(i) {
    +
    +        }); // <- wstawiony
    +
    +        options.value.test(
    +            'long string to pass here',
    +            'and another long string to pass'
    +        ); // <- wstawiony
    +
    +        return; // <- wstawiony, psując deklarację return
    +        { // potraktowane jako definicja bloku
    +
    +            // etykieta oraz pojedyncze wyrażenie
    +            foo: function() {} 
    +        }; // <- wstawiony
    +    }
    +    window.test = test; // <- wstawiony
    +
    +// Kolejna połączona linia
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- wstawiony
    +
    +})(window); //<- wstawiony
    +
    + + + +

    Parser drastycznie zmienił działanie powyższego kodu. W niektórych przypadkach +zmienił go źle.

    + +

    Nawiasy

    + +

    W przypadku, gdy w następnej linii znajduje się nawias, parser nie wstawi +średnika.

    + +
    log('testing!')
    +(options.list || []).forEach(function(i) {})
    +
    + +

    Kod ten zostanie zmieniony w poniższą linię.

    + +
    log('testing!')(options.list || []).forEach(function(i) {})
    +
    + +

    Jest bardzo prawdopodobne, że log nie zwróci fukcji. Co za tym idzie +powyższy kod wyrzuci błąd TypeError oznajmując, że undefined is not a +function - undefined nie jest funkcją.

    + +

    Wnioski

    + +

    Zaleca się, aby nigdy nie pomijać średników, pozostawiać nawias otwierający +w tej samej linii co odpowiadająca mu definicja i nigdy nie pozostawiać deklaracji +if / else bez nawiasów - nawet, jeżeli są jednolinijkowe. Wszystkie te uwagi nie +tylko pomagają poprawić spójność kodu, ale też zapobiegają zmianie działania +kodu przez parser JavaScript.

    Inne

    setTimeout i setInterval

    Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania +funkcji korzystając z funkcji setTimeout i setInterval. +Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the setTimeout and setInterval functions.

    + + + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // zwraca licznę typu Number > 0
    +
    + +

    Powyższe wywołanie setTimeout zwraca ID budzika i planuje wywołanie foo za +około tysiąc milisekund. foo zostanie wykonana dokładnie jeden raz.

    + +

    Nie ma pewności, że kod zaplanowany do wykonania wykona się dokładnie po +upłynięciu zadanego czasu podanego jako parametr do setTimeout, ponieważ zależy +to od dokładności zegara w silniku JavaScript, który wykonuje kod oraz od tego, +że inny kawałek kodu może zablokować wątek, ponieważ JavaScript jest tylko +jedno wątkowy.

    + +

    Funkcja, która została przekazana jako pierwszy parametr zostanie wykonana w +globalnym zasięgu, co oznacza, że this wewnątrz tej funkcji +będzie wkazywać na obiekt global.

    + +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this wskazuje na obiekt global
    +        console.log(this.value); // wypisze undefined
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    +
    + + + +

    Kolejkowanie wywołań z setInterval

    + +

    Podczas, gdy setTimeout wywołuje podaną funkcję tylko raz, setInterval - +jak wskazuje nazwa - będzie wykonywać funkcję w odstępach czasowych co X +milisekund. Jednakże korzystanie z tej funkcji jest odradzane.

    + +

    Kiedy wykonywany kod zablokuje możliwość uruchomienia zaplanowanej funkcji, +setInterval będzie próbować uruchamiać daną funkcję co będzie powodować +kolejkowanie wykonania tej samej funkcji kilkakrotnie. W szczególności może się +to wydarzyć przy krótkim interwale.

    + +
    function foo(){
    +    // coś co blokuje wykonanie na 1 sekundę 
    +}
    +setInterval(foo, 100);
    +
    + +

    W powyższym kodzie kod foo zostanie wywołany tylko raz i zablokuje wywołanie na +jedną sekundę.

    + +

    Podczas, gdy funkcja foo blokuje wykonanie setInterval będzie planować kolejne +wywołania foo. W momencie, gdy pierwsze wywołanie foo się zakończy, już +w kolejce do wywołania będą czekały kolejne dziesięć wywołań tej funkcji.

    + +

    Radzenie sobie z możliwymi blokadami

    + +

    Najprostrzym jak również najbardziej kontrolowaną sytuacją jest użycie setTimeout +wewnątrz wywoływanej funkcji.

    + +
    function foo(){
    +    // coś co blokuje wykonanie na 1 sekundę
    +    setTimeout(foo, 100);
    +}
    +foo();
    +
    + +

    Powyższy kod nie tylko hermetyzuje wywołanie setTimeout ale również zapobiega +kolejkowaniu wywołań fukcji i daje dodatkową kontrolę. W tym przypadku funkcja +foo może zdecydować czy powinna się wywołać ponownie czy też nie.

    + +

    Ręczne usuwanie budzików

    + +

    Usuwanie budzików i interwałów dokonywane jest przez przekazanie odpowiedniego ID +do clearTimeout lub clearInterval, w zależności z jakiej funkcji zostało +zwrócone ID.

    + +
    var id = setTimeout(foo, 1000);
    +clearTimeout(id);
    +
    + +

    Usuwanie wszystkich budzików

    + +

    Ponieważ nie istnieje wbudowana metoda na usunięcie wszystkich budzików i/lub +interwałów, konieczne jest użycie metody brute force aby osiągnąć ten efekt.

    + +
    // usunięcie "wszystkich" budzików 
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +
    + +

    Nadal może istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała, +ponieważ ID było z innego przedziału, dlatego zamiast korzystania z metody brute +force, zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć.

    + +

    Ukryte wykorzystanie eval

    + +

    Do setTimeout i setInterval można również przekazać string jako pierwszy +parametr zamiast obiektu funkcji, jednakże nigdy nie należy korzystać z tej +możliwości, ponieważ wewnętrznie setTimeout i setInterval wykorzystują eval.

    + + + +
    function foo() {
    +    // zostanie wykonane 
    +}
    +
    +function bar() {
    +    function foo() {
    +        // nigdy nie zostanie wywołane
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +
    + +

    Ponieważ eval nie zostało wywołane wprost w tym przypadku, to +string przekazany do setTimeout zostanie uruchomiony w zasięgu globalnym, +co za tym idzie lokalna zmienna foo z zasięgu bar nie zostanie użyta.

    + +

    Kolejnym zaleceniem jest aby nie stosować stringów do przekazywania argumentów +do funkcji, która ma zostać wywołana przez budzik.

    + +
    function foo(a, b, c) {}
    +
    +// NIGDY nie należy tak robić 
    +setTimeout('foo(1,2, 3)', 1000)
    +
    +// Zamiast tego należy skorzystać z anonimowej funkcji
    +setTimeout(function() {
    +    foo(a, b, c);
    +}, 1000)
    +
    + + + +

    Wnioski

    + +

    Nie należy nigdy przekazywać stringu jako parametru do setTimeout lub +setInterval. Jest to wyraźną oznaką bardzo złego kodu, jeżeli potrzebne jest +przekazanie argumentów do funkcji należy skorzystać z anonimowej funkcji i +wewnątrz niej dokonać przekazania argumentów.

    + +

    Ponadto, należy unikać korzystanie z setInterval, ponieważ planista może +zablokować wykonanie JavaScriptu.

    \ No newline at end of file From bbe88bba290d486d50b6a725541b43fbc9ce4c49 Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 18:17:52 +0200 Subject: [PATCH 204/641] Corrections in Polish translation (Other section). --- doc/pl/other/timeouts.md | 66 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/doc/pl/other/timeouts.md b/doc/pl/other/timeouts.md index bf2ad512..9cf78d13 100644 --- a/doc/pl/other/timeouts.md +++ b/doc/pl/other/timeouts.md @@ -1,7 +1,7 @@ ### `setTimeout` i `setInterval` Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania -funkcji korzystając z funkcji `setTimeout` i `setInterval`. +funkcji przy użyciu funkcji `setTimeout` i `setInterval`. Since JavaScript is asynchronous, it is possible to schedule the execution of a function by using the `setTimeout` and `setInterval` functions. @@ -9,7 +9,7 @@ function by using the `setTimeout` and `setInterval` functions. > standardu [DOM][1]. function foo() {} - var id = setTimeout(foo, 1000); // zwraca licznę typu Number > 0 + var id = setTimeout(foo, 1000); // zwraca liczbę typu Number > 0 Powyższe wywołanie `setTimeout` zwraca ID budzika i planuje wywołanie `foo` za **około** tysiąc milisekund. `foo` zostanie wykonana dokładnie **jeden raz**. @@ -18,11 +18,11 @@ Powyższe wywołanie `setTimeout` zwraca ID budzika i planuje wywołanie `foo` z upłynięciu zadanego czasu podanego jako parametr do `setTimeout`, ponieważ zależy to od dokładności zegara w silniku JavaScript, który wykonuje kod oraz od tego, że inny kawałek kodu może zablokować wątek, ponieważ JavaScript jest tylko -jedno wątkowy. +jednowątkowy. Funkcja, która została przekazana jako pierwszy parametr zostanie wykonana w globalnym zasięgu, co oznacza, że [`this`](#function.this) wewnątrz tej funkcji -będzie wkazywać na obiekt *global*. +będzie wskazywać na obiekt *global*. function Foo() { this.value = 42; @@ -35,22 +35,22 @@ będzie wkazywać na obiekt *global*. new Foo(); > **Uwaga:** Ponieważ `setTimeout` przyjmuje **obiekt funkcji** jako pierwszy -> argument często popełnianym błędem jest wykorzystanie składni `setTimeout(foo(), 1000)`, +> argument, często popełnianym błędem jest wykorzystanie składni `setTimeout(foo(), 1000)`, > która użyje wartości zwróconej przez funkcję `foo` jako parametru zamiast > funkcji `foo` samej w sobie. W większości przypadków będzie to cichy błąd, -> ponieważ jeżeli funkcja zwróci `undefined` `setTimeout` **nie** wyrzuci żadnego +> ponieważ jeżeli funkcja zwróci `undefined`, `setTimeout` **nie** wyrzuci żadnego > błędu. ### Kolejkowanie wywołań z `setInterval` -Podczas, gdy `setTimeout` wywołuje podaną funkcję tylko raz, `setInterval` - +Podczas gdy `setTimeout` wywołuje podaną funkcję tylko raz, `setInterval` - jak wskazuje nazwa - będzie wykonywać funkcję **w odstępach czasowych** co `X` milisekund. Jednakże korzystanie z tej funkcji jest odradzane. Kiedy wykonywany kod zablokuje możliwość uruchomienia zaplanowanej funkcji, -`setInterval` będzie próbować uruchamiać daną funkcję co będzie powodować -kolejkowanie wykonania tej samej funkcji kilkakrotnie. W szczególności może się -to wydarzyć przy krótkim interwale. +`setInterval` będzie próbować uruchamiać daną funkcję, co będzie powodować +kolejkowanie wykonania tej samej funkcji kilkukrotnie. Może się to zdażyć +szczególnie przy krótkim interwale. function foo(){ // coś co blokuje wykonanie na 1 sekundę @@ -60,13 +60,13 @@ to wydarzyć przy krótkim interwale. W powyższym kodzie kod `foo` zostanie wywołany tylko raz i zablokuje wywołanie na jedną sekundę. -Podczas, gdy funkcja `foo` blokuje wykonanie `setInterval` będzie planować kolejne -wywołania `foo`. W momencie, gdy pierwsze wywołanie `foo` się zakończy, już -w kolejce do wywołania będą czekały kolejne **dziesięć** wywołań tej funkcji. +Podczas, gdy funkcja `foo` blokuje wykonanie, `setInterval` będzie planować kolejne +wywołania `foo`. W momencie, gdy pierwsze wywołanie `foo` się zakończy, +w kolejce do wywołania będzie już czekało kolejne **dziesięć** wywołań tej funkcji. ### Radzenie sobie z możliwymi blokadami -Najprostrzym jak również najbardziej kontrolowaną sytuacją jest użycie `setTimeout` +Najprostszą, jak również najbardziej kontrolowaną sytuacją, jest użycie `setTimeout` wewnątrz wywoływanej funkcji. function foo(){ @@ -75,9 +75,9 @@ wewnątrz wywoływanej funkcji. } foo(); -Powyższy kod nie tylko hermetyzuje wywołanie `setTimeout` ale również zapobiega -kolejkowaniu wywołań fukcji i daje dodatkową kontrolę. W tym przypadku funkcja -`foo` może zdecydować czy powinna się wywołać ponownie czy też nie. +Powyższy kod nie tylko hermetyzuje wywołanie `setTimeout`, ale też zapobiega +kolejkowaniu wywołań funkcji i daje dodatkową kontrolę. W tym przypadku funkcja +`foo` może zdecydować czy powinna się wywołać ponownie, czy też nie. ### Ręczne usuwanie budzików @@ -90,17 +90,17 @@ zwrócone ID. ### Usuwanie wszystkich budzików -Ponieważ nie istnieje wbudowana metoda na usunięcie wszystkich budzików i/lub -interwałów, konieczne jest użycie metody brute force aby osiągnąć ten efekt. +Ponieważ nie istnieje wbudowana metoda usuwania wszystkich budzików i/lub +interwałów, do osiągnięcia tego efektu konieczne jest użycie metody 'brute force'. // usunięcie "wszystkich" budzików for(var i = 1; i < 1000; i++) { clearTimeout(i); } -Nadal może istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała, -ponieważ ID było z innego przedziału, dlatego zamiast korzystania z metody brute -force, zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć. +Nadal mogą istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała. +Ponieważ ID było z innego przedziału, zamiast korzystania z metody brute force, +zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć. ### Ukryte wykorzystanie `eval` @@ -125,11 +125,11 @@ możliwości, ponieważ wewnętrznie `setTimeout` i `setInterval` wykorzystują } bar(); -Ponieważ `eval` nie zostało wywołane [wprost](#core.eval) w tym przypadku, to -string przekazany do `setTimeout` zostanie uruchomiony w *zasięgu globalnym*, -co za tym idzie lokalna zmienna `foo` z zasięgu `bar` nie zostanie użyta. +Ponieważ `eval` nie zostało wywołane w tym przypadku [wprost](#core.eval), to +string przekazany do `setTimeout` zostanie uruchomiony w *zasięgu globalnym*. +Co za tym idzie, lokalna zmienna `foo` z zasięgu `bar` nie zostanie użyta. -Kolejnym zaleceniem jest aby **nie** stosować stringów do przekazywania argumentów +Kolejnym zaleceniem jest **niestosowanie** stringów do przekazywania argumentów do funkcji, która ma zostać wywołana przez budzik. function foo(a, b, c) {} @@ -137,23 +137,23 @@ do funkcji, która ma zostać wywołana przez budzik. // NIGDY nie należy tak robić setTimeout('foo(1,2, 3)', 1000) - // Zamiast tego należy skorzystać z anonimowej funkcji + // zamiast tego należy skorzystać z anonimowej funkcji setTimeout(function() { foo(a, b, c); }, 1000) ->**Uwaga:** Mimo, że możliwe jest wykorzystanie składni -> `setTimeout(foo, 1000, a, b, c)`, to nie zaleca się korzystania z niej, ponieważ +>**Uwaga:** Mimo że możliwe jest wykorzystanie składni +> `setTimeout(foo, 1000, a, b, c)`, nie zaleca się korzystania z niej, ponieważ > może to prowadzić do subtelnych błędów podczas wykorzystania [metod](#function.this). ### Wnioski -Nie należy **nigdy** przekazywać stringu jako parametru do `setTimeout` lub -`setInterval`. Jest to wyraźną oznaką **bardzo** złego kodu, jeżeli potrzebne jest -przekazanie argumentów do funkcji należy skorzystać z *anonimowej funkcji* i +**Nigdy** nie należy przekazywać stringu jako parametru do `setTimeout` lub +`setInterval`. Jest to wyraźną oznaką **bardzo** złego kodu. Jeżeli potrzebne jest +przekazanie argumentów do funkcji, należy skorzystać z *anonimowej funkcji* i wewnątrz niej dokonać przekazania argumentów. -Ponadto, należy unikać korzystanie z `setInterval`, ponieważ planista może +Ponadto, należy unikać korzystania z `setInterval`, ponieważ planista może zablokować wykonanie JavaScriptu. [1]: http://pl.wikipedia.org/wiki/Obiektowy_model_dokumentu "Document Object Model" From 120540c3743c58a46bc0cceeaf9314c82239b638 Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 18:35:54 +0200 Subject: [PATCH 205/641] Corrections in Polish translation (Types section). --- doc/pl/types/casting.md | 26 +++++++++++++------------- doc/pl/types/equality.md | 18 +++++++++--------- doc/pl/types/instanceof.md | 8 ++++---- doc/pl/types/typeof.md | 8 ++++---- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/doc/pl/types/casting.md b/doc/pl/types/casting.md index 5e358c63..7c676ee1 100644 --- a/doc/pl/types/casting.md +++ b/doc/pl/types/casting.md @@ -1,43 +1,43 @@ ## Rzutowanie typów -JavaScript jest językiem słabo typowanym, co za tym idzie będzie stosować koercję +JavaScript jest językiem słabo typowanym. Co za tym idzie, będzie stosować koercję typów **gdziekolwiek** jest to możliwe. - // These are true + // te zwracają true new Number(10) == 10; // Number.toString() zostanie przekształcone // z powrotem do liczby - 10 == '10'; // Stringi zostaną przekształcone do typu Number - 10 == '+10 '; // Kolejne wariacje + 10 == '10'; // stringi zostaną przekształcone do typu Number + 10 == '+10 '; // kolejne wariacje 10 == '010'; // i następne isNaN(null) == false; // null zostanie przekształcony do 0 // który oczywiście nie jest NaN - // Poniższe zwracają false + // poniższe zwracają false 10 == 010; 10 == '-10'; -> **Uwaga ES5: Literały licznowe zaczynające sie od `0` są interpretowane jako +> **Uwaga ES5: Literały liczbowe zaczynające sie od `0` są interpretowane jako > liczby w systemie ósemkowym. W trybie strict mode w ECMAScript 5 wsparcie dla > liczb ósemkowych zostało porzucone. -Aby uniknąć powyższych problemów, należy **koniecznie** skorzystać ze +Aby uniknąć powyższych problemów, należy **koniecznie** korzystać ze [ściełego operatora równości](#types.equality). Mimo, że pozwala to uniknąć wiele typowych problemów to nadal istnieje wiele innych, które powstają na bazie słabego typowania języka JavaScript. ### Konstruktory typów wbudowanych -Konstruktory typów wbudowanych takich, jak `Number` lub `String` zachowują się -inaczej jeżeli są poprzedzone słowem kluczowym `new` a inaczej jeżeli nie są. +Konstruktory typów wbudowanych, takich jak `Number` lub `String`, zachowują się +inaczej kiedy są poprzedzone słowem kluczowym `new` a inaczej kiedy nie są. new Number(10) === 10; // False, Object i Number Number(10) === 10; // True, Number i Number new Number(10) + 0 === 10; // True, ponieważ dokonano jawnej konwersji -Korzystanie z wbudowanych typów jak `Number` jako konstruktor utworzy nowy obiekt -typu `Number`, natomiast opuszczenie słowa kluczowego `new` spowoduje, że funkcja -`Number` zachowa się jak konwerter. +Korzystanie z wbudowanych typów jak `Number` jako konstruktora tworzy nowy obiekt +typu `Number`, natomiast opuszczenie słowa kluczowego `new` powoduje, że funkcja +`Number` zachowuje się jak konwerter. Ponadto, użycie literałów lub wartości nieobiektowych zaowocuje jeszcze większą ilością rzutowań (koercją) typów. @@ -58,7 +58,7 @@ Zastosowanie **unarnego** operatora + spowoduje rzutowanie do typu Number. ### Rzutowanie do typu Boolean -Używając dwukrotnie operatora **negacji** dowolna wartość może zostać zrzutowana +Używając dwukrotnie operatora **negacji**, dowolna wartość może zostać zrzutowana do typu Boolean !!'foo'; // true diff --git a/doc/pl/types/equality.md b/doc/pl/types/equality.md index e79b952f..48f4197a 100644 --- a/doc/pl/types/equality.md +++ b/doc/pl/types/equality.md @@ -21,16 +21,16 @@ JavaScript jest słabo typowanym językiem. Oznacza to, że operator równości Powyższa tabela przedstawia wyniki koercji typów. Nieprzewidywalne wyniki porównania są głównym powodem, że stosowanie `==` jest powszechnie uważane za złą -praktykę. Skomplikowane reguły konwersji są powodem trudnych do wyśledzenia błędy. +praktykę. Skomplikowane reguły konwersji są powodem trudnych do wyśledzenia błędów. -Ponadto koercja ma również wpływ na wydajność na przykład gdy typ String musi zostać +Ponadto koercja ma również wpływ na wydajność, Na przykład gdy typ String musi zostać przekształcony na typ Number przed porównaniem z drugą liczbą. ### Operator ścisłej równości Operator ścisłej równości składa się z **trzech** znaków "równa się": `===` -Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem nie +Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem - nie dokonuje koercji typów przed porównaniem. "" === "0" // false @@ -49,8 +49,8 @@ obiektów o różnych typach. ### Porównywanie obiektów -Mimo, że oba operatory `==` i `===` nazywane są operatorami **równościowymi**, -to zachowują się różnie gdy jednym z operandów jest obiekt typu `Object`. +Mimo że oba operatory `==` i `===` nazywane są operatorami **równościowymi**, +to zachowują się różnie, gdy jednym z operandów jest obiekt typu `Object`. {} === {}; // false new String('foo') === 'foo'; // false @@ -59,13 +59,13 @@ to zachowują się różnie gdy jednym z operandów jest obiekt typu `Object`. foo === foo; // true Oba operatory porównują **toższmość** a **nie** równość, czyli będą porównywać czy -jeden i drugi operand jest tą samą **instancją** obiektu, podobnie jak operator -`is` w Pythonie i porównanie wskaźników w C. +jeden i drugi operand jest tą samą **instancją** obiektu (podobnie jak operator +`is` w Pythonie i porównanie wskaźników w C). ### Wnioski -Zaleca się aby używać tylko operatora **ścisłej równości**. W sytuacjach gdy +Zaleca się, aby używać tylko operatora **ścisłej równości**. W sytuacjach gdy potrzebna jest koercja (porównanie obiektów różnych typów), konwersja powinna -być dokonana [jawnie](#types.casting) a nie pozostawiona trudnym regułom koercji +być dokonana [jawnie](#types.casting), a nie pozostawiona trudnym regułom koercji obowiązującym w języku. diff --git a/doc/pl/types/instanceof.md b/doc/pl/types/instanceof.md index 73ae4ebd..aee5fe86 100644 --- a/doc/pl/types/instanceof.md +++ b/doc/pl/types/instanceof.md @@ -1,8 +1,8 @@ ## Operator `instanceof` Operator `instanceof` porównuje konstruktory obiektów przekazanych jako operendy. -Jest on jedynie użyteczny do porównywania obiektów utworzonych klas. Stosowanie -go na wbudowanych typach jest praktycznie tak samo bezużyteczne jak operatora +Jest on użyteczny jedynie do porównywania obiektów utworzonych klas. Stosowanie +go na wbudowanych typach jest praktycznie tak samo bezużyteczne, jak operatora [typeof](#types.typeof). ### Porównywanie obiektów utworzonych klas @@ -14,7 +14,7 @@ go na wbudowanych typach jest praktycznie tak samo bezużyteczne jak operatora new Bar() instanceof Bar; // true new Bar() instanceof Foo; // true - // Poniżej kod który przypisuje do Bar.prototype obiekt funkcji Foo + // poniżej kod który przypisuje do Bar.prototype obiekt funkcji Foo // a nie faktyczną instancję Foo Bar.prototype = Foo; new Bar() instanceof Foo; // false @@ -34,7 +34,7 @@ samymi obiektami. ### Wnioski -Operator `instanceof` powinien być **tylko** używany podczas korzystania z obiektów +Operator `instanceof` powinien być używany **wyłącznie** podczas korzystania z obiektów klas utworzonych, które były zdefiniowane w tym samym kontekscie JavaScriptowym. Podobnie jak operator [`typeof`](#types.typeof), należy **unikać** korzystania z tego operatora w innych sytuacjach. diff --git a/doc/pl/types/typeof.md b/doc/pl/types/typeof.md index 186e241f..ccbc21d1 100644 --- a/doc/pl/types/typeof.md +++ b/doc/pl/types/typeof.md @@ -1,10 +1,10 @@ ## Operator `typeof` Operator `typeof` (razem z operatorem [`instanceof`](#types.instanceof)) jest -prawdopodobnie najwiekszą wadą konstrukcji języka JavaScript, jest on praktycznie -**całkowicie wadliwy**. +prawdopodobnie najwiekszą wadą konstrukcji języka JavaScript. Posiada on praktycznie +**same wady**. -Mimo, że `instanceof` ma swoje wady to nadal ma ograniczone zastosowanie w praktyce, +Mimo że `instanceof` ma swoje wady to nadal ma ograniczone zastosowanie w praktyce, natomiast `typeof` ma tylko jeden praktyczny przypadek użycia, który na dodatek **nie** jest związany z sprawdzaniem typu obiektu. @@ -83,5 +83,5 @@ są zdefiniowane w specyfikacji, co za tym idzie mogą się różnić w różnyc implementacjach. O ile nie operator `typeof` nie jest użyty do sprawdzania czy zmienna została -zdefiniowana, powinien być unikany **o ile to tylko możliwe**. +zdefiniowana, powinien być unikany **jeśli to tylko możliwe**. From 64a603ee551b7f2b565e52ec249046a273441ecc Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 18:49:31 +0200 Subject: [PATCH 206/641] Small correction in Polish translation. --- doc/pl/object/general.md | 2 +- server.js | 2 +- site/pl/index.html | 272 +++++++++++++++++++-------------------- 3 files changed, 138 insertions(+), 138 deletions(-) diff --git a/doc/pl/object/general.md b/doc/pl/object/general.md index e4ff6a96..9512c293 100644 --- a/doc/pl/object/general.md +++ b/doc/pl/object/general.md @@ -85,7 +85,7 @@ została usunięta i dlatego nie została wypisana. ### Notacja właściwości var test = { - 'case': 'jestem zastrzeżonym słowem kluczowym, więc muszę być w cudzysłowie', + 'case': 'jestem słowem kluczowym, więc muszę być w cudzysłowie', delete: 'tak samo jak ja' // wyrzuca błąd SyntaxError }; diff --git a/server.js b/server.js index fb64b73d..1ead3c14 100644 --- a/server.js +++ b/server.js @@ -2,7 +2,7 @@ var build = require('./build').build, qs = require('querystring'), port = 9900, - repoURL = "https://github.com/cramerdev/JavaScript-Garden"; + repoURL = "https://github.com/ciembor/JavaScript-Garden"; require('http').createServer(function (request, response) { var payload = ''; diff --git a/site/pl/index.html b/site/pl/index.html index 872f9cd0..7542ba1d 100644 --- a/site/pl/index.html +++ b/site/pl/index.html @@ -1,7 +1,7 @@ JavaScript Garden -

    Wstęp

    JavaScript Garden jest rosnącą kolekcją dokumentów o najdziwniejszych +

    Wstęp

    JavaScript Garden jest rosnącą kolekcją dokumentów o najdziwniejszych częściach języka JavaScript. Dokumentacja pomaga uniknąć najczęściej popełnianych błędów, sybtelnych bugów, problemów wydajnościowych oraz złych praktyk, na które niedoświadczeni programiści JavaScript mogą natrafić próbując poznać tajniki tego @@ -32,9 +32,9 @@ Foo.bar; // 1

    -

    Popularnym błędem jest wykorzystanie literałów liczbowych jako obiektu. -Spowodowanie jest to usterką w parserze JavaScript, który interpretuje kropkę -po literale liczbowym jako rozdzielenie części całkowitej od części po przecinku +

    Popularnym błędem jest traktowanie literałów liczbowych jak obiektu. +Spowodowane jest to specyfiką parsera JavaScript, który interpretuje kropkę +po literale liczbowym jako rozdzielenie części całkowitej od części ułamkowej liczby.

    2.toString(); // wyrzuca błąd SyntaxError
    @@ -45,20 +45,20 @@
     
     
    2..toString(); // druga kropka jest poprawnie rozpoznana
     2 .toString(); // zauważ, że pozostawiona jest spacja przed kropką
    -(2).toString(); // 2 zostanie zewaluowane najpiewr
    +(2).toString(); // 2 zostanie najpierw zewaluowane
     

    Obiekty jako typy danych

    -

    Obiekty w języku JavaScript mogą być używana jako tablice asocjacyjne. -Ponieważ obiekty głównie składają się z mapowań pomiędzy nazwanymi właściwościami (kluczami) +

    Obiekty w języku JavaScript mogą być używana jako tablice asocjacyjne, +ponieważ obiekty składają się głównie z mapowań pomiędzy nazwanymi właściwościami (kluczami) a wartościami dla tych atrybutów.

    -

    Używając literału obiektu - notacji {} - istnieje możliwość stworzenie obiektu prostego. +

    Używając literału obiektu - notacji {} - istnieje możliwość stworzenia obiektu prostego. Ten nowy obiekt bedzie dziedziczył z Object.prototype oraz -nie bedzie posiadał żadnych własnych właściwości zdefiniowanych w sobie.

    +nie bedzie posiadał żadnych własnych właściwości.

    -
    var foo = {}; // nowy pusty obiekt
    +
    var foo = {}; // nowy, pusty obiekt
     
     // nowy obiekt z właściwością test o wartości 12
     var bar = {test: 12}; 
    @@ -66,8 +66,8 @@
     
     

    Dostęp do właściwości

    -

    Właściwości obiektu można uzyskać na dwa sposoby, poprzez notację z kropką -lub notacje z nawiasami kwadratowymi.

    +

    Właściwości obiektu można uzyskać na dwa sposoby - poprzez notację z kropką +lub z nawiasami kwadratowymi.

    var foo = {name: 'Kitten'}
     foo.name; // kitten
    @@ -80,8 +80,8 @@
     foo['1234']; // działa, zwraca undefined
     
    -

    Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą że notacja z nawiasami -kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzycenia +

    Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą, że notacja z nawiasami +kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzucenia błędu podczas odczytu nieistniejącej właściwości.

    Usuwanie właściwości

    @@ -106,43 +106,43 @@ } -

    Powyższy kod wypisuje dwie linie bar undefined i foo null - tylko własność baz +

    Powyższy kod wypisuje dwie linie - bar undefined i foo null. Tylko własność baz została usunięta i dlatego nie została wypisana.

    Notacja właściwości

    var test = {
    -    'case': 'I am a keyword so I must be notated as a string',
    -    delete: 'I am a keyword too so me' // wyrzuca błąd SyntaxError
    +    'case': 'jestem zastrzeżonym słowem kluczowym, więc muszę być w cudzysłowie',
    +    delete: 'tak samo jak ja' // wyrzuca błąd SyntaxError
     };
     
    -

    Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst(bez cudzysłowów +

    Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst (bez cudzysłowów lub apostrofów) lub jako string (w cudzisłowach lub apostrofach). -Ze względu na kolejne niedociągnięcie w parserze JavaScript +Ze względu na kolejne niedociągnięcie w parserze JavaScript, powyższy kod wyrzuci błąd SyntaxError dla implementacji JavaScript ponizej ECMAScript 5.

    Ten błąd wynika z faktu, że delete jest słowem kluczowym, dlatego musi zostać zapisany jako string (z cudzysłowami lub apostrofami), aby zapewnić, że zostanie -to poprawnie zinterpretowane przez starsze silniki języka JavaScript.

    Prototyp

    JavaScript nie posiada klasycznego modelu dziedziczenia, lecz zamiast tego +to poprawnie zinterpretowane przez starsze silniki języka JavaScript.

    Prototyp

    JavaScript nie posiada klasycznego modelu dziedziczenia. Zamiast tego dziedziczenie jest realizowane poprzez prototypy.

    Choć jest to często uważane za jedną ze słabości języka JavaScript, prototypowy model dziedziczenia, jest w rzeczywistości potężniejszy od klasycznego -modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowym -jest dość proste, podczas gdy zrobienie odwrotnie to już o wiele trudniejsze zadanie.

    +modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowego +jest dość proste, podczas gdy zrobienie odwrotnego przekształcenie to o wiele trudniejsze zadanie.

    Ze względu na fakt, że w JavaScript jest w zasadzie jedynym powszechnie stosowanym -językiem, któy posiada prototypowy model dziedziczenia, to wymaga troche czasu aby -dostosować się do różnic pomiędzy tymi dwoma modelami.

    +językiem, któy posiada prototypowy model dziedziczenia, dostosowanie się do różnic pomiędzy +tymi dwoma modelami wymaga trochę czasu.

    Pierwszą znaczącą różnicą jest to, że dziedziczenie w JavaScript odbywa się za pomocą tak zwanych łańcuchów prototypów.

    @@ -174,27 +174,27 @@ { toString: ... /* etc. */ }
    -

    W powyższym przykładzie obiekt test będzie dziedziczył z obydwu tj. +

    W powyższym przykładzie obiekt test będzie dziedziczył z obydwu, tj. Bar.prototyp i Foo.prototyp, stąd będzie miał dostęp do funkcji method, która była zdefiniowana w Foo. Ponadto obiekt będzie miał dostęp do właściwości value, która jest jednyną instancją Foo i stała się jego prototypem. -Ważne jest, aby pamiętać new Bar nie tworzy nowej instancji Foo, -ale wykorzystuje instancje, którą jest przypisana do własności prototype. +Należy pamiętać, że new Bar nie tworzy nowej instancji Foo, +tylko wykorzystuje instancję, która jest przypisana do własności prototype. Zatem Wszystkie instancje Bar będą dzieliły tą samą własność value.

    Wyszukiwanie własności

    -

    Podczas dostępu do właściwości obiektu, JavaScript przejdzie w górę łańcucha -prototypów dopóki nie znajdzie właściwości z żądaną nazwą.

    +

    Podczas dostępu do właściwości obiektu JavaScript przejdzie w górę łańcucha +prototypów, dopóki nie znajdzie właściwości bez nazwy.

    -

    Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha mianowicie Object.prototype +

    Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha, mianowicie Object.prototype i nadal nie znajdzie określonej właściwości, to zwróci wartość undefined.

    @@ -215,42 +215,42 @@

    Czas wyszukiwania właściwości, które są na końcu łańcucha prototypów może mieć negatywny wpływ na wydajność krytycznych części kodu. Dodatkowo, próba dostępu -do nieistniejącej właściwości powoduje zawsze przeszukanie całego łańcucha prototypów.

    +do nieistniejącej właściwości zawsze spowoduje przeszukanie całego łańcucha prototypów.

    -

    Również, podczas iteracji po właściwościach obiektu -każda właściwość, która znajduje się w łańcuchu prototypów niezależnie -na jakim znajduje się poziomie zostanie wyliczona.

    +

    Również podczas iteracji po właściwościach obiektu +każda właściwość, która znajduje się w łańcuchu prototypów (niezależnie +na jakim znajduje się poziomie) zostanie wyliczona.

    Rozszerzanie natywnych prototypów

    Rozszerzanie Object.prototype lub innego prototypu wbudowanych typów jest jednym z -najczęściej używanych niedoskonałej częsci języka JavaScript.

    +najczęściej nadużywanej częsci języka JavaScript.

    Technika ta nazywana jest monkey patching i łamie zasady enkapsulacji. -Jednak jest szeroko rozpowszechniona w frameworkach takich jak Prototype. -Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez dodawanie do nich -niestandardowych funkcjonalności.

    +Mimo to jest szeroko rozpowszechniona w frameworkach takich jak Prototype. +Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez wzbogacanie ich o +niestandardowe funkcjonalności.

    Jedynym dobrym powodem do rozszerzania wbudowanych prototypów jest portowanie
    -funkcjonalności znajdujących sie w nowszych silnikach JavaScript np. Array.forEach

    +funkcjonalności znajdujących sie w nowszych silnikach JavaScript, np. Array.forEach

    Wnioski

    -

    Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczanie -należy całkowicie rozumieć prototypowy model dziedziczenia. Ponadto należy uważać -na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń -aby uniknąć problemów z wydajnością. Natywne prototypy nie powinny nigdy być +

    Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczania,
    +należy całkowicie zrozumieć prototypowy model dziedziczenia. Ponadto trzeba uważać +na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń, +aby uniknąć problemów z wydajnością. Natywne prototypy nigdy nie powinny być rozszerzane, chyba że ze względu na wprowadzanie kompatybilności z nowszymi silnikami -JavaScript.

    hasOwnProperty

    W celu sprawdzenia czy dana właściwość została zdefiniowana w tym obiekcie a nie -w łańcuchu prototypów niezbędne jest skorzystanie z metody -hasOwnProperty, która wszystkie obiekty dziedziczą z Object.prototype.

    +JavaScript.

    hasOwnProperty

    W celu sprawdzenia, czy dana właściwość została zdefiniowana w tym obiekcie, a nie +w łańcuchu prototypów, niezbędne jest skorzystanie z metody +hasOwnProperty, której wszystkie obiekty dziedziczą z Object.prototype.

    -

    hasOwnProperty jest jedyna metodą w języku JavaScript która operuje na właściwościach +

    hasOwnProperty jest jedyną metodą w języku JavaScript, która operuje na właściwościach i nie przegląda całego łańcucha prototypów.

    // Zatrucie Object.prototype
    @@ -272,7 +272,7 @@
     

    hasOwnProperty jako właściwość

    JavaScript nie chroni właściwości o nazwie hasOwnProperty, zatem istnieje -możliwość, że obiekt może posiadać tak nazwaną właściwość. Konieczne jest użycie +możliwość, że obiekt będzie posiadać tak nazwaną właściwość. Konieczne jest użycie zewnętrznego hasOwnProperty, aby otrzymać poprawne rezultaty.

    var foo = {
    @@ -291,16 +291,16 @@
     
     

    Wnioski

    -

    Jedyną metodą służącą do sprawdzenia zdefiniowania jakiejś właściwości w konkretnym -obiekcie jest metoda hasOwnProperty. Zaleca się korzystać z hasOwnProperty jako część -każdej pętli for in, pozwoli to uniknąć błędów pochodzących z -rozszerzonych natywnych prototypów.

    The for in Loop

    Podobnie jak operator in, pętla for in przeszukuje łańcuch prototypów +

    Jedyną metodą służącą do sprawdzenia istnienia jakiejś właściwości w konkretnym +obiekcie jest metoda hasOwnProperty. Zaleca się korzystać z hasOwnProperty w +każdej pętli for in. Pozwoli to uniknąć błędów pochodzących +z rozszerzonych natywnych prototypów.

    Pętla for in

    Podobnie jak operator in, pętla for in przeszukuje łańcuch prototypów podczas iteracji po właściwościach obiektu.

    // Zatrucie Object.prototype
    @@ -312,17 +312,17 @@
     }
     
    -

    Ponieważ nie jest możliwe, aby zmienić zachowanie pętli for in to niezbędne +

    Ponieważ zmiana zachowania pętli for in nie jest możliwa, niezbędne jest odfiltrowanie niechcianych właściwości wewnątrz ciała pętli, korzystając z metody hasOwnProperty z Object.prototype.

    -

    Korzystanie z hasOwnProperty do odfiltrowania

    +

    Filtrowania przy użyciu hasOwnProperty

    // foo z przykładu powyżej
     for(var i in foo) {
    @@ -332,20 +332,20 @@
     }
     
    -

    To jest jedyna poprawna wersja, którą należy używać. Ze względu na użycie -hasOwnProperty zostanie wypisane jedynie moo. Gdy opuścimy hasOwnProperty -kod będzie podatny na błędy, gdy natywne prototypy np. Object.prototype -zostanie rozszerzony.

    +

    To jest jedyna poprawna wersja, której należy używać. Ze względu na użycie +hasOwnProperty zostanie wypisane jedynie moo. Gdy opuścimy hasOwnProperty, +kod będzie podatny na błędy, gdy natywne prototypy (np. Object.prototype) +zostaną rozszerzone.

    -

    Prototype jest jednym z szeroko rozpowszechniony frameworków, który dokonuje -takiego rozszerzenia. Używanie tego frameworku oraz nie używanie w pętle for in +

    Prototype jest jednym z popularniejszych frameworków, które dokonują +takiego rozszerzenia. Używanie tego frameworku oraz nie stosowanie w pętli for in metody hasOwnProperty gwarantuje błędy w wykonaniu.

    Wnioski

    -

    Zaleca się aby zawsze używać metody hasOwnProperty. Nigdy nie powinno się dokonywać -żadnych założeń na temat środowiska, w którym kod będzie wykonywany i czy natywne -prototypy zostały rozszerzone czy nie.

    Funkcje

    Deklaracje funkcji i wyrażenia funkcyjne

    Funcje w języku JavaScript są typami pierwszoklasowymi, co oznacza, że mogą +

    Zaleca się, aby zawsze używać metody hasOwnProperty. Nigdy nie powinno się dokonywać +żadnych założeń na temat środowiska, w którym kod będzie wykonywany ani tego, czy +natywne prototypy zostały rozszerzone, czy nie.

    Funkcje

    Deklaracje funkcji i wyrażenia funkcyjne

    Funcje w języku JavaScript są typami pierwszoklasowymi, co oznacza, że mogą być przekazywane jak każda inna wartość. Jednym z typowych zastosowań tej cechy jest przekazywanie anonimowej funkcji jako callback do innej, prawdopodobnie asynchronicznej funkcji.

    @@ -1242,16 +1242,16 @@

    Powyższa tabela przedstawia wyniki koercji typów. Nieprzewidywalne wyniki porównania są głównym powodem, że stosowanie == jest powszechnie uważane za złą -praktykę. Skomplikowane reguły konwersji są powodem trudnych do wyśledzenia błędy.

    +praktykę. Skomplikowane reguły konwersji są powodem trudnych do wyśledzenia błędów.

    -

    Ponadto koercja ma również wpływ na wydajność na przykład gdy typ String musi zostać +

    Ponadto koercja ma również wpływ na wydajność, Na przykład gdy typ String musi zostać przekształcony na typ Number przed porównaniem z drugą liczbą.

    Operator ścisłej równości

    Operator ścisłej równości składa się z trzech znaków "równa się": ===

    -

    Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem nie +

    Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem - nie dokonuje koercji typów przed porównaniem.

    ""           ===   "0"           // false
    @@ -1271,8 +1271,8 @@
     
     

    Porównywanie obiektów

    -

    Mimo, że oba operatory == i === nazywane są operatorami równościowymi, -to zachowują się różnie gdy jednym z operandów jest obiekt typu Object.

    +

    Mimo że oba operatory == i === nazywane są operatorami równościowymi, +to zachowują się różnie, gdy jednym z operandów jest obiekt typu Object.

    {} === {};                   // false
     new String('foo') === 'foo'; // false
    @@ -1282,19 +1282,19 @@
     

    Oba operatory porównują toższmość a nie równość, czyli będą porównywać czy -jeden i drugi operand jest tą samą instancją obiektu, podobnie jak operator -is w Pythonie i porównanie wskaźników w C.

    +jeden i drugi operand jest tą samą instancją obiektu (podobnie jak operator +is w Pythonie i porównanie wskaźników w C).

    Wnioski

    -

    Zaleca się aby używać tylko operatora ścisłej równości. W sytuacjach gdy +

    Zaleca się, aby używać tylko operatora ścisłej równości. W sytuacjach gdy potrzebna jest koercja (porównanie obiektów różnych typów), konwersja powinna -być dokonana jawnie a nie pozostawiona trudnym regułom koercji +być dokonana jawnie, a nie pozostawiona trudnym regułom koercji obowiązującym w języku.

    Operator typeof

    Operator typeof (razem z operatorem instanceof) jest -prawdopodobnie najwiekszą wadą konstrukcji języka JavaScript, jest on praktycznie -całkowicie wadliwy.

    +prawdopodobnie najwiekszą wadą konstrukcji języka JavaScript. Posiada on praktycznie
    +same wady.

    -

    Mimo, że instanceof ma swoje wady to nadal ma ograniczone zastosowanie w praktyce, +

    Mimo że instanceof ma swoje wady to nadal ma ograniczone zastosowanie w praktyce, natomiast typeof ma tylko jeden praktyczny przypadek użycia, który na dodatek nie jest związany z sprawdzaniem typu obiektu.

    @@ -1382,9 +1382,9 @@ implementacjach.

    O ile nie operator typeof nie jest użyty do sprawdzania czy zmienna została -zdefiniowana, powinien być unikany o ile to tylko możliwe.

    Operator instanceof

    Operator instanceof porównuje konstruktory obiektów przekazanych jako operendy. -Jest on jedynie użyteczny do porównywania obiektów utworzonych klas. Stosowanie -go na wbudowanych typach jest praktycznie tak samo bezużyteczne jak operatora +zdefiniowana, powinien być unikany jeśli to tylko możliwe.

    Operator instanceof

    Operator instanceof porównuje konstruktory obiektów przekazanych jako operendy. +Jest on użyteczny jedynie do porównywania obiektów utworzonych klas. Stosowanie +go na wbudowanych typach jest praktycznie tak samo bezużyteczne, jak operatora typeof.

    Porównywanie obiektów utworzonych klas

    @@ -1396,7 +1396,7 @@ new Bar() instanceof Bar; // true new Bar() instanceof Foo; // true -// Poniżej kod który przypisuje do Bar.prototype obiekt funkcji Foo +// poniżej kod który przypisuje do Bar.prototype obiekt funkcji Foo // a nie faktyczną instancję Foo Bar.prototype = Foo; new Bar() instanceof Foo; // false @@ -1418,51 +1418,51 @@

    Wnioski

    -

    Operator instanceof powinien być tylko używany podczas korzystania z obiektów +

    Operator instanceof powinien być używany wyłącznie podczas korzystania z obiektów klas utworzonych, które były zdefiniowane w tym samym kontekscie JavaScriptowym. Podobnie jak operator typeof, należy unikać korzystania -z tego operatora w innych sytuacjach.

    Rzutowanie typów

    JavaScript jest językiem słabo typowanym, co za tym idzie będzie stosować koercję +z tego operatora w innych sytuacjach.

    Rzutowanie typów

    JavaScript jest językiem słabo typowanym. Co za tym idzie, będzie stosować koercję typów gdziekolwiek jest to możliwe.

    -
    // These are true
    +
    // te zwracają true
     new Number(10) == 10; // Number.toString() zostanie przekształcone
                           // z powrotem do liczby
     
    -10 == '10';           // Stringi zostaną przekształcone do typu Number
    -10 == '+10 ';         // Kolejne wariacje
    +10 == '10';           // stringi zostaną przekształcone do typu Number
    +10 == '+10 ';         // kolejne wariacje
     10 == '010';          // i następne
     isNaN(null) == false; // null zostanie przekształcony do 0
                           // który oczywiście nie jest NaN
     
    -// Poniższe zwracają false
    +// poniższe zwracają false
     10 == 010;
     10 == '-10';
     
    -

    Aby uniknąć powyższych problemów, należy koniecznie skorzystać ze +

    Aby uniknąć powyższych problemów, należy koniecznie korzystać ze ściełego operatora równości. Mimo, że pozwala to uniknąć wiele typowych problemów to nadal istnieje wiele innych, które powstają na bazie słabego typowania języka JavaScript.

    Konstruktory typów wbudowanych

    -

    Konstruktory typów wbudowanych takich, jak Number lub String zachowują się -inaczej jeżeli są poprzedzone słowem kluczowym new a inaczej jeżeli nie są.

    +

    Konstruktory typów wbudowanych, takich jak Number lub String, zachowują się +inaczej kiedy są poprzedzone słowem kluczowym new a inaczej kiedy nie są.

    new Number(10) === 10;     // False, Object i Number
     Number(10) === 10;         // True, Number i Number
     new Number(10) + 0 === 10; // True, ponieważ dokonano jawnej konwersji
     
    -

    Korzystanie z wbudowanych typów jak Number jako konstruktor utworzy nowy obiekt -typu Number, natomiast opuszczenie słowa kluczowego new spowoduje, że funkcja -Number zachowa się jak konwerter.

    +

    Korzystanie z wbudowanych typów jak Number jako konstruktora tworzy nowy obiekt +typu Number, natomiast opuszczenie słowa kluczowego new powoduje, że funkcja +Number zachowuje się jak konwerter.

    Ponadto, użycie literałów lub wartości nieobiektowych zaowocuje jeszcze większą ilością rzutowań (koercją) typów.

    @@ -1485,7 +1485,7 @@

    Rzutowanie do typu Boolean

    -

    Używając dwukrotnie operatora negacji dowolna wartość może zostać zrzutowana +

    Używając dwukrotnie operatora negacji, dowolna wartość może zostać zrzutowana do typu Boolean

    !!'foo';   // true
    @@ -1734,7 +1734,7 @@
     if / else bez nawiasów - nawet, jeżeli są jednolinijkowe. Wszystkie te uwagi nie 
     tylko pomagają poprawić spójność kodu, ale też zapobiegają zmianie działania 
     kodu przez parser JavaScript.

    Inne

    setTimeout i setInterval

    Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania -funkcji korzystając z funkcji setTimeout i setInterval. +funkcji przy użyciu funkcji setTimeout i setInterval. Since JavaScript is asynchronous, it is possible to schedule the execution of a function by using the setTimeout and setInterval functions.

    @@ -1744,7 +1744,7 @@
    function foo() {}
    -var id = setTimeout(foo, 1000); // zwraca licznę typu Number > 0
    +var id = setTimeout(foo, 1000); // zwraca liczbę typu Number > 0
     

    Powyższe wywołanie setTimeout zwraca ID budzika i planuje wywołanie foo za @@ -1754,11 +1754,11 @@ upłynięciu zadanego czasu podanego jako parametr do setTimeout, ponieważ zależy to od dokładności zegara w silniku JavaScript, który wykonuje kod oraz od tego, że inny kawałek kodu może zablokować wątek, ponieważ JavaScript jest tylko -jedno wątkowy.

    +jednowątkowy.

    Funkcja, która została przekazana jako pierwszy parametr zostanie wykonana w globalnym zasięgu, co oznacza, że this wewnątrz tej funkcji -będzie wkazywać na obiekt global.

    +będzie wskazywać na obiekt global.

    function Foo() {
         this.value = 42;
    @@ -1773,23 +1773,23 @@
     
     
     
     

    Kolejkowanie wywołań z setInterval

    -

    Podczas, gdy setTimeout wywołuje podaną funkcję tylko raz, setInterval - +

    Podczas gdy setTimeout wywołuje podaną funkcję tylko raz, setInterval - jak wskazuje nazwa - będzie wykonywać funkcję w odstępach czasowych co X milisekund. Jednakże korzystanie z tej funkcji jest odradzane.

    Kiedy wykonywany kod zablokuje możliwość uruchomienia zaplanowanej funkcji, -setInterval będzie próbować uruchamiać daną funkcję co będzie powodować -kolejkowanie wykonania tej samej funkcji kilkakrotnie. W szczególności może się -to wydarzyć przy krótkim interwale.

    +setInterval będzie próbować uruchamiać daną funkcję, co będzie powodować +kolejkowanie wykonania tej samej funkcji kilkukrotnie. Może się to zdażyć +szczególnie przy krótkim interwale.

    function foo(){
         // coś co blokuje wykonanie na 1 sekundę 
    @@ -1800,13 +1800,13 @@
     

    W powyższym kodzie kod foo zostanie wywołany tylko raz i zablokuje wywołanie na jedną sekundę.

    -

    Podczas, gdy funkcja foo blokuje wykonanie setInterval będzie planować kolejne -wywołania foo. W momencie, gdy pierwsze wywołanie foo się zakończy, już -w kolejce do wywołania będą czekały kolejne dziesięć wywołań tej funkcji.

    +

    Podczas, gdy funkcja foo blokuje wykonanie, setInterval będzie planować kolejne +wywołania foo. W momencie, gdy pierwsze wywołanie foo się zakończy, +w kolejce do wywołania będzie już czekało kolejne dziesięć wywołań tej funkcji.

    Radzenie sobie z możliwymi blokadami

    -

    Najprostrzym jak również najbardziej kontrolowaną sytuacją jest użycie setTimeout +

    Najprostszą, jak również najbardziej kontrolowaną sytuacją, jest użycie setTimeout wewnątrz wywoływanej funkcji.

    function foo(){
    @@ -1816,9 +1816,9 @@
     foo();
     
    -

    Powyższy kod nie tylko hermetyzuje wywołanie setTimeout ale również zapobiega -kolejkowaniu wywołań fukcji i daje dodatkową kontrolę. W tym przypadku funkcja -foo może zdecydować czy powinna się wywołać ponownie czy też nie.

    +

    Powyższy kod nie tylko hermetyzuje wywołanie setTimeout, ale też zapobiega +kolejkowaniu wywołań funkcji i daje dodatkową kontrolę. W tym przypadku funkcja +foo może zdecydować czy powinna się wywołać ponownie, czy też nie.

    Ręczne usuwanie budzików

    @@ -1832,8 +1832,8 @@

    Usuwanie wszystkich budzików

    -

    Ponieważ nie istnieje wbudowana metoda na usunięcie wszystkich budzików i/lub -interwałów, konieczne jest użycie metody brute force aby osiągnąć ten efekt.

    +

    Ponieważ nie istnieje wbudowana metoda usuwania wszystkich budzików i/lub +interwałów, do osiągnięcia tego efektu konieczne jest użycie metody 'brute force'.

    // usunięcie "wszystkich" budzików 
     for(var i = 1; i < 1000; i++) {
    @@ -1841,9 +1841,9 @@
     }
     
    -

    Nadal może istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała, -ponieważ ID było z innego przedziału, dlatego zamiast korzystania z metody brute -force, zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć.

    +

    Nadal mogą istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała. +Ponieważ ID było z innego przedziału, zamiast korzystania z metody brute force, +zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć.

    Ukryte wykorzystanie eval

    @@ -1871,11 +1871,11 @@ bar(); -

    Ponieważ eval nie zostało wywołane wprost w tym przypadku, to -string przekazany do setTimeout zostanie uruchomiony w zasięgu globalnym, -co za tym idzie lokalna zmienna foo z zasięgu bar nie zostanie użyta.

    +

    Ponieważ eval nie zostało wywołane w tym przypadku wprost, to +string przekazany do setTimeout zostanie uruchomiony w zasięgu globalnym. +Co za tym idzie, lokalna zmienna foo z zasięgu bar nie zostanie użyta.

    -

    Kolejnym zaleceniem jest aby nie stosować stringów do przekazywania argumentów +

    Kolejnym zaleceniem jest niestosowanie stringów do przekazywania argumentów do funkcji, która ma zostać wywołana przez budzik.

    function foo(a, b, c) {}
    @@ -1883,26 +1883,26 @@
     // NIGDY nie należy tak robić 
     setTimeout('foo(1,2, 3)', 1000)
     
    -// Zamiast tego należy skorzystać z anonimowej funkcji
    +// zamiast tego należy skorzystać z anonimowej funkcji
     setTimeout(function() {
         foo(a, b, c);
     }, 1000)
     

    Wnioski

    -

    Nie należy nigdy przekazywać stringu jako parametru do setTimeout lub -setInterval. Jest to wyraźną oznaką bardzo złego kodu, jeżeli potrzebne jest -przekazanie argumentów do funkcji należy skorzystać z anonimowej funkcji i +

    Nigdy nie należy przekazywać stringu jako parametru do setTimeout lub +setInterval. Jest to wyraźną oznaką bardzo złego kodu. Jeżeli potrzebne jest +przekazanie argumentów do funkcji, należy skorzystać z anonimowej funkcji i wewnątrz niej dokonać przekazania argumentów.

    -

    Ponadto, należy unikać korzystanie z setInterval, ponieważ planista może +

    Ponadto, należy unikać korzystania z setInterval, ponieważ planista może zablokować wykonanie JavaScriptu.