From 46d0058aa6258a12306b0fdc84fbfabe155b6f7f Mon Sep 17 00:00:00 2001 From: "ByungDae, Sohn" Date: Wed, 21 Nov 2012 13:42:31 +0900 Subject: [PATCH 001/216] corrected mistranslation and full korean translation improved --- .project | 11 +++++ doc/ko/array/constructor.md | 10 ++--- doc/ko/array/general.md | 20 ++++----- doc/ko/core/delete.md | 16 +++---- doc/ko/core/eval.md | 8 ++-- doc/ko/core/semicolon.md | 12 +++--- doc/ko/core/undefined.md | 9 ++-- doc/ko/function/arguments.md | 27 ++++++------ doc/ko/function/closures.md | 30 ++++++------- doc/ko/function/constructors.md | 24 ++++++----- doc/ko/function/general.md | 28 ++++++------- doc/ko/function/scopes.md | 74 ++++++++++++++++----------------- doc/ko/function/this.md | 10 ++--- doc/ko/intro/index.md | 3 +- doc/ko/object/forinloop.md | 16 +++---- doc/ko/object/general.md | 23 +++++----- doc/ko/object/hasownproperty.md | 18 ++++---- doc/ko/object/prototype.md | 36 ++++++++-------- doc/ko/other/timeouts.md | 55 ++++++++++++------------ doc/ko/types/casting.md | 6 +-- doc/ko/types/equality.md | 26 ++++++------ doc/ko/types/instanceof.md | 10 ++--- doc/ko/types/typeof.md | 20 ++++----- 23 files changed, 253 insertions(+), 239 deletions(-) create mode 100644 .project diff --git a/.project b/.project new file mode 100644 index 00000000..42a345c2 --- /dev/null +++ b/.project @@ -0,0 +1,11 @@ + + + JavaScript-Garden + + + + + + + + diff --git a/doc/ko/array/constructor.md b/doc/ko/array/constructor.md index 471fd60d..0c60a842 100644 --- a/doc/ko/array/constructor.md +++ b/doc/ko/array/constructor.md @@ -1,6 +1,6 @@ -## `Array` 생성자 +## `배열` 생성자 -`Array` 생성자가 파라미터를 처리하는 방법은 모호하다. 그래서 항상 `[]` 노테이션으로 Array를 만들어야 한다. +배열을 만들때 `배열` 생성자에 파라미터를 넣어 만드는 방법은 헷갈릴수있다. 그래서 항상 각 괄호(`[]`) 노테이션을 이용해 배열을 만들 것을 권한다 [1, 2, 3]; // Result: [1, 2, 3] new Array(1, 2, 3); // Result: [1, 2, 3] @@ -9,16 +9,16 @@ new Array(3); // Result: [] new Array('3') // Result: ['3'] -`Array` 생성자에 인자로 숫자를 넘기면 `length`가 그 숫자인 텅 빈 `Array` 하나를 반환된다. 생성자는 **오직** `length` 프로퍼티에 그 숫자를 할당하기만 하고 `Array`는 실제로 초기화하지 않는다. +`배열` 생성자에 숫자를 인자로 넣으면 그 숫자 크기 만큼의 빈 `배열`을 반환한다. 즉 배열의 `length`는 그 숫자가 된다. 이때 생성자는 **단지** `length` 프로퍼티에 그 숫자를 할당하기만 하고 `배열`은 실제로 초기화 하지도 않는다. var arr = new Array(3); arr[1]; // undefined 1 in arr; // false, 이 인덱스는 초기화되지 않음. -Array의 length 프로퍼티에 숫자를 할당해주는 이 기능이 유용할 때도 있긴 있다. `for loop`을 사용하지 않고 스트링을 더할 때가 그렇다. +`for`문을 사용하지 않고 문자열을 더하는 경우에는 length 프로퍼티에 숫자를 할당해주는 기능이 유용할 때도 있다. new Array(count + 1).join(stringToRepeat); ### 결론 -`Array` 생성자는 가능하면 사용하지 말아야 한다. `[]` 노테이션이 더 알맞다. 더 간략하고 명확하기 때문에 보기도 좋다. +`배열` 생성자는 가능하면 사용하지 말고, 각 괄호 (`[]`) 노테이션이을 사용하자. 후자가 더 간략하고 명확할 뿐만 아니라 보기도 좋다. diff --git a/doc/ko/array/general.md b/doc/ko/array/general.md index 0a396d2e..1a665fa3 100644 --- a/doc/ko/array/general.md +++ b/doc/ko/array/general.md @@ -1,29 +1,29 @@ -## Array Iteration과 프로퍼티 +## 배열 순회와 프로퍼티 -JavaScript에서는 Array도 객체 Iterate를 할 때 [`for in`](#object.forinloop)을 사용해서 좋을 게 없다. 실제로 Array에 `for in`을 사용하지 말아야 할 근거가 매우 많다. +JavaScript에서는 배열(Array)도 객체(Object)지만 객체 순회(Iterate)를 할 때 [`for in`](#object.forinloop)을 사용해서 좋을 게 없다. 실제로 배열을 탐색할때 `for in`문 사용하지 말아야 할 이유가 매우 많다. -> **Note:** JavaScript의 Array는 *Associative Array*가 **아니다**. JavaScript [객체](#object.general)는 key/value만 Mapping할 뿐이다. Associative Array는 순서를 보장하지만, 객체는 보장하지 않는다. +> **Note:** JavaScript의 배열은 *연관 배열(Associative Array)*이 **아니다**. JavaScript는 오직 key/value를 맵핑한 [객체](#object.general)만 있을 뿐이다. 연관 배열은 순서를 보장해주지만 객체는 순서를 보장하지 않는다. `for in`은 프로토타입 체인에 있는 프로퍼티를 모두 훑는(enumerate) 데다가 객체 자신의 프로퍼티만 훑으려면 [`hasOwnProperty`](#object.hasownproperty)를 사용해야 하기 때문에 `for`보다 20배 느리다. -### Iteration +### 배열 순회 -Array를 Iterate할 때에는 구식인 `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`로 Array의 length 값을 캐시해야 한다는 것을 꼭 기억해야 한다. +이 예제에서 `l = list.length`로 배열의 length 값을 캐시해야 한다는 것을 꼭 기억해야 한다. -매 Iterate마다 Array에 있는 `length` 프로퍼티에 접근하는 것은 좀 부담스럽다. 최신 JavaScript 엔진은 이 일을 알아서 처리해주기도 하지만 코드가 늘 새 엔진에서 실행되도록 보장할 방법이 없다. +매번 반복할때마다 배열에 있는 `length` 프로퍼티에 접근하는 것은 좀 부담스럽다. 최신 JavaScript 엔진은 이 일을 알아서 처리해주기도 하지만 코드가 늘 새 엔진에서 실행되도록 보장할 방법이 없다. 실제로 캐시 하지 않으면 성능이 반으로 줄어든다. ### `length` 프로퍼티 -`length` 프로퍼티의 *getter*는 단순히 Array 안에 있는 엘리먼트의 개수를 반환하고 *setter*는 Array를 할당한 수만큼 잘라 버린다. +`length` 프로퍼티의 *getter*는 단순히 Array 안에 있는 엘리먼트의 개수를 반환하고 *setter*는 배열을 할당한 수만큼 잘라 버린다. var foo = [1, 2, 3, 4, 5, 6]; foo.length = 3; @@ -32,8 +32,8 @@ Array를 Iterate할 때에는 구식인 `for`를 사용하는 것이 가장 빠 foo.length = 6; foo; // [1, 2, 3] -현재 크기보다 더 작은 값을 할당하면 Array를 자르지만, 현재 크기보다 더 큰 값을 할당한다고 해서 Array를 늘리지 않는다. +현재 크기보다 더 작은 값을 할당하면 배열을 자르지만, 현재 크기보다 더 큰 값을 할당한다고 해서 배열을 늘리진 않는다. ### 결론 -최적의 성능을 위해서는 `for`를 사용하고 `length` 프로퍼티 값을 캐시해야 한다. Array에 `for in`을 사용하면 성능도 떨어지고 버그 나기도 쉽다. +최적의 성능을 위해서는 `for`문을 사용하고 `length` 프로퍼티 값을 캐시해야 한다. 배열에 `for in`을 사용하면 성능도 떨어지고 버그 나기도 쉽다. diff --git a/doc/ko/core/delete.md b/doc/ko/core/delete.md index 342c04e9..7f008b2c 100644 --- a/doc/ko/core/delete.md +++ b/doc/ko/core/delete.md @@ -1,10 +1,10 @@ -## `delete` +## `delete` 연산자 -간단히 말해서 global 변수, Function, 등은 `DontDelete` 속성이기 때문에 delete하지 못 한다. +간단히 말해서 전역 변수와 전역 함수 그리고 `DontDelete` 속성을 가진 자바스크립트 객체는 삭제할 수 없다. ### Global 코드와 Function 코드 -Global이나 Function Scope에 정의한 Fuction이나 변수는 모두 Activation 객체나 Global 객체의 프로퍼티다. 이 프로퍼티는 모두 `DontDelete` 속성을 가진다. Global이나 Function 코드에서 변수나 Function의 정의하면 항상 `DontDelete` 프로퍼티로 만들어진다. 그러니까 delete할 수 없다: +전역이나 함수 스코프에 정의한 함수나 변수는 모두 Activation 객체나 전역 객체의 프로퍼티다. 이 프로퍼티는 모두 `DontDelete` 속성을 가진다. 전역이나 함수 코드에 정의한 변수와 함수는 항상 `DontDelete` 프로퍼티로 만들어지기 때문에 삭제될 수 없다: // Global 변수: var a = 1; // DontDelete가 설정된다. @@ -21,9 +21,9 @@ Global이나 Function Scope에 정의한 Fuction이나 변수는 모두 Activati delete f; // false f; // 1 -### Explicit 프로퍼티 +### 명시적인(Explicit) 프로퍼티 -다음 예제에서 만드는 프로퍼티는 delete할 수 있다. 이런 걸 Explicit 프로퍼티라고 부른다: +다음 예제에서 만드는 프로퍼티는 delete할 수 있다. 이런 걸 명시적인(Explicit) 프로퍼티라고 부른다: // Explicit 프로퍼티를 만든다: var obj = {x: 1}; @@ -33,7 +33,7 @@ Global이나 Function Scope에 정의한 Fuction이나 변수는 모두 Activati obj.x; // undefined obj.y; // undefined -`obj.x`와 `obj.y`는 `DontDelete` 속성이 아니라서 delete할 수 있다. 그러나 다음과 같은 코드도 잘 동작하기 때문에 헷갈린다: +`obj.x`와 `obj.y`는 `DontDelete` 속성이 아니라서 delete할 수 있다. 하지만 다음과 같은 코드도 잘 동작하기 때문에 헷갈린다: // IE를 빼고 잘 동작한다: var GLOBAL_OBJECT = this; @@ -42,7 +42,7 @@ Global이나 Function Scope에 정의한 Fuction이나 변수는 모두 Activati delete GLOBAL_OBJECT.a; // true GLOBAL_OBJECT.a; // undefined -[`this`](#function.this)가 Global 객체를 가리키는 것을 이용해서 명시적으로 프로퍼티 `a`를 선언하면 삭제할 수 있다. 이것은 꼼수다. +[`this`](#function.this)가 전역 객체를 가리키는 것을 이용해서 명시적으로 프로퍼티 `a`를 선언하면 삭제할 수 있다. 이것은 꼼수다. IE (적어도 6-8)는 버그가 있어서 안 된다. @@ -73,4 +73,4 @@ Host 객체를 delete하면 어떻게 될지 알 수 없다. 표준에는 어떻 ### 결론 -`delete` 연산자는 엉뚱하게 동작할 때가 잦다. 명시적으로 정의한 일반 객체의 프로퍼티만 delete하는 것이 안전하다. +`delete` 연산자는 엉뚱하게 동작할 때가 많다. 명시적으로 정의한 일반 객체의 프로퍼티만 delete하는 것이 안전하다. diff --git a/doc/ko/core/eval.md b/doc/ko/core/eval.md index 80706598..528b1883 100644 --- a/doc/ko/core/eval.md +++ b/doc/ko/core/eval.md @@ -1,6 +1,6 @@ ## 왜 `eval`을 사용하면 안 될까? -`eval` 함수는 스트링으로 된 JavaScript 코드를 Local Scope에서 실행한다. +`eval` 함수는 JavaScript 문자열을 지역 스코프에서 실행한다. var foo = 1; function test() { @@ -11,7 +11,7 @@ test(); // 3 foo; // 1 -`eval`을 `eval`이라는 이름으로 **직접** 직행할 때에만 Local Scope에서 실행된다. +`eval`함수는 `eval`이라는 이름으로 **직접** 실행할 때에만 지역 스코프에서 실행된다. 그리고 `eval`이라는 이름에 걸맞게 악명또한 높다. var foo = 1; function test() { @@ -23,7 +23,7 @@ test(); // 2 foo; // 3 -어쨌든 `eval`은 사용하지 말아야 한다. eval을 사용하는 경우의 99.9%는 사실 eval이 필요 없다. +어쨌든 `eval`은 사용하지 말아야 한다. eval을 사용하는 99.9%는 사실 eval 없이도 만들수있다. ### 가짜 `eval` @@ -31,7 +31,7 @@ ### 보안 이슈 -`eval`은 보안 문제도 있다. 단순히 **모든** 코드를 실행하기 때문에 신뢰하지 못하는 코드가 **절대로** 포함되지 않도록 주의해야 한다. +`eval`은 어떤 코드라도 **무조건** 실행하기 때문에 보안 문제도 있다. 따라서 신뢰하지 못하거나 모르는 코드가 포함되어 있을 경우 **절대로** 사용해서는 안된다. ### 결론 diff --git a/doc/ko/core/semicolon.md b/doc/ko/core/semicolon.md index f58e1782..7a384caa 100644 --- a/doc/ko/core/semicolon.md +++ b/doc/ko/core/semicolon.md @@ -1,6 +1,6 @@ -## 쎄미콜론을 자동으로 삽입해준다. +## 자동으로 삽입되는 쎄미콜론 -JavaScript는 C와 문법이 비슷하지만, 꼭 코드에 쎄미콜론을 사용하도록 강제하지 않는다. 그래서 생략할 수 있다. +JavaScript는 C와 문법이 비슷하지만, 꼭 코드에 쎄미콜론을 사용하도록 강제하지는 않는다. 그래서 생략할 수 있다. 사실 JavaScript는 쎄미콜론이 꼭 있어야 하고 없으면 이해하지 못한다. 그래서 JavaScript 파서는 쎄미콜론이 없으면 **자동으로** 쎄미콜론을 추가한다. @@ -81,18 +81,18 @@ JavaScript는 C와 문법이 비슷하지만, 꼭 코드에 쎄미콜론을 사 파서는 완전히 다른 코드로 만들어 버린다. 이것은 **오류**다. -### Parenthesis +### 괄호 해석 -쎄미콜론 없이 괄호가 붙어 있으면 파서는 쎄미콜론을 넣지 않는다. +파서는 괄호에는 쎄미콜론을 넣지 않는다. log('testing!') (options.list || []).forEach(function(i) {}) -파서는 다음과 같이 코드를 바꾼다. +그래서 다음과 같이 한줄로 코드를 바꾼다. log('testing!')(options.list || []).forEach(function(i) {}) -`log` 함수가 함수를 반환할 가능성은 거의 없다. 아마도 `undefined is not a function`이라는 `TypeError`가 발생할 거다. +이렇게 한줄로 바뀌면 `log` 함수가 함수를 반환할 가능성이 거의 없으므로 `undefined is not a function`이라는 `TypeError`가 발생한다. ### 결론 diff --git a/doc/ko/core/undefined.md b/doc/ko/core/undefined.md index 65748e1e..2bacc83b 100644 --- a/doc/ko/core/undefined.md +++ b/doc/ko/core/undefined.md @@ -1,6 +1,6 @@ ## `undefined`와 `null` -JavaScript는 `nothing`을 두 가지로 표현할 수 있고 그중 `undefined`가 더 유용하다. +JavaScript는 `nothing`을 표현할때 `null`과 `undefined` 두 가지로 표현할 수 있고 그중 `undefined`가 더 유용하다. ### `undefined`도 변수 @@ -13,9 +13,8 @@ JavaScript는 `nothing`을 두 가지로 표현할 수 있고 그중 `undefined` `undefined` 값이 반환될 때: - global 변수 `undefined`에 접근할 때. - - 아직 초기화하지 않은 변수 - - `return` 구문이 없는 함수는 `undefined`를 반환함. - - `return` 구문이 없는 함수는 `undefined`를 반환함. + - 선언은 했지만 아직 초기화하지 않은 변수에 접근할 때. + - `return` 구문이 없는 함수는 암묵적으로 `undefined`를 반환함. - `return` 구문으로 아무것도 반환하지 않을 때. - 없는 프로퍼티를 찾을 때. - 함수 인자가 생략될 때. @@ -30,7 +29,7 @@ global 변수 `undefined`는 `undefined`라는 객체를 가리키는 것뿐이 그래서 `undefined`와 비교하려면 먼저 `undefined`의 값을 찾아와야 한다. -`undefined` 변수가 바뀔 때를 대비해서 `undefined`라는 변수를 인자로 받는 [anonymous wrapper](#function.scopes)로 감싸고 아무런 인자를 넘기지 않는 꼼수를 사용한다. +`undefined` 변수가 바뀔 때를 대비해서 `undefined`라는 변수를 인자로 받는 [anonymous wrapper](#function.scopes)로 감싸고 인자를 넘기지 않는 꼼수를 사용한다. var undefined = 123; (function(something, foo, undefined) { diff --git a/doc/ko/function/arguments.md b/doc/ko/function/arguments.md index cda8407c..dcc5e8a2 100644 --- a/doc/ko/function/arguments.md +++ b/doc/ko/function/arguments.md @@ -1,12 +1,12 @@ ## `arguments` 객체 -JavaScript의 모든 Function Scope에는 `arguments`라는 특별한 변수가 있다. 이 변수는 Function에 넘겨진 모든 인자에 대한 정보가 담겨 있다. +JavaScript의 모든 함수 스코프에는 `arguments`라는 특별한 변수가 있다. 이 변수는 함수에 넘겨진 모든 인자에 대한 정보가 담겨 있다. > **Note:** `arguments` 변수는 Function 안에서 다시 정의할 수 없다. `var` 구문이나 파라미터에 `arguments`라는 이름으로 변수를 정의해도 변수가 재정의되지 않는다. -`length` 프로퍼티도 있는 데다가 여러모로 Array와 비슷하게 생겼지만 Array.prototype을 상속받지 않았다. `arguments` 객체는 `Array`가 아니다. +`arguments` 객체는 `Array`가 아니다. 물론 `length` 프로퍼티도 있고 여러모로 Array와 비슷하게 생겼지만 Array.prototype을 상속받지는 않았다. -그래서 `arguments`에는 `push`, `pop`, `slice` 같은 표준 메소드가 없다. `for`로 하는 Iteration은 원래 잘되지만 `Array`의 메소드를 이용하려면 `arguments`를 Array로 변환해야 한다. +그래서 `arguments`에는 `push`, `pop`, `slice` 같은 표준 메소드가 없다. 일반 `for`문을 이용해 순회는 할수 있지만, `Array`의 메소드를 이용하려면 `arguments`를 Array로 변환해야 한다. ### Array로 변환하기 @@ -18,7 +18,7 @@ JavaScript의 모든 Function Scope에는 `arguments`라는 특별한 변수가 ### arguemnts 객체 넘기기 -어떤 Function에서 다른 Function로 arguments 객체를 넘길 때에는 다음과 같이 하는 것이 좋다. +어떤 함수에서 다른 함수로 arguments 객체를 넘길 때에는 다음과 같은 방법을 권한다. (역주: foo 함수는 bar 함수 한번 랩핑한 함수다. ) function foo() { bar.apply(null, arguments); @@ -27,7 +27,7 @@ JavaScript의 모든 Function Scope에는 `arguments`라는 특별한 변수가 // 내곡동에 땅이라도 산다. } -`call`과 `apply`를 함께 사용하여 unbound wrapper도 쉽게 만들 수 있다. +또 다른 방법으로는 함수를 랩핑하지 않고, 풀어서 `call`과 `apply`를 함께 사용하는 방법이 있다. (역주: 프로토타입에 있는 method를 호출하기 전에 Foo 객체 안에 있는 method로 한번더 필터링하는 효과가 있다. ) function Foo() {} @@ -35,7 +35,7 @@ JavaScript의 모든 Function Scope에는 `arguments`라는 특별한 변수가 console.log(this, a, b, c); }; - // "method"의 unbound 버전 + // "method"를 풀어 쓴(unbound) 버전 // 이 Function의 인자: this, arg1, arg2...argN Foo.method = function() { @@ -43,9 +43,9 @@ JavaScript의 모든 Function Scope에는 `arguments`라는 특별한 변수가 Function.call.apply(Foo.prototype.method, arguments); }; -### 파라미터와 arguments 객체 인덱스 +### 일반 파라미터와 arguments 객체의 인덱스 -파라미터와 `arguments` 객체의 프로퍼티는 모두 *getter*와 *setter*를 가진다. +일반 파라미터와 `arguments` 객체의 프로퍼티는 모두 *getter*와 *setter*를 가진다. 그래서 파라미터나 `arguments` 객체의 프로퍼티의 값을 바꾸면 둘 다 바뀐다. @@ -62,7 +62,7 @@ JavaScript의 모든 Function Scope에는 `arguments`라는 특별한 변수가 } foo(1, 2, 3); -### 성능에 대한 진실과 오해. +### 성능에 대한 오해와 진실. `arguments` 객체는 항상 만들어 지지만 예외도 있다. `arguments`라는 이름의 변수를 Function 안에 정의하거나 그 이름으로 파라미터를 만들면 `arguemnts` 객체는 만들어지지 않는다. 그렇지만, 이럴때는 어차피 안쓰겠다는 의미니까 상관 없다. @@ -73,8 +73,8 @@ JavaScript의 모든 Function Scope에는 `arguments`라는 특별한 변수가 그러나 예외도 있다. 최신 JavaScript 엔진에서 `arguments.callee`를 사용하면 성능이 확 떨어진다. function foo() { - arguments.callee; // 이 Function를 가리킨다. - arguments.callee.caller; // 이 Function를 호출한 Function를 가리킨다. + arguments.callee; // 이 함수를 가리킨다. + arguments.callee.caller; // 이 함수를 호출한 부모함수를 가리킨다. } function bigLoop() { @@ -83,9 +83,10 @@ JavaScript의 모든 Function Scope에는 `arguments`라는 특별한 변수가 } } -이 코드에서 Callee와 Caller를 알아야 하기 때문에 `foo`는 더는 [인라인][1]하지 않는다. 이렇게 쓰면 인라인이 주는 성능상 장점을 포기해야 하는데다가 Function이 호출되는 상황(calling context)에 의존하게 돼 버려서 Encapsulation도 해친다. +위 코드에서 'foo' 함수는 자기 자신과 자신을 호출한 함수를 알아야 하기 때문에 더이상 [인라인][1]되지 않는다. 이렇게 쓰면 인라인이 주는 성능상 장점을 포기해야 하는데다가 이 함수가 호출되는 상황(calling context)에 의존하게 돼 버려서 캡슐화(Encapsulation)도 해친다. +(역주: 보통 코드가 컴파일 될때 코드를 인라인 시키면서 최적화 하는데, 위와 같이 arguments.callee나 caller를 사용하게 되면 런타임시에 해당 함수가 결정되므로 인라인 최적화를 할수가 없다.) -`arguments.callee`와 그 프로퍼티들은 **절대** 사용하지 말아야 한다. +`arguments.callee`와 arguments.callee의 프로퍼티들은 **절대** 사용하지 말자!. > **ES5 Note:** strict 모드에서 `arguments.callee`는 deprecated됐기 때문에 사용하면 `TypeError`가 난다. diff --git a/doc/ko/function/closures.md b/doc/ko/function/closures.md index 49628231..4d7463a6 100644 --- a/doc/ko/function/closures.md +++ b/doc/ko/function/closures.md @@ -1,8 +1,8 @@ -## Closure와 Reference +## 클로져(Closure)와 참조(Reference) -*Closure*는 JavaScript의 특장점 중 하나다. Closure에서는 그 Closure를 만든 외부 Scope에 접근할 있다. JavaScript에서 Scope을 만들려면 [Function Scope](#function.scopes)을 사용하는 방법밖에 없기 때문에 Closure는 함수로 만든다. +*클로져*는 JavaScript의 특장점 중 하나다. 클로저를 만들면 클로저 스코프 안에서 클로저를 만든 외부 스코프(Scope)에 항상 접근할 있다. JavaScript에서 스코프는 [함수 스코프](#function.scopes)밖에 없기 때문에 기본적으로 모든 함수는 클로저가 될수있다. -### private 변수 +### private 변수 만들기 function Counter(start) { var count = start; @@ -21,22 +21,22 @@ foo.increment(); foo.get(); // 5 -`Counter`는 `increment` Closure와 `get` Closure 두 개를 반환한다. 이 두 Closure는 `Counter` Scope에 대한 **reference**를 유지하고 있기 때문에 그 Scope에 있는 count 변수에 계속 접근할 수 있다. +여기서 `Counter`는 `increment` 클로저와 `get` 클로저 두 개를 반환한다. 이 두 클로저는 `Counter` 함수 스코프에 대한 **참조**를 유지하고 있기 때문에 이 함수 스코프에 있는 count 변수에 계속 접근할 수 있다. -### Private 변수가 진짜 맞나? +### Private 변수의 동작 원리 -JavaScript에서 Scope을 어딘가에 할당하거나 저장해두는 것이 불가능하다. 그래서 Scope 밖에서는 count 변수에 직접 접근할 수 없다. 꼭 Scope 안에서 정의한 두 closure를 통해서만 접근할 수 있다. +JavaScript에서는 스코프(Scope)를 어딘가에 할당해두거나 참조할수 없기 때문에 스코프 밖에서는 count 변수에 직접 접근할 수 없다. 접근할수 있는 유일한 방법은 스코프 안에 정의한 두 클로저를 이용하는 방법밖에 없다. var foo = new Counter(4); foo.hack = function() { count = 1337; }; -이 코드의 count는 `Counter` Scope의 변수 count가 아니다. `foo.hack`은 그 Scope 안에 정의되지 않았기 때문에 이 `count`는 *Global* 변수를 사용하는 것이다. +위 코드에서 `foo.hack` 함수는 Counter 함수 안에서 정의되지 않았기 때문에 이 함수가 실행되더라도 `Counter` 함수 스코프 안에 있는 count 값은 변하지 않는다. 대신 foo.hack 함수의 `count`는 *Global* 스코프에 생성되거나 이미 만들어진 변수를 덮어쓴다. -### Loop에서 Closure 사용하기 +### 반복문에서 클로저 사용하기 -많은 사람은 Loop에서 Closure를 사용할 때 자주 index 변수를 잘못 사용한다. +사람들이 반복문에서 클로저를 사용할 때 자주 실수를 하는 부분이 있는데 바로 인덱스 변수를 복사할때 발생한다. for(var i = 0; i < 10; i++) { setTimeout(function() { @@ -46,13 +46,13 @@ JavaScript에서 Scope을 어딘가에 할당하거나 저장해두는 것이 이 코드는 `0`부터 `9`까지의 수를 출력하지 않고 `10`만 열 번 출력한다. -이 *Anonymous* Function은 변수 `i`에 대한 참조를 저장했다가 `console.log`가 호출되는 시점에 `i`의 값을 사용한다. `console.log`가 호출되는 시점은 `for loop`이 이미 끝난 상태라서 `i` 값은 10이다. +타이머에 설정된 *익명* 함수는 변수 `i`에 대한 참조를 들고 있다가 `console.log`가 호출되는 시점에 `i`의 값을 사용한다. `console.log`가 호출되는 시점에서 `for loop`는 이미 끝난 상태기 때문에 `i` 값은 10이 된다. 기대한 결과를 얻으려면 `i` 값을 복사해 두어야 한다. -### 이 Reference 문제 해결하기 +### 앞의 참조 문제 해결하기 -[Anonymous Wrapper](#function.scopes)로 index 값을 복사하는 것이 좋다. +반복문의 index 값을 복사하는 가장 좋은 방법은 익명함수로 랩핑[Anonymous Wrapper](#function.scopes)하는 방법이다. for(var i = 0; i < 10; i++) { (function(e) { @@ -62,11 +62,11 @@ JavaScript에서 Scope을 어딘가에 할당하거나 저장해두는 것이 })(i); } -이 Anonymous Function의 인자로 `i`를 넘기면 이 함수의 파라미터 e에 i의 **값**이 복사된다. +이 익명 함수에 `i`를 인자로 넘기면 이 함수의 파라미터 e에 i의 **값**이 복사되어 넘어갈 것이다. -이 `setTimeout`는 anonymous function 파라미터인 `e`에 대한 참조를 갖게 되고 `e`는 loop의 상태에 따라 변하지 않는다. +그리고 `setTimeout`는 익명 함수의 파라미터인 `e`에 대한 참조를 갖게 되고 `e`값은 복사되어 넘어왔으므로 loop의 상태에 따라 변하지 않는다. -함수를 반환하는 Anonymous Wrapper를 이용하는 방법도 있다. 다음 코드는 위 코드와 같다. +또다른 방법으로 랩핑한 익명 함수에서 출력 함수를 반환하는 방법도 있다. 아래 코드는 위 코드와 동일하게 동작한다. for(var i = 0; i < 10; i++) { setTimeout((function(e) { diff --git a/doc/ko/function/constructors.md b/doc/ko/function/constructors.md index cb71cdeb..1aa6de4a 100644 --- a/doc/ko/function/constructors.md +++ b/doc/ko/function/constructors.md @@ -1,10 +1,10 @@ ## 생성자 -JavaScript에서 생성자는 다른 언어들과 다르게 `new` 키워드로 호출되는 함수가 생성자다. +JavaScript의 생성자는 다른 언어들과 다르게 `new` 키워드로 호출되는 함수가 생성자가 된다. -어쨌든 생성자로 호출된 함수의 this는 막 만들어진 객체를 참조한다. **막 만든** 객체의 [prototype](#object.prototype)에는 생성자의 prototype이 할당된다. +생성자로 호출된 함수의 this 객체는 새로 생성된 객체를 가리키고, **새로 만든** 객체의 [prototype](#object.prototype)에는 생성자의 prototype이 할당된다. -생성자에 `return` 구문이 없으면 this가 가리키는 객체를 반환한다. +그리고 생성자에 명시적인 `return` 구문이 없으면 this가 가리키는 객체를 반환한다. function Foo() { this.bla = 1; @@ -16,9 +16,9 @@ JavaScript에서 생성자는 다른 언어들과 다르게 `new` 키워드로 var test = new Foo(); -`new` 키워드가 실행되는 시점에 `Foo`를 생성자로 호출하고 `Foo.prototype`을 새 객체의 prototype에 할당한다. +위 코드는 `new` 키워드가 실행되는 시점에 `Foo`를 생성자로 호출하고 `Foo.prototype`을 새 객체의 prototype에 할당한다. -생성자에 `return` 구문이 있고 literal이 아니라 `객체`를 반환하면 그 객체가 반환된다. +아래 코드와 같이 생성자에 명시적인 `return` 문이 있는 경우에는 반환하는 값이 객체인 경우에만 그 값을 반환한다. function Bar() { return 2; @@ -37,11 +37,12 @@ JavaScript에서 생성자는 다른 언어들과 다르게 `new` 키워드로 new 키워드가 없으면 그 함수는 객체를 반환하지 않는다. function Foo() { - this.bla = 1; // gets set on the global object + this.bla = 1; // 전역객체에 할당된다. } Foo(); // undefined -이 함수는 그때그때 다르게 동작하지만 보통 [`this`](#function.this)의 규칙에 따라 `this`의 값으로 *Global 객체*가 사용된다.:w +위 예제는 그때그때 다르게 동작한다. 그리고 [`this`](#function.this) 객체의 동작 원리에 따라서 Foo 함수안의 `this`의 값은 *Global 객체*를 가리키게된다. +(역주: 결국 new 키워드를 빼고, 코드를 작성할 경우 원치 않은 this 참조 오류가 발생할 수 있다.) ### 팩토리 @@ -62,11 +63,12 @@ new 키워드가 없으면 그 함수는 객체를 반환하지 않는다. new Bar(); Bar(); -new 키워드가 있으나 없으니 `Bar` 생성자는 똑같이 동작한다. [Closure](#function.closures)가 할당된 method 프로퍼티가 있는 객체를 만들어 반환한다. +new 키워드의 유무과 관계없이 `Bar` 생성자의 동작은 동일한다. 즉 [클로저](#function.closures)가 할당된 method 프로퍼티가 있는 새로운 객체를 만들어 반환한다. -`new Bar()`는 반환된 객체의 prototype 프로퍼티에 아무런 영향을 주지 않는다. 객체를 반환하지 않는 생성자로 만들어지는 경우에만 객체의 prototype이 생성자의 것으로 할당된다. +`new Bar()`로 호출되는 생성자는 반환되는 객체의 prototype 프로퍼티에 아무런 영향을 주지 않는다. 객체를 반환하지 않는 생성자로 만들어지는 경우에만 객체의 prototype이 생성자의 것으로 할당된다. -그러니까 이 예제에서 `new` 키워드의 유무는 아무 차이가 없다. +그러니까 이 예제에서 `new` 키워드의 유무는 아무런 차이가 없다. +(역주: 생성자에 객체를 만들어 명시적으로 반환하면 new 키워드에 관계없이 잘 동작하는 생성자를 만들수있다. 즉, new 키워드가 빠졌을때 발생하는 this 참조 오류를 방어해준다.) ### 팩토리로 객체 만들기 @@ -89,7 +91,7 @@ new 키워드가 있으나 없으니 `Bar` 생성자는 똑같이 동작한다. return obj; } -`new` 키워드가 없어도 괜찮고 [private 변수](#function.closures)를 사용하기도 쉽다. 그렇지만, 단점도 있다. +`new` 키워드가 없어도 잘 동작하고 [private 변수](#function.closures)를 사용하기도 쉽다. 그렇지만, 단점도 있다. 1. prototype으로 메소드를 공유하지 않으므로 메모리를 좀 더 사용한다. 2. 팩토리를 상속하려면 모든 메소드를 복사하거나 객체의 prototype에 객체를 할당해 주어야 한다. diff --git a/doc/ko/function/general.md b/doc/ko/function/general.md index 402a34ec..091fd105 100644 --- a/doc/ko/function/general.md +++ b/doc/ko/function/general.md @@ -1,37 +1,37 @@ -## Function Declarations과 Function Expressions +## 함수 선언과 함수 표현식 -JavaScript의 Function은 First Class Object라서 일반 객체처럼 취급될 수 있다. 그래서 익명 함수를 비동기 함수의 callback 같은 거로 넘길 수 있다. +JavaScript에서 함수는 First Class Object다. 즉, 함수 자체가 또 다른 함수의 인자될 수 있다는 말이다. 그래서 익명 함수를 비동기 함수의 콜백으로 넘기는 것도 이런 특징을 이용한 일반적인 사용법이다. -### `function` Declaration +### `함수` 선언 function foo() {} -코드를 실행하기 전에 이 함수는 [Hoist](#function.scopes)되기 때문에 해당 Scope 어디에서나 이 함수를 호출할 수 있다. 심지어 함수를 정의하기 전에 호출해도 된다. +위와 같이 선언한 함수는 프로그램이 실행하기 전에 먼저 [호이스트(Hoist)](#function.scopes) (스코프가 생성)되기 때문에 정의된 스코프(Scope) 안에서는 어디서든 이 함수를 사용할 수 있다. 심지어 함수를 정의하기 전에 호출해도 된다. - foo(); // 이 코드가 실행되기 전에 foo가 만들어져서 잘 호출된다. + foo(); // 이 코드가 실행되기 전에 foo가 만들어지므로 잘 동작한다. function foo() {} -### `function` Expression +### `함수` 표현식 var foo = function() {}; -`foo` 변수에 *익명* 함수를 할당하는 예를 보자. +위 예제는 `foo` 변수에 *익명* 함수를 할당한다. foo; // 'undefined' foo(); // TypeError가 난다. var foo = function() {}; -JavaScript가 Hoist하는 것은 `var`로 선언하는 부분뿐이기 때문에 코드가 실행하기 전에 `foo` 변수는 정의된다. +'var'문을 이용해 선언하는 경우, 코드가 실행되기 전에 'foo' 라는 이름의 변수를 스코프의 맨 위로 올리게 된다.(호이스트 된다) 이때 foo 값은 undefiend로 정의된다. -그러나 할당은 런타임에만 가능한 일이라 할당하는 코드가 실행될 때까지 `foo`변수는 기본 값인 [undefined](#core.undefined)다. +하지만 변수에 값을 할당하는 일은 런타임 상황에서 이루어지게 되므로 실제 코드가 실행되는 순간의 `foo`변수는 기본 값인 [undefined](#core.undefined)이 된다. -### Named Function Expression +### 이름있는 함수 표현식 -Named Function을 할당하는 경우는 조금 특이하다. +이름있는 함수를 할당할때도 특이한 경우가 있다. var foo = function bar() { - bar(); // 된다. + bar(); // 이 경우는 동작 하지만, } - bar(); // ReferenceError + bar(); // 이 경우는 참조에러를 발생시킨다. -함수 밖에서는 `bar`를 사용할 수 없지만, 함수 안에서는 사용할 수 있다. JavaScript가 [이름을 찾는 방법](#function.scopes)이 있는데 function Scope에서는 항상 그 함수의 이름을 사용할 수 있다. +foo 함수 스코프 밖에서는 foo 변수 외에는 다른 값이 없기 때문에 `bar`는 함수 밖에서 사용할 수 없지만 함수 안에서는 사용할 수 있다. [이와 같은 방법](#function.scopes)으로 자바스크립트에서 어떤 함수의 이름은 항상 그 함수의 지역 스코프 안에서 사용할수있다. \ No newline at end of file diff --git a/doc/ko/function/scopes.md b/doc/ko/function/scopes.md index ad40e0f3..7792ee65 100644 --- a/doc/ko/function/scopes.md +++ b/doc/ko/function/scopes.md @@ -1,6 +1,6 @@ -## Scope과 Namespace + ## 스코프와 네임스페이스 -JavaScript는 '{}' Block이 배배 꼬여 있어도 문법적으로는 잘 처리하지만, Block Scope은 지원하지 않는다. 그래서 JavaScript에서는 항상 *Function Scope*을 사용한다. +JavaScript는 '{}' Block이 배배 꼬여 있어도 문법적으로는 잘 처리하지만, Block Scope은 지원하지 않는다. 그래서 JavaScript에서는 항상 *함수 스코프*를 사용한다. function test() { // Scope for(var i = 0; i < 10; i++) { // Scope이 아님 @@ -11,11 +11,11 @@ JavaScript는 '{}' Block이 배배 꼬여 있어도 문법적으로는 잘 처 > **Note:** 할당할 때, 반환할 때, Function 인자에서 사용되는 것을 제외하면 `{...}`는 모두 객체 리터럴이 아니라 Block 구문으로 해석된다. 그래서 [세미콜론을 자동으로 넣어주면](#core.semicolon) 에러가 생길 수 있다. -그리고 JavaScript에는 Namepspace 개념이 없어서 *항상 공유하는* namepace가 딱 하나다. +그리고 JavaScript에는 Namepspace 개념이 없기 때문에 모든 값이 하나의 *전역* 스코프에 정의된다. -변수를 사용할 때마다 JavaScript는 아는 Scope을 상위 방향으로 찾는다. Global Scope에까지 해당 변수를 찾지 못하면 `ReferenceError`가 난다. +변수를 참조 할 때마다 JavaScript는 해당 변수를 찾을 때까지 상위 방향으로 스코프를 탐색한다. 변수 탐색하다가 전역 스코프에서도 찾지 못하면 `ReferenceError`를 발생시킨다. -### Global 변수 지옥. +### 전역 변수 문제. // script A foo = '42'; @@ -23,7 +23,7 @@ JavaScript는 '{}' Block이 배배 꼬여 있어도 문법적으로는 잘 처 // script B var foo = '42' -이 두 스크립트는 전혀 다르다. Script A는 *Global* Scope에 `foo`라는 변수를 정의하는 것이고 Script B는 *현* Scope에 변수 `foo`를 정의하는 것이다. +이 두 스크립트는 전혀 다르다. Script A는 *전역* 스코프에 `foo`라는 변수를 정의하는 것이고 Script B는 *현* 스코프에 변수 `foo`를 정의하는 것이다. 다시 말하지만, 이 둘은 전혀 다르고 `var`가 없을 때 특별한 의미가 있다. @@ -36,7 +36,7 @@ JavaScript는 '{}' Block이 배배 꼬여 있어도 문법적으로는 잘 처 test(); foo; // 21 -Function에서 `var` 구문을 빼버리면 Global Scope의 `foo`의 값을 바꿔버린다. '뭐 이게 뭐가 문제야'라고 생각될 수 있지만 수천 줄인 JavaScript 코드에서 `var`를 빼먹어서 생긴 버그를 해결하는 것은 정말 어렵다. +test 함수 안에 있는 'foo' 변수에 `var` 구문을 빼버리면 Global Scope의 `foo`의 값을 바꿔버린다. '뭐 이게 뭐가 문제야'라고 생각될 수 있지만 수천 줄인 JavaScript 코드에서 `var`를 빼먹어서 생긴 버그를 해결하는 것은 정말 어렵다. // Global Scope var items = [/* some list */]; @@ -51,19 +51,19 @@ Function에서 `var` 구문을 빼버리면 Global Scope의 `foo`의 값을 바 } } -subLoop이 Global 변수 `i`의 값을 변경해버리기 때문에 외부 Loop은 `subLoop`을 한번 호출하고 나면 종료된다. 두 번째 `for` Loop에 `var`를 사용하여 `i`를 정의하면 이 문제는 생기지 않는다. 외부 Scope의 변수를 사용하는 것이 아니라면 `var`를 꼭 넣어야 한다. +subLoop 함수는 전역 변수 `i`의 값을 변경해버리기 때문에 외부에 있는 for문은 `subLoop`을 한번 호출하고 나면 종료된다. 두 번째 `for`문에 `var`를 사용하여 `i`를 정의하면 이 문제는 생기지 않는다. 즉, 의도적으로 외부 스코프의 변수를 사용하는 것이 아니라면 `var`를 꼭 넣어야 한다. -### Local 변수 +### 지역 변수 -JavaScript에서 Local 변수는 [Function 파라미터](#function.general)와 `var`로 정의한 변수뿐이다. +JavaScript에서 지역 변수는 [함수의 파라미터](#function.general)와 `var`로 정의한 변수밖에 없다. - // Global Scope + // 전역 공간 var foo = 1; var bar = 2; var i = 2; function test(i) { - // test Function의 local Scope + // test 함수의 지역 공간 i = 5; var foo = 3; @@ -71,11 +71,11 @@ JavaScript에서 Local 변수는 [Function 파라미터](#function.general)와 ` } test(10); -`foo`, `i`는 `test` Function Scope에 있는 Local 변수라서 Global의 `foo`, `i` 값은 바뀌지 않는다. 하지만 `bar`는 Global 변수이기 때문에 Global의 `bar` 값이 변경된다. +`foo` 변수와 `i` 변수는 `test`함수 스코프에 있는 지역 변수라서 전역 공간에 있는 `foo`, `i` 값은 바뀌지 않는다. 하지만 `bar`는 전역 변수이기 때문에 전역 공간에 있는 `bar`의 값이 변경된다. -### Hoisting +### 호이스팅(Hoisting) -JavaScript는 선언문을 모두 **Hoist**한다. Hoist는 `var` 구문이나 `function`을 선언문을 해당 Scope의 가장 처음으로 옮기는 것을 말한다. +JavaScript는 선언문을 모두 **호이스트(Hoist)**한다. 호이스트란 `var` 구문이나 `function` 선언문을 해당 스코프의 맨 위로 옮기는 것을 말한다. bar(); var bar = function() {}; @@ -94,7 +94,7 @@ JavaScript는 선언문을 모두 **Hoist**한다. Hoist는 `var` 구문이나 ` } } -코드를 본격적으로 실행하기 전에 JavaScript는 `var` 구문과 `function` 선언문을 해당 Scope의 상위로 옮긴다. +코드를 본격적으로 실행하기 전에 JavaScript는 `var` 구문과 `function` 선언문을 해당 스코프의 맨위로 옮긴다. // var 구문이 여기로 옮겨짐. var bar, someValue; // default to 'undefined' @@ -114,23 +114,23 @@ JavaScript는 선언문을 모두 **Hoist**한다. Hoist는 `var` 구문이나 ` } bar(); // bar()가 아직 'undefined'이기 때문에 TypeError가 남 - someValue = 42; // Hoisting은 할당문까지 옮기지 않는다. + someValue = 42; // Hoisting은 할당문은 옮기지 않는다. bar = function() {}; test(); -Block Scope이 없으므로 Loop이나 if의 Block 안에 있는 `var` 구문들까지도 모두 Function Scope의 앞쪽으로 옮겨진다. 그래서 `if` Block의 결과는 좀 이상해진다. +블록 스코프(Block Scope)는 없으므로 for문과 if문 안에 있는 `var` 구문들까지도 모두 함수 스코프 앞쪽으로 옮겨진다. 그래서 `if` Block의 결과는 좀 이상해진다. -원래 코드에서 `if` Block은 *Global 변수* `goo`를 바꾸는 것처럼 보였지만 Hoisting 후에는 *local 변수*를 바꾼다. +원래 코드에서 `if` Block은 *전역 변수* `goo`를 바꾸는 것처럼 보였지만 호이스팅(Hoisting) 후에는 *지역 변수*를 바꾼다. -*Hoisting*을 모르면 다음과 같은 코드는 `ReferenceError`를 낼 것으로 생각할 것이다. +*호이스팅*을 모르면 다음과 같은 코드는 `ReferenceError`를 낼 것으로 생각할 것이다. // SomeImportantThing이 초기화됐는지 검사한다. if (!SomeImportantThing) { var SomeImportantThing = {}; } -`var` 구문은 *Global Scope* 상단으로 옮겨지기 때문에 이 코드는 잘 동작한다. +`var` 구문은 *전역 스코프*의 맨위로 옮겨지기 때문에 이 코드는 잘 동작한다. var SomeImportantThing; @@ -143,11 +143,11 @@ Block Scope이 없으므로 Loop이나 if의 Block 안에 있는 `var` 구문들 ### 이름 찾는 순서 -JavaScript의 모든 Scope은 *현 객체*를 가리키는 [`this`](#function.this)를 가지고 있다. *Global Scope*에도 this가 있다. +JavaScript의 모든 Scope은 *현 객체*를 가리키는 [`this`](#function.this)를 가지고 있다. *전역 스코프*에도 this가 있다. -Function Scope에는 [`arguments`](#function.arguments)라는 변수가 하나 더 있다. 이 변수는 Function에 넘겨진 인자들이 담겨 있다. +함수 스코프에는 [`arguments`](#function.arguments)라는 변수가 하나 더 있다. 이 변수는 함수에 인자로 넘겨진 값들이 담겨 있다. -예를 들어 Function Scope에서 `foo`라는 변수에 접근할 때 JavaScript는 다음과 같은 순서로 찾는다. +예를 들어 함수 스코프에서 `foo`라는 변수에 접근할 때 JavaScript는 다음과 같은 순서로 찾는다. 1. 해당 Scope에서 `var foo` 구문으로 선언된 것을 찾는다. 2. Function 파라미터에서 `foo`라는 것을 찾는다. @@ -156,29 +156,29 @@ Function Scope에는 [`arguments`](#function.arguments)라는 변수가 하나 > **Note:** `arguments`라는 파라미터가 있으면 Function의 기본 객체인 `arguments`가 생성되지 않는다. -### Namespace +### 네임스페이스 -JavaScript에서는 Global Namepspace 하나밖에 없어서 변수 이름이 중복되기 쉽다. 하지만 *Anonymous Wrappers*가 있어서 쉽게 피해갈 수 있다. +JavaScript에서는 전역 공간(Namepspace) 하나밖에 없어서 변수 이름이 중복되기 쉽다. 하지만 *이름없는 랩퍼(Anonymous Wrappers)*를 통해 쉽게 피해갈 수 있다. (function() { - // 일종의 Namepspace라고 할 수 있다. + // 일종의 네임스페이스라고 할 수 있다. window.foo = function() { - // 이 Closure는 Global Scope에 노출된다. + // 이 클로저는 전역 스코프에 노출된다. }; - })(); // Function를 정의하자마자 실행한다. + })(); // 함수를 정의하자마자 실행한다. -Unnamed Function은 [expressions](#function.general)이기 때문에 호출되려면 먼저 Evaluate돼야 한다. +이름없는 함수는 [표현식(expressions)](#function.general)이기 때문에 호출되려면 먼저 평가(Evaluate)돼야 한다. - ( // 소괄호 안에 있는 것을 먼저 Evaluate한다. + ( // 소괄호 안에 있는 것을 먼저 평가한다. function() {} - ) // 그리고 Function 객체를 반환한다. - () // Evaluation된 결과를 호출한다. + ) // 그리고 함수 객체를 반환한다. + () // 평가된 결과를 호출한다. -Function을 Evaluate하고 바로 호출하는 방법이 몇가지 더 있다. 문법은 다르지만 똑같다. +함수를 평가하고 바로 호출하는 방법이 몇가지 더 있다. 문법은 다르지만 똑같다. - // Fucntion을 Evaluate하자마자 호출하는 방법들... + // 함수를 평가하자마자 호출하는 방법들... !function(){}(); +function(){}(); (function(){}()); @@ -186,6 +186,6 @@ Function을 Evaluate하고 바로 호출하는 방법이 몇가지 더 있다. ### 결론 -코드를 캡슐화할 때는 늘 *Anonymous Wrapper*로 Namepspace를 만들어 사용해야 한다. 이 Wrapper는 이름이 중복되는 것을 막아 주고 더 쉽게 모듈화할 수 있도록 해준다. +코드를 캡슐화할 때는 항상 *이름없는 랩퍼(Anonymous Wrapper)*로 네임스페이스를 만들어 사용할 것을 추천한다. 이 래퍼(Wrapper)는 이름이 중복되는 것을 막아 주고 더 쉽게 모듈화할 수 있도록 해준다. -그리고 Global 변수를 사용하는 것은 악질적인 습관이다. 이유야 어쨌든 에러 나기 쉽고 관리하기도 어렵다. +그리고 전역 변수를 사용하는 것은 좋지 못한 습관이다. 이유야 어쨌든 에러 나기 쉽고 관리하기도 어렵다. diff --git a/doc/ko/function/this.md b/doc/ko/function/this.md index 116b1250..b6d57aa6 100644 --- a/doc/ko/function/this.md +++ b/doc/ko/function/this.md @@ -1,4 +1,4 @@ -## `this` +## `this`의 동작 원리 다른 프로그래밍 언어에서 `this`가 가리키는 것과 JavaScript에서 `this`가 가리키는 것과는 좀 다르다. `this`가 가리킬 수 있는 객체는 정확히 5종류나 된다. @@ -42,7 +42,7 @@ Global Scope에서도 this가 사용될 수 있고 이때에는 *Global* 객체 > **Note:** 객체 리터럴에서 this는 그 객체를 가리키지 않는다. 예를 들어 `var obj= {me:this}`에서 `me`가 `obj`를 가리키는 것이 아니라 위에 설명한 5가지 객체 중 하나를 가리킨다. -### 대표적인 결점 +### 대표적인 함정 `this`가 Global 객체를 가리키는 것도 잘못 설계된 부분 중 하나다. 괜찮아 보이지만 실제로는 전혀 사용하지 않는다. @@ -69,14 +69,14 @@ Global Scope에서도 this가 사용될 수 있고 이때에는 *Global* 객체 ### Method할당 하기 -메소드를 변수에 *할당*해 버리기 때문에 Function Aliasing은 JavaScript에서 안된다. +JavaScript의 또다른 함정은 바로 함수의 별칭을 만들수 없다는 점이다. 별칭을 만들기 위해 메소드를 변수에 넣으면 자바스크립트는 별칭을 만들지 않고 바로 *할당*해 버린다. var test = someObject.methodTest; test(); -`test`는 다른 함수를 호출하는 것과 다름없어서 `this`가 someObject를 가리키지 않는다. +첫번째 코드로 인해 이제 `test`는 다른 함수와 똑같이 동작한다. 그래서 test 함수 내부의 `this`도 더이상 someObject를 가리키지 않는다. (역주: test가 methodTest의 별칭이라면 methodTest 함수 내부의 this도 someObject를 똑같이 가리켜야 하지만 test의 this는 더이상 someObject가 아니다.) -처음에는 `this`를 늦게 바인딩하는 것이 나쁜 아이디어라고 생각할 수도 있지만, 이 점이 실제로 [prototypal inheritance](#object.prototype)를 가능케 해준다. +이렇게 `this`를 늦게 바인딩해서 나타나는 약점때문에 늦은 바인딩이 나쁜 거라고 생각할수도 있지만, 사실 이런 특징으로 인해 [프로토타입 상속(prototypal inheritance)](#object.prototype)도 가능해진다. function Foo() {} Foo.prototype.method = function() {}; diff --git a/doc/ko/intro/index.md b/doc/ko/intro/index.md index 16d2ed66..87b72473 100644 --- a/doc/ko/intro/index.md +++ b/doc/ko/intro/index.md @@ -14,9 +14,10 @@ JavaScript Garden은 단순히 JavaScript 언어 자체를 설명하려 만들 - [Andreas Blixt][6] (언어 교정) ## 번역 - - [박창우][] + - [박창우][손병대][] [박창우]: https://github.com/pismute +[손병대]: https://github.com/miconblog ## 호스팅 diff --git a/doc/ko/object/forinloop.md b/doc/ko/object/forinloop.md index 5a619c47..fd05cedb 100644 --- a/doc/ko/object/forinloop.md +++ b/doc/ko/object/forinloop.md @@ -1,10 +1,10 @@ ## `for in` Loop -`in` 연산자와 마찬가지로 `for in`도 객체의 프로퍼티뿐만 아니라 프로토타입 체인까지 Traverse 한다. +객체의 프로퍼티를 탐색할때 `in` 연산자와 마찬가지로 `for in` 문도 프로토타입 체인까지 탐색한다. -> **Note:** `for in`은 Array의 `length`처럼 `enumerable` 속성이 `false`인 프로퍼티는 Iterate 하지 않는다. +> **Note:** `for in`문은 배열의 `length`프로퍼티처럼 `enumerable` 속성이 `false`인 프로퍼티는 탐색하지 않는다. - // 원래는 Object.prototype을 바꾸면 안 된다. + // Object.prototype을 오염시킨다. Object.prototype.bar = 1; var foo = {moo: 2}; @@ -12,9 +12,9 @@ console.log(i); // bar와 moo 둘 다 출력한다. } -선택적으로 Iterate 하려면 `for in`은 바꿀 수 없으니까 Loop 바디에서 하는 수밖에 없다. `Object.prototype`의 [`hasOwnProperty`](#object.hasownproperty)메소드를 사용하면 객체의 프로퍼티만 골라낼 수 있다. +`for in`문에 정의된 기본 동작을 바꿀순 없기 때문에 루프 안에서 불필요한 프로퍼티를 필터링 해야한다. 그래서 `Object.prototype`의 [`hasOwnProperty`](#object.hasownproperty)메소드를 이용해 본래 객체의 프로퍼티만 골라낸다. -> **Note:** `for in`은 프로토타입 체인을 모두 Traverse 한다. 그래서 상속할 때마다 더 느려진다. +> **Note:** `for in`은 프로토타입 체인을 모두 탐색하기 때문에 상속할 때마다 더 느려진다. ### `hasOwnProperty`로 필터링 하기 @@ -25,12 +25,12 @@ } } -실무에 사용할 작정이라면 이렇게 써야 옳다. `hasOwnProperty` 때문에 **오직** `moo`만 출력된다. `hasOwnProperty`가 없으면 `Object.prototype`같은 네이티브 프로토타입이 확장될 때 에러 날 수 있다. +위와 같이 사용해야 올바른 사용법이다. `hasOwnProperty` 때문에 **오직** `moo`만 출력된다. `hasOwnProperty`가 없으면 이 코드는 `Object.prototype`으로 네이티브 객체가 확장될 때 에러가 발생할 수 있다. -네이티브 프로토타입을 확장하는 [Proptotype 라이브러리][1]을 사용하면 `hasOwnProperty`가 없는 `for in` Loop은 꼭 문제를 일으킨다. +따라서 [Proptotype 라이브러리][1]처럼 네이티브 객체를 프로토타입으로 확장한 프레임워크를 사용할 경우 `for in` 문에 `hasOwnProperty`를 사용하지 않을 경우 문제가 발생할 수 있다. ### 결론 -`hasOwnProperty`는 항상 사용해야 한다. 실제로 코드가 동작할 환경에서 네이티브 프로토타입의 확장 여부에 대해 어떠한 가정도 하지 말아야 한다. +`hasOwnProperty`를 항상 사용하길 권한다. 실제 코드가 동작하는 환경에서는 절대로 네이티브 객체가 프로토타입으로 확장됐다 혹은 확장되지 않았다를 가정하면 안된다. [1]: http://www.prototypejs.org/ diff --git a/doc/ko/object/general.md b/doc/ko/object/general.md index 51ae6f60..0e7ae1c6 100644 --- a/doc/ko/object/general.md +++ b/doc/ko/object/general.md @@ -1,6 +1,6 @@ ## 객체와 프로퍼티 -JavaScript에서 [`null`](#core.undefined)과 [`undefined`](#core.undefined)를 제외한 모든 것은 객체다. +JavaScript에서 [`null`](#core.undefined)과 [`undefined`](#core.undefined)를 제외한 모든 것들은 객체처럼 동작한다. false.toString() // 'false' [1, 2, 3].toString(); // '1,2,3' @@ -9,11 +9,11 @@ JavaScript에서 [`null`](#core.undefined)과 [`undefined`](#core.undefined)를 Foo.bar = 1; Foo.bar; // 1 -숫자 리터럴은 객체가 아니라는 오해가 있는데 단지 JavaScript 파서의 문제일 뿐이다. JavaScript 파서는 숫자에 *Dot Notation*이 들어가면 오류라고 생각한다. +숫자 리터럴은 객체처럼 사용되지 못할꺼라는 오해가 있는데 이것은 단지 JavaScript 파서의 문제일 뿐이다. JavaScript 파서는 숫자에 *Dot Notation*이 들어가면 오류라고 생각한다. 2.toString(); // SyntaxError가 난다. -하지만, 숫자를 객체로 인식하는 꼼수가 몇 가지 있다. +하지만, 숫자를 객체처럼 사용할수 있는 꼼수가 몇 가지 있다. 2..toString(); // 두 번째 점은 잘 된다. 2 .toString(); // 왼쪽 공백이 있으면 잘 된다. @@ -21,7 +21,7 @@ JavaScript에서 [`null`](#core.undefined)과 [`undefined`](#core.undefined)를 ### Object 타입 -JavaScript 객체는 name/value 쌍으로 된 프로퍼티로 구성되기 때문에 [*Hashmap*][1]으로도 사용할 수 있다. +JavaScript 객체는 name/value 쌍으로 된 프로퍼티로 구성되기 때문에 [*Hashmap*][1]처럼 사용될 수도 있다. 객체 리터럴인 Object Notation으로 객체를 만들면 `Object.prototype`을 상속받고 [프로퍼티를 하나도 가지지 않은](#object.hasownproperty) 객체가 만들어진다. @@ -30,9 +30,9 @@ JavaScript 객체는 name/value 쌍으로 된 프로퍼티로 구성되기 때 // 값이 12인 'test' 프로퍼티가 있는 객체를 만든다. var bar = {test: 12}; -### 프로퍼티 +### 프로퍼티 접근 -객체의 프로퍼티는 Dot Notation이나 Square Bracket Notation으로 접근할 수 있다. +객체의 프로퍼티는 객체이름 다음에 점을 찍어(Dot Notation) 접근하거나 각괄호를 이용해(Square Bracket Notation) 접근할 수 있다. var foo = {name: 'Kitten'} foo.name; // kitten @@ -44,12 +44,11 @@ JavaScript 객체는 name/value 쌍으로 된 프로퍼티로 구성되기 때 foo.1234; // SyntaxError foo['1234']; // works - -'[]'은 프로퍼티를 동적으로 할당할 수 있고 변수 이름 규칙에도 구애받지 않는다. 그렇지만, 두 가지 방법은 근본적으로 서로 똑같다. +두 방식 모두 거의 동일하게 동작한다. 다만 차이가 있다면 각괄호 방식은 프로퍼티 이름을 동적으로 할당해서 값에 접근 할수 있지만 점을 이용한 방식은 구문 오류를 발생시킨다. ### 프로퍼티 삭제 -객체의 프로퍼티는 `delete`로만 삭제할 수 있다. 프로퍼티에 `undefined`나 `null`을 할당하는 것은 프로퍼티를 삭제하는 것이 아니라 프로퍼티에 할당된 *value*만 지우고 *key*는 그대로 두는 것이다. +객체의 프로퍼티를 삭제하려면 `delete`를 사용해야만 한다. 프로퍼티에 `undefined`나 `null`을 할당하는 것은 프로퍼티를 삭제하는 것이 아니라 프로퍼티에 할당된 *value*만 지우고 *key*는 그대로 두는 것이다. var obj = { bar: 1, @@ -67,7 +66,7 @@ JavaScript 객체는 name/value 쌍으로 된 프로퍼티로 구성되기 때 } -`baz`만 제거했기 때문에 `bar undefined`와 `foo null`은 출력되고 `baz`와 관련된 것은 출력되지 않는다. +위 코드의 출력 결과는 `baz`만 제거했기 때문에 `bar undefined`와 `foo null`은 출력되고 `baz`와 관련된 것은 출력되지 않는다. ### Notation of Keys @@ -76,8 +75,8 @@ JavaScript 객체는 name/value 쌍으로 된 프로퍼티로 구성되기 때 delete: 'I am a keyword, so me too' // SyntaxError가 난다. }; -프로퍼티의 key에 문자열이나 스트링을 사용할 수 있다. 이 부분도 JavaScript 파서의 설계 오류다. ECMAScript 5 이전에는 `SystaxError`가 났었다. +프로퍼티는 따옴표 없는 문자열(plain characters)과 따옴표로 감싼 문자열(strings)을 모두 Key 값으로 사용할 수 있다. 하지만 위와 같은 코드는 JavaScript 파서의 잘못된 설계 때문에 구버전(ECMAScript 5 이전 버전)에서는 `SystaxError`가 발생할 것이다. -이 에러는 `delete`가 키워드이기 때문에 발생하는 것이다. key를 스트링 리터럴로 정의하면 JavaScript 엔진은 언제나 잘 해석한다. +위 코드에서 문제가 되는 `delete` 키워드를 따옴표로 감싸면 구버전의 JavaScript 엔진에서도 제대로 해석될 것이다. [1]: http://en.wikipedia.org/wiki/Hashmap diff --git a/doc/ko/object/hasownproperty.md b/doc/ko/object/hasownproperty.md index b53ad583..fa36645e 100644 --- a/doc/ko/object/hasownproperty.md +++ b/doc/ko/object/hasownproperty.md @@ -1,12 +1,12 @@ ## `hasOwnProperty` -어떤 프로퍼티가 해당 객체 자신의 것인지 아니면 [프로토타입 체인](#object.prototype)에 있는 것인지 확인하려면 `Object.prototype`을 상속받은 `hasOwnProperty` 메소드를 사용해야 한다. +어떤 객체의 프로퍼티가 자기 자신의 프로퍼티인지 아니면 [프로토타입 체인](#object.prototype)에 있는 것인지 확인하려면 `hasOwnProperty` 메소드를 사용한다. 그리고 이 메소드는 `Object.prototype`으로 부터 상속받아 모든 객체가 가지고 있다. -> **Note:** 이 메소드로는 프로퍼티의 값이 `undefined`인지 확인할 수 없다. 프로퍼티가 존재해도 그 값은 `undefined`일 수 있다. +> **Note:** hasOwnProperty 메소드로는 어떤 프로퍼티가 존재하는지 확인하는 용도로는 사용할수 있지만, 그 값이 `undefined`일 수 있기 때문에 어떤 프로퍼티의 값이 `undefined`인지 확인하는 용도로 사용하긴 어렵다. -프로토타입 체인을 Traverse 하지 않으려면 `hasOwnProperty`를 사용하는 방법밖에 없다. +`hasOwnProperty`메소드는 프로토타입 체인을 탐색하지 않고, 프로퍼티를 다룰수있는 유일한 방법이다. - // Object.prototype을 더럽힌다. + // Object.prototype을 오염시킨다. Object.prototype.bar = 1; var foo = {goo: undefined}; @@ -16,11 +16,11 @@ foo.hasOwnProperty('bar'); // false foo.hasOwnProperty('goo'); // true -프로퍼티의 존재 여부를 확인하는 방법은 `hasOwnProperty` 메소드 뿐이다. 이 메소드는 프로토타입 체인의 프로퍼티말고 해당 객체의 프로퍼티만 Iterate할 때 유용하다. 객체 자체의 프로퍼티와 프로토타입 체인 어딘가에 있는 프로퍼티를 골라 주는 다른 방법은 없다. +`hasOwnProperty` 메소드는 어떤 프로퍼티가 자기 자신의 프로퍼티인지 아닌지 정확하게 알려주기 때문에 객체의 프로퍼티를 순회할때 꼭 필요하다. 그리고 프로토타입 체인 어딘가에 정의된 프로퍼티만을 제외하는 방법은 없다. -### `hasOwnProperty`도 프로퍼티 +### `hasOwnProperty` 메소드도 프로퍼티다 -JavaScript는 `hasOwnProperty` 프로퍼티도 보호해주지 않는다. 그래서 객체에 `hasOwnProperty` 프로퍼티가 있으면 다른 객체의 `hasOwnProperty` 메소드를 빌려 사용해야 한다. +JavaScript는 `hasOwnProperty`라는 이름으로 프로퍼티를 덮어 쓸수도 있다. 그래서 객체 안에 같은 이름으로 정의된 `hasOwnProperty`가 있을 경우, 본래 `hasOwnProperty`의 값을 정확하게 얻고 싶다면 다른 객체의 `hasOwnProperty` 메소드를 빌려써야 한다. var foo = { hasOwnProperty: function() { @@ -34,10 +34,10 @@ JavaScript는 `hasOwnProperty` 프로퍼티도 보호해주지 않는다. 그래 // 다른 객체의 hasOwnProperty를 사용하여 foo 객체의 프로퍼티 유무를 확인한다. ({}).hasOwnProperty.call(foo, 'bar'); // true - // Object에 원래 있는 hasOwnProperty를 사용해도 된다. + // Object에 있는 hasOwnProperty를 사용해도 된다. Object.prototype.hasOwnProperty.call(obj, 'bar'); // true ### 결론 -객체에 프로퍼티가 있는지 `hasOwnProperty`로만 확인할 수 있다. [`for in` loop](#object.forinloop)은 항상 `hasOwnProperty`와 함께 사용해야 한다. 네이티브 객체의 [프로토타입](#object.prototype)을 확장하는 사태가 일어나도 안전하게 지켜줄 것이다. +어떤 객체에 원하는 프로퍼티가 있는지 확인하는 가장 확실한 방법은 `hasOwnProperty`를 사용하는 것이다. [`for in` loop](#object.forinloop)에서 네이티브 객체에서 확장된 프로퍼티를 제외하고 순회하려면 `hasOwnProperty`와 함께 사용하길 권한다. diff --git a/doc/ko/object/prototype.md b/doc/ko/object/prototype.md index e2d591cc..9e8ffb2a 100644 --- a/doc/ko/object/prototype.md +++ b/doc/ko/object/prototype.md @@ -2,13 +2,13 @@ Javascript는 클래스 스타일의 상속 모델을 사용하지 않고 *프로토타입* 스타일의 상속 모델을 사용한다. -'이 점이 JavaScript의 약점이다.'라고 말하는 사람들도 있지만 실제로는 prototypal inheritance 모델이 훨씬 더 강력하다. 왜냐하면, 프로토타입 모델에서 클래스 모델을 흉내 내기는 매우 쉽지만, 반대로 클래스 모델에서 프로토타입 모델을 흉내 내기란 너무 어렵다. +'이 점이 JavaScript의 약점이다.'라고 말하는 사람들도 있지만 실제로는 prototypal inheritance 모델이 훨씬 더 강력하다. 그 이유는 프로토타입 모델에서 클래스 모델을 흉내 내기는 매우 쉽지만, 반대로 클래스 모델에서 프로토타입 모델을 흉내 내기란 매우 어렵기 때문이다. -실제로 Prototypal Inheritance 모델을 채용한 언어 중에서 JavaScript만큼 널리 사용되는 언어는 없었기 때문에 너무 늦게 두 모델의 차이점이 정리된 감이 있다. +실제로 Prototypal Inheritance 모델을 채용한 언어 중에서 JavaScript만큼 널리 사용된 언어가 없었기 때문에 두 모델의 차이점이 다소 늦게 정리된 감이 있다. -JavaScript는 *프로토타입 체인*이라는 것으로 상속을 구현한다. +먼저 가장 큰 차이점은 *프로토타입 체인*이라는 것을 이용해 상속을 구현한다는 점이다. -> **Note:** 간단히 말해서 `Bar.prototype = Foo.prototype`은 두 객체가 **하나의 프로토타입**을 공유하는 것이다. 그래서 한 객체의 프로토타입을 변경하면 그 프로토타입 객체를 사용하는 다른 객체에도 영향을 끼친다. 대부분은 나쁜 결과로 이어진다. +> **Note:** 간단히 말해서 `Bar.prototype = Foo.prototype`은 두 객체가 **하나의 프로토타입**을 공유하는 것이다. 그래서 한 객체의 프로토타입을 변경하면 그 프로토타입 객체를 사용하는 다른 객체도 영향을 받는다. 따라서 대부분의 경우 프로토타입을 변경하지는 않는다. function Foo() { this.value = 42; @@ -23,7 +23,7 @@ JavaScript는 *프로토타입 체인*이라는 것으로 상속을 구현한다 Bar.prototype = new Foo(); Bar.prototype.foo = 'Hello World'; - // Bar function을 생성자로 만들고 + // Bar 함수를 생성자로 만들고 Bar.prototype.constructor = Bar; var test = new Bar() // bar 인스턴스를 만든다. @@ -37,42 +37,42 @@ JavaScript는 *프로토타입 체인*이라는 것으로 상속을 구현한다 Object.prototype { toString: ... /* etc. */ } -`Bar.prototype`과 `Foo.prototype`을 둘 다 상속받았기 때문에 `test` 객체는 Foo에 정의한 `method` 함수에 접근할 수 있다. 프로토타입 체인에 있는 `Foo` 인스턴스의 `value` 프로퍼티도 사용할 수 있다. `new Bar()`를 해도 `Foo` 인스턴스는 새로 만들어지지 않고 Bar의 prototype에 있는 것을 재사용한다. 그래서 모든 Bar 인스턴스의 `value` 프로퍼티에 들어 있는 객체는 전부 **같은 객체다**. +위 코드에서 `test` 객체는 `Bar.prototype`과 `Foo.prototype`을 둘 다 상속받았기 때문에 Foo에 정의한 `method` 함수에 접근할 수 있다. 그리고 프로토타입 체인에 있는 `Foo` 인스턴스의 `value` 프로퍼티도 사용할 수 있다. `new Bar()`를 해도 `Foo` 인스턴스는 새로 만들어지지 않고 Bar의 prototype에 있는 것을 재사용한다. 그래서 모든 Bar 인스턴스는 **같은** `value` 프로퍼티를 공유한다. -> **Note:** `Bar.prototype = Foo`라고 하는 것은 `Foo`의 prototype을 가리키는 것이 아니라 Foo라는 Function의 prototype을 가리키는 것이다. 그래서 프로토타입 체인에 `Foo.prototype` 대신 `Function.prototype`이 들어서는 것이기 때문에 `method` 프로퍼티는 못 찾는다. +> **Note:** `Bar.prototype = Foo`라고 하는 것은 `Foo`의 prototype을 가리키는 것이 아니라 Foo라는 Function의 prototype을 가리키는 것이다. 그래서 프로토타입 체인에 `Foo.prototype` 대신 `Function.prototype`이 들어가 있기 때문에 `method` 프로퍼티는 찾지 못한다. -### 프로토타입 찾기 +### 프로토타입 탐색 -객체의 프로퍼티에 접근을 시도하면 JavaScript는 해당 이름의 프로퍼티를 찾을 때까지 위쪽으로 프로토타입 체인을 뒤진다. +객체의 프로퍼티에 접근하려고 하면 JavaScript는 해당 이름의 프로퍼티를 찾을 때까지 프로토타입 체인을 거슬러 올라가면서 탐색하게 된다. -체인의 끝까지 찾았는데도(보통은 `Object.prototype`임) 발견하지 못하면 [undefined](#core.undefined)를 반환한다. +프로토타입 체인을 끝까지 탐색했음에도(보통은 `Object.prototype`임) 불구하고 원하는 프로퍼티를 찾지 못하면 [undefined](#core.undefined)를 반환한다. ### prototype 프로퍼티 -prototype 프로퍼티는 프로토타입 체인을 만드는 데 사용하고 어떤 거라도 할당할 수 있지만, primitive 값을 할당하면 무시된다. +prototype 프로퍼티는 프로토타입 체인을 만드는 데 사용하고 어떤 값이든 할당할 수 있지만, primitive 값을 할당되면 무시한다. function Foo() {} Foo.prototype = 1; // 무시됨 -객체를 할당하면 프로토타입 체인이 동적으로 잘 만들어진다. +반면에 위 예제처럼 객체를 할당하면 프로토타입 체인이 동적으로 잘 만들어진다. ### 성능 -성능이 중요한 부분에서는 프로토타입 체인을 따라 프로퍼티를 찾는 것이 부담일 수 있다. 게다가 없는 프로퍼티에 접근하면 항상 프로토타입 체인 전체를 뒤진다. +프로토타입 체인을 탐색하는 시간이 오래걸릴수록 성능에 부정적인 영향을 줄수있다. 특히 성능이 중요한 코드에서 프로퍼티 탐색시간은 치명적인 문제가 될수있다. 가령, 없는 프로퍼티에 접근하려고 하면 항상 프로토타입 체인 전체를 탐색하게 된다. -객체를 [Iterate](#object.forinloop)하면 프로토타입 체인에 있는 **모든** 프로퍼티가 나열된다. +뿐만아니라 객체를 [순회(Iterate)](#object.forinloop)할때도 프로토타입 체인에 있는 **모든** 프로퍼티를 탐색하게 된다. ### 네이티브 프로토타입의 확장 -JavaScript에서는 `Object.prototype`같이 네이티브 객체들의 프로토타입도 확장할 수 있지만, 이것도 잘못 설계됐다. +종종 `Object.prototype`을 이용해 내장 객체를 확장하는 경우가 있는데, 이것도 역시 잘못 설계된 것중에 하나다. -이것을 [Monkey Patching][1]라고 부르는데 *캡슐화*를 망친다. 굉장히 많이 사용하는 [Prototype][2]도 굳이 기본 타입에 표준도 아닌 것들을 추가하는 이유를 아직 설명하지 못하고 있다. +위와 같이 확장하는 것을 [Monkey Patching][1]라고 부르는데 *캡슐화*를 망친다. 물론 [Prototype][2]같은 유명한 프레임워크들도 이런 확장을 사용하지만, 기본 타입에 표준도 아닌 기능들을 너저분하게 추가하는 이유를 여전히 설명하지 못하고 있다. -기본 타입을 확장하는 것이 좋을 때도 있다. [`Array.forEach`][3]같이 새 JavaScript 엔진에 추가된 기능을 위한 backport를 만들 때는 유용하다. +기본 타입을 확장해야하는 유일한 이유는 [`Array.forEach`][3]같이 새로운 JavaScript 엔진에 추가된 기능을 대비해 미리 만들어 놓는 경우 말고는 없다. ### 결론 -Prototypal Inheritance 모델을 사용하는 코드를 작성하기 전에는 이 모델을 완벽하게 이해해야 한다. 프로토타입 체인과 관련된 성능 문제로 고생하지 않으려면 프로토타입 체인의 길이에 주의하고 너무 길지 않게 적당히 끊어줘야 한다. JavaScript의 새 기능에 대한 호환성을 유지하려는 경우를 제외하고 절대로 네이티브 프로토타입을 확장하면 안 된다. +프로토타입을 이용해 복잡한 코드를 작성하기 전에 반드시 프로토타입 상속 (Prototypal Inheritance) 모델을 완벽하게 이해하고 있어야 한다. 뿐만아니라 프로토타입 체인과 관련된 성능 문제로 고생하지 않으려면 프로토타입 체인이 너무 길지 않도록 항상 주의하고 적당히 끊어줘야 한다. 마지막으로 새로운 JavaScript 기능에 대한 호환성 유지 목적이 아니라면 절대로 네이티브 프로토타입을 확장하지마라. [1]: http://en.wikipedia.org/wiki/Monkey_patch [2]: http://prototypejs.org/ diff --git a/doc/ko/other/timeouts.md b/doc/ko/other/timeouts.md index fb0384ed..2e72036b 100644 --- a/doc/ko/other/timeouts.md +++ b/doc/ko/other/timeouts.md @@ -1,22 +1,22 @@ ### `setTimeout`과 `setInterval` -JavaScript는 비동기이기 때문에 `setTimeout`과 `setInterval` 로 함수의 실행 순서를 조절할 수 있다. +JavaScript는 `setTimeout`과 `setInterval`함수를 이용해 비동기로 함수를 실행시킬수있다. > **Note:** Timeout은 ECMAScript 표준이 아니라 [DOM][1]때문에 구현됐다. function foo() {} var id = setTimeout(foo, 1000); // 0보다 큰 수를 반환한다. -`setTimeout`을 호출하면 timeout의 ID를 반환하고 **대충** 1,000밀리 초 후에 `foo`를 실행시킨다. `foo`는 **딱 한 번만** 실행한다. +`setTimeout`을 호출하면 타이머의 ID를 반환하고 **대략** 1,000밀리 초 후에 `foo`를 실행시킨다. `foo`는 **딱 한 번만** 실행한다. -JavaScript 엔진의 단위 시간(timer resolution)에 따라서 코드를 실행시키고 단일 쓰레드인 JavaScript를 특정 코드가 블록 시켜 버릴 수도 있기 때문에 `setTimeout`으로 코드가 실행돼야 할 시간을 정해줘도 **정확하게 그 시간에 실행되지 않는다.**. +JS엔진은 타이머에 설정한 시간(timer resolution)에 따라서 코드를 실행하지만 단일 쓰레드이기 때문에 특정 코드는 실행이 지연 될수도 있다. 따라서 `setTimeout`으로 코드가 실행돼야 할 시간을 정해줘도 **정확하게 그 시간에 실행되지 않을수도 있다.**. -첫 번째 인자로 넘긴 함수가 실행될 때 컨텍스트인 [`this`](#function.this)는 *Global* 객체를 가리킨다. +첫 번째 인자로 넘긴 함수는 전역 객체가 실행시킨다. 따라서 인자로 넘겨진 함수 내부의 [`this`](#function.this)는 *전역* 객체를 가리키게 된다. function Foo() { this.value = 42; this.method = function() { - // this는 Global 객체를 가리키기 때문에 + // this는 전역 객체를 가리키기 때문에 console.log(this.value); // undefined를 출력한다. }; setTimeout(this.method, 500); @@ -25,24 +25,25 @@ JavaScript 엔진의 단위 시간(timer resolution)에 따라서 코드를 실 > **Note:** `setTimeout`의 첫 번째 파라미터에 **함수** 객체를 넘겨야 하는 데 `setTimeout(foo(), 1000)`처럼 함수의 실행 결과를 넘기는 실수를 저지를 때가 잦다. 이럴 때 `setTimeout`은 그냥 `undefined`를 반환할 뿐이지 에러를 발생시키지 않는다. -### `setInterval`은 계속 함수 호출을 쌓는다(Stacking). +### 함수 호출을 쌓는(Stacking) `setInterval`함수. -`setTimeout`은 딱 한 번 함수를 호출하지만 `setInterval`은 이름처럼 **지정한 시간마다** 함수를 실행해 준다. 이 `setInterval`은 별로다. +`setTimeout`은 딱 한 번 함수를 호출하지만 `setInterval`은 이름처럼 **지정한 시간마다** 함수를 실행시켜준다. 하지만 이 함수의 사용은 좀 생각해봐야한다. -만약 실행하는 코드가 일정시간 동안 블럭되도 `setInterval`은 계속 함수를 호출시키려 든다. 특히 주기가 짧으면 밀리기 쉬워서 함수 호출은 계속 쌓일 수 있다. +`setInterval`은 실행하는 코드가 일정시간 동안 블럭되도 계속해서 함수를 호출하기 때문에 주기가 짧은 경우 함수 호출이 쉽게 쌓여버린다. function foo(){ // 1초 동안 블럭함. } setInterval(foo, 1000); -`foo`는 단순히 호출될 때마다 1초 동안 블럭하는 함수다. +위 코드에서 `foo`함수는 호출될 때마다 1초씩 실행을 지연시킨다. -`foo`가 블럭해도 `setInterval`은 계속 함수 호출을 쌓는다. `foo`의 첫 번째 호출이 끝나도 *10번* 이상의 함수 호출이 쌓여 대기하고 있다. +하지만 `foo`함수가 블럭되더라도 `setInterval`함수는 계속해서 함수 호출을 쌓기 때문에 `foo`함수 호출이 끝나면 *10번* 이상의 함수 호출이 쌓여서 대기하고 있을수도 있다. +(역주: 따라서 함수 호출이 쌓이게 되면 원래 기대했던 실행 주기를 보장받지 못한다.) -### 오래 걸리는 코드 다루기 +### 블럭되는 코드 해결법 -`setTimeout` 으로 함수 자신을 호출하는 방법으로 해결하기가 가장 쉽다. +앞에 문제를 해결하는 가장 쉽고 일반적인 방법은 `setTimeout` 함수에서 자기 자신을 다시 호출하는 방법이다. function foo(){ // something that blocks for 1 second @@ -50,40 +51,40 @@ JavaScript 엔진의 단위 시간(timer resolution)에 따라서 코드를 실 } foo(); -함수 호출이 쌓이지도 않을 뿐만 아니라 `setTimeout` 호출을 해당 함수 안에서 관리하고 `foo` 함수에서 계속 실행할지 말지 조절할 수도 있다. +이 방법은 함수 호출이 쌓이지도 않을 뿐만 아니라 `setTimeout` 호출을 해당 함수 안에서 관리하기 때문에 `foo` 함수에서 계속 실행할지 말지도 조절할 수 있다. -### Timeout 없애기 +### 타이머 없애기 -`clearTimeout`과 `clearInterval` 함수로 setTimeout과 setInterval로 등록한 timeout과 interval을 삭제할 수 있다. `set` 함수들이 반환한 id를 저장했다가 `clear` 함수를 호출하여 삭제한다. +`clearTimeout`과 `clearInterval` 함수로 setTimeout과 setInterval로 등록한 timeout과 interval을 삭제할 수 있다. `set` 함수들이 반환한 id를 저장했다가 `clear` 함수를 호출해서 삭제한다. var id = setTimeout(foo, 1000); clearTimeout(id); -### timeout을 전부 없애기 +### 모든 타이머 없애기 -등록한 timeout과 interval을 한꺼번에 제거하는 메소드는 없다. 구현해서 사용해야 한다. +등록한 timeout과 interval을 한꺼번에 제거하는 내장 함수는 없다. 따라서 좀 무식하지만 직접 구현해야 한다. - // clear "all" timeouts + // "모든" 타이머 지우기 for(var i = 1; i < 1000; i++) { clearTimeout(i); } -Id가 1부터 1000 사이에 있는 timeout들을 제거했지만, 그 외의 것은 아직 남아있다. 또 다른 방법이 있다. `setTimeout`은 항상 호출될 때마다 전보다 1만큼 큰 수를 ID로 반환한다. 이 점을 이용해 1부터 가장최근 ID까지 모두 삭제할 수 있다. +위와 같은 방법은 숫자가 미치지 못하는 타이머는 여전히 남아있을수 있다는 단점이 있다. 또 다른 해결 방법은 타이머가 반환하는 값이 항상 전보다 1만큼 큰 수를 반환한다는 점을 착안한 방법이다. - // clear "all" timeouts + // "모든" 타이머 지우기 var biggestTimeoutId = window.setTimeout(function(){}, 1), i; for(i = 1; i <= biggestTimeoutId; i++) { clearTimeout(i); } -이 방법은 모든 주요 브라우저에서 문제없이 잘 동작한다. 하지만 ID가 항상 순차적이어야 한다고 표준에 명시된 것이 아니다. 그러므로 timeout ID를 모두 저장했다가 삭제하는 것이 가장 안전하다. 그러면 전부 깨끗하게 제거할 수 있다. +이 방법은 모든 주요 브라우저에서 문제없이 잘 동작하지만 ID가 항상 순차적이어야 한다고 표준에 명시된 것이 아니다. 그러므로 timeout ID를 모두 저장했다가 삭제하는 것이 가장 안전하다. 그러면 전부 깨끗하게 제거할 수 있다. -### 숨겨진 `eval` +### 보이지 않게 사용되는 `eval`함수 -`setTimeout`과 `setInterval`의 첫 파라미터에 스트링도 넘길 수 있다. 그렇지만, 내부적으로 `eval`을 사용하는 것이기 때문에 절대 사용하지 말아야 한다. +`setTimeout`과 `setInterval`의 첫 파라미터로 문자열을 넘길 수 있다. 하지만 내부적으로 `eval`을 사용하는 것이기 때문에 절대 사용해서는 안된다. -> **Note:** timeout 함수는 ECMAScript 표준이 아녀서 첫 인자가 스트링 타입일 때에는 JavaScript 구현체마다 다르게 동작한다. 예를 들어, Microsoft의 JScript는 `eval`이 아니라 `Function` 생성자를 사용한다. +> **Note:** timeout 함수는 ECMAScript 표준이 아니기 때문에 문자열로 넘어오는 첫번째 인자에 대한 해석은 구현체마다 다르다. 예를 들어, Microsoft의 JScript는 `eval`이 아니라 `Function` 생성자를 사용한다. function foo() { // 이게 호출됨 @@ -97,7 +98,7 @@ Id가 1부터 1000 사이에 있는 timeout들을 제거했지만, 그 외의 } bar(); -이 경우 `eval`이 [그냥(directly)](#core.eval) 호출되는 것이 아니다. `setTimeout`에 넘겨진 스트링은 *Global* Scope에서 실행되기 때문에 `bar`의 Local 함수 `foo`가 실행되는 것이 아니라 *Global* Scope의 `foo`가 실행된다. +이 경우 `eval`이 [그냥(directly)](#core.eval) 호출되는 것이 아니다. `setTimeout`에 인자로 넘어간 문자열은 *전역* 스코프에서 실행되기 때문에 `bar`함수 영역에 있는 지역 변수 `foo`가 실행되는 것이 아니라 *전역* 스코프에 있는 `foo`가 실행된다. 함수에 파라미터를 넘겨야 하면 스트링을 사용하지 말아야 한다. @@ -115,8 +116,8 @@ Id가 1부터 1000 사이에 있는 timeout들을 제거했지만, 그 외의 ### 결론 -`setTimeout`과 `setInterval`의 파라미터로 스트링은 절대 사용하지 말아야 한다. 핸들러 함수에 인자를 넘겨야 하는 경우도 **분명히** 탈 난다. *익명 함수*을 사용해서 호출해야 한다. +`setTimeout`과 `setInterval`함수에 문자열 인자를 절대 사용해서는 안된다. 핸들러 함수에 인자를 넘기는 코드도 **절대** 좋은 코드가 아니다. *익명 함수*을 사용해서 호출해야 한다. -그리고 `setInterval`은 해당 핸들러가 블럭되든 말든 상관하지 않기 때문에 사용하면 안 된다. +그리고 `setInterval`은 해당 핸들러가 블럭되든 말든 상관하지 않기 때문에 되도록이면 쓰지말자. [1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" diff --git a/doc/ko/types/casting.md b/doc/ko/types/casting.md index eb89f546..7a6e28ea 100644 --- a/doc/ko/types/casting.md +++ b/doc/ko/types/casting.md @@ -1,4 +1,4 @@ -## Type Casting +## 타입 캐스팅 JavaScript는 Weak Typing 언어이기 때문에 필요할 때마다 알아서 타입을 변환한다. @@ -15,9 +15,9 @@ JavaScript는 Weak Typing 언어이기 때문에 필요할 때마다 알아서 10 == 010; 10 == '-10'; -> **ES5 Note:** `0`으로 시작하는 숫자 리터럴은 8진수다. 하지만, ECMAScript 5의 strict 모드에서는 더는 8진수로 해석하지 않는다. +> **ES5 Note:** `0`으로 시작하는 숫자 리터럴은 8진수다. 하지만, ECMAScript 5의 strict 모드에서는 8진수로 더이상 해석하지 않는다. -이런 문제들은 [strict equal operator](#types.equality)로 **미리 방지해야** 한다. 이 operator로 JavaScript의 많은 결점을 보완할 수 있지만, 아직도 weak typing 시스템 때문에 생기는 문제가 많다. +위와 같은 문제들은 ***반드시** [삼중 등호 연산자](#types.equality)를 이용해 해결하길 권한다. 물론 삼중 등호로 많은 결점을 보완할 수 있지만, 여전히 weak typing 시스템 때문에 생기는 많은 문제가 남아있다. ### 기본 타입 생성자 diff --git a/doc/ko/types/equality.md b/doc/ko/types/equality.md index 970b2c14..a9daccb5 100644 --- a/doc/ko/types/equality.md +++ b/doc/ko/types/equality.md @@ -1,12 +1,12 @@ ## 객체 비교하기 -JavaScript에서 객체를 비교하는 방법은 두 가지다. +JavaScript에서 객체를 비교하는 방법은 두 가지가 있다. -### Equality Operator +### 이중 등호 연산자 -`==`가 Equality Operator이다. +이중 등호 연산자는 `==`을 말한다. -JavaScript는 Weak Typing을 따르기 때문에 equality operator가 비교할 때 두 객체의 자료형을 **강제로** 변환한다. +JavaScript는 Weak Typing을 따르기 때문에 이중 등호를 이용해 비교할 때 두 객체의 자료형을 **강제로** 변환한다. "" == "0" // false 0 == "" // true @@ -18,15 +18,15 @@ JavaScript는 Weak Typing을 따르기 때문에 equality operator가 비교할 null == undefined // true " \t\r\n" == 0 // true -이 표는 왜 Equality Operator를 사용하면 안 되는지를 보여준다. 이 복잡한 변환 규칙은 실제로 골치 아픈 버그를 만들어 낸다. +이 표는 이중 등호를 사용하면 왜 안되는지를 보여준다. 이 복잡한 변환 규칙은 실제로 골치 아픈 버그를 만들어 낸다. -게다가 강제로 타입을 변환하는 것은 성능 문제도 일으킨다. 예를 들어 스트링과 숫자를 비교하려면 반드시 숫자로 변환해야 한다. +게다가 강제로 타입을 변환하게 되면 성능에도 영향을 준다. 예를 들어 문자와 숫자를 비교하려면 반드시 먼저 문자를 숫자로 변환해야 한다. -### Strict Equality Operator +### 삼중 등호 연산자 -Strict Equality Operator는 `===`이다. +삼중 등호 연산자는 `===`을 말한다. -강제로 타입을 변환하지 않는 것을 제외하고는 Equality Operator와 똑같다. +삼중 등호는 강제로 타입을 변환하지 않는다는 사실을 제외하면 이중 등호와 동일하다. "" === "0" // false 0 === "" // false @@ -38,11 +38,11 @@ Strict Equality Operator는 `===`이다. null === undefined // false " \t\r\n" === 0 // false -이 결과가 훨씬 명확하고 문제를 빨리 발견할 수 있게 해준다. 이 Operator를 사용하면 코드가 좀 더 튼튼하고 비교하는 두 객체의 타입이 다르면 성능도 빠르다. +위 결과가 훨씬 더 명확하고 문제가 쉽게 드러난다. 삼중 등호를 사용하면 코드를 좀 더 튼튼하게 만들수 있고, 비교하는 두 객체의 타입이 다르면 더 좋은 성능을 얻을 수도 있다. ### 객체 비교하기 -`==`와 `===`는 둘 다 **Equality** Operator지만 비교하는 객체 중 적어도 한 개가 Object 타입일 때에는 다르게 동작한다. +이중 등호와(`==`)와 삼중 등호(`===`)는 둘 다 **값을 비교하는** 연산이지만 피연산자중에 Object 타입이 하나라도 있으면 다르게 동작한다. {} === {}; // false new String('foo') === 'foo'; // false @@ -50,8 +50,8 @@ Strict Equality Operator는 `===`이다. var foo = {}; foo === foo; // true -두 Operator 모두 **같은 객체(identity)**인지 비교하는 것이지 객체의 값이 같은지 비교하는 것이 아니다. C에서 포인터를 비교하거나 Python의 is처럼 같은 인스턴스인지 비교하는 것이다. +두 연산자 모두 두 객체의 값이 같은지를 비교하지 않고, 두 객체가 **같은 객체(identity)**인지를 비교한다. C에서 포인터를 비교하거나 Python의 is처럼 같은 인스턴스인지 비교하는 것이다. ### 결론 -반드시 **Strict Equality Operator**를 사용해야 한다. 비교하기 위해서 꼭 타입 변환이 필요하면 언어의 복잡한 변환 규칙에 맡기지 말고 꼭 명시적으로 변환하고 나서 비교해야 한다. +**삼중 등호 연산자**를 사용할 것을 강력하게 권한다. 비교하기 위해서 타입 변환이 필요하면 언어의 복잡한 변환 규칙에 맡기지 말고 꼭 명시적으로 변환한 후에 비교해야 한다. diff --git a/doc/ko/types/instanceof.md b/doc/ko/types/instanceof.md index 560f4435..621f8121 100644 --- a/doc/ko/types/instanceof.md +++ b/doc/ko/types/instanceof.md @@ -1,8 +1,8 @@ -## `instanceof` +## `instanceof` 연산자 -`instanceof`는 두 객체의 생성자를 비교하는 것이고 직접 만든 타입의 객체를 비교할 때 유용하다. 기본 타입만 생각하면 이 연산자는 [typeof](#types.typeof)처럼 거의 쓸모 없다. +`instanceof`연산자는 두 피연산자의 생성자를 비교할때 사용하고 직접 만든 객체를 비교할 때 매우 유용하다. 내장 타입에 쓰는 경우에는 [typeof](#types.typeof)처럼 거의 쓸모가 없다. -### 직접 만든 타입의 객체를 `intanceof`로 비교하기 +### 커스텀 객체를 `intanceof`로 비교하기 function Foo() {} function Bar() {} @@ -11,12 +11,12 @@ new Bar() instanceof Bar; // true new Bar() instanceof Foo; // true - // Bar.prototype에 function 객체인 Foo를 할당하면 + // Bar.prototype에 함수 객체인 Foo를 할당하면 // Bar의 인스턴스는 Foo의 인스턴스가 아니다. Bar.prototype = Foo; new Bar() instanceof Foo; // false -### 기본 타입 객체를 `intanceof`로 비교하기 +### 기본 내장 객체 타입을 `intanceof`로 비교하기 new String('foo') instanceof String; // true new String('foo') instanceof Object; // true diff --git a/doc/ko/types/typeof.md b/doc/ko/types/typeof.md index fb7c6576..a2db9595 100644 --- a/doc/ko/types/typeof.md +++ b/doc/ko/types/typeof.md @@ -1,10 +1,10 @@ -## `typeof` +## `typeof` 연산자 -`typeof`도 [`instanceof`](#types.instanceof)와 함께 JavaScript에서 치명적으로 잘못 설계된 부분이다. 이건 정말 아무짝에 쓸모없다. +`typeof` 연산자도 [`instanceof`](#types.instanceof) 연산자와 함께 JavaScript에서 치명적으로 잘못 설계된 부분이다. 이건 정말이지 아무짝에도 쓸모가 없다. -`instanceof`는 그래도 쓸 데가 좀 있었는데 `typeof`는 딱 한 군데에만 써먹을 수 있다. 객체의 타입을 검사할 일이 없다. +`instanceof` 연산자는 그래도 여전히 쓸만한 데가 좀 있는데 `typeof` 연산자는 객체의 타입을 검사하는 것 외에는 쓸만한데가 없고, 이마저도 거의 쓸일이 없다. -> **Note:** `typeof`는 함수처럼 `typeof(obj)`로 사용할 수 있다. 하지만, 이것은 함수를 호출하는 것이 아니라 단순히 `()`안의 값이 반환되고 `typeof`가 적용되는 것이다. `typeof`라는 함수는 **없다**. +> **Note:** `typeof` 연산자는 함수처럼 `typeof(obj)`로 사용할수 있지만 함수 호출은 아니다. 괄호 안의 값은 `typeof`의 피연산자로 적용되고 그 결과가 반환된다. `typeof`라는 함수는 **없다**. ### JavaScript 타입 표 @@ -26,13 +26,13 @@ {} Object object new Object() Object object -이 표에서 *Type*은 `typeof`가 반환하는 값이다. 표에서 본 것처럼 이 값은 계속 쓸모없다. +위 표에서 *Type*은 `typeof`가 반환하는 값이다. 위 표에서처럼 일치되는 값이 거의 없다. -Class는 객체 내부에 있는 `[[Class]]` 프로퍼티의 값이다. +위 표에서 *Class*는 객체 내부에 있는 `[[Class]]` 프로퍼티의 값을 말한다. > **표준**에는 `[[Class]]`의 값은 `Arguments`, `Array`, `Boolean`, `Date`, `Error`, `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`중 하나라고 나와있다. -`[[Class]]`의 값을 가져다 쓰려면 `Object.prototype`의 `toString` 메소드를 사용해야 한다. +`[[Class]]` 프로퍼티의 값을 가져다 쓰려면 `Object.prototype`의 `toString` 메소드를 사용한다. ### 객체의 클래스 @@ -54,10 +54,10 @@ Class는 객체 내부에 있는 `[[Class]]` 프로퍼티의 값이다. typeof foo !== 'undefined' -이것은 `foo`가 정의됐는지 아닌지를 확인해준다. 정의되지 않은 변수에 접근하면 `ReferenceError` 나는데 이것을 방지할 수 있다. `typeof`가 유용한 건 이때뿐이다. +위 코드는 `foo`가 정의됐는지 아닌지를 확인해준다. 정의되지 않은 변수에 접근하면 `ReferenceError` 나는데 이것을 방지할 수 있다. `typeof`가 유용한 건 이때뿐이다. ### 결론 -객체의 타입을 검사하려면 `Object.prototype.toString`를 사용해야 한다. 다른 방법은 신뢰할 수 없다. 위 표에서 보여준 것처럼 typeof가 반환하는 값은 표준에 나와 있지 않기 때문에 구현마다 다르다. +객체의 타입을 검사하려면 `Object.prototype.toString`를 사용해야 한다. 다른 방법은 신뢰할 수 없다. 위 표에서 보여준 것처럼 typeof가 반환하는 값은 표준에 나와 있지 않기 때문에 구현방법도 제각각이다. -변수가 정의됐는지 확인할 때는 빼고 **목숨을 걸고** `typeof`를 사용하지 못하게 해야 한다. +변수가 정의됐는지 확인할 때를 제외하고 **가급적** `typeof`는 피해야한다. \ No newline at end of file From 5f34e58254739b46e13ab46dc75159c9e1b5b135 Mon Sep 17 00:00:00 2001 From: "ByungDae, Sohn" Date: Wed, 26 Dec 2012 11:14:29 +0900 Subject: [PATCH 002/216] improve transration --- doc/ko/function/arguments.md | 2 +- doc/ko/intro/index.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/ko/function/arguments.md b/doc/ko/function/arguments.md index dcc5e8a2..5ea383cb 100644 --- a/doc/ko/function/arguments.md +++ b/doc/ko/function/arguments.md @@ -64,7 +64,7 @@ JavaScript의 모든 함수 스코프에는 `arguments`라는 특별한 변수 ### 성능에 대한 오해와 진실. -`arguments` 객체는 항상 만들어 지지만 예외도 있다. `arguments`라는 이름의 변수를 Function 안에 정의하거나 그 이름으로 파라미터를 만들면 `arguemnts` 객체는 만들어지지 않는다. 그렇지만, 이럴때는 어차피 안쓰겠다는 의미니까 상관 없다. +`arguments` 객체는 항상 만들어지지만 두가지 예외사항이 있다. `arguments`라는 이름으로 변수를 함수 안에 정의하거나 arguments 객체로 넘겨받는 인자중 하나라도 정식 인자로 받아서 사용하면 `arguemnts` 객체는 만들어지지 않는다. 하지만 뭐 이런 경우들은 어차피 arguments 객체를 안쓰겠다는 의미니까 상관 없다. 그리고 *getter*와 *setter*는 항상 생성되기 때문에 getter/setter를 사용하는 것은 성능에 별 영향을 끼치지 않는다. 예제처럼 단순한 코드가 아니라 `arguments` 객체를 다방면으로 활용하는 실제 코드에서도 마찬가지다. diff --git a/doc/ko/intro/index.md b/doc/ko/intro/index.md index 87b72473..2fe16ea7 100644 --- a/doc/ko/intro/index.md +++ b/doc/ko/intro/index.md @@ -14,7 +14,8 @@ JavaScript Garden은 단순히 JavaScript 언어 자체를 설명하려 만들 - [Andreas Blixt][6] (언어 교정) ## 번역 - - [박창우][손병대][] + - [박창우][] + - [손병대][] [박창우]: https://github.com/pismute [손병대]: https://github.com/miconblog From 9c4235052673113663baf58969f9acc98611cf48 Mon Sep 17 00:00:00 2001 From: "ByungDae, Sohn" Date: Wed, 26 Dec 2012 11:37:15 +0900 Subject: [PATCH 003/216] improve translation --- doc/ko/function/arguments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ko/function/arguments.md b/doc/ko/function/arguments.md index 5ea383cb..097bec13 100644 --- a/doc/ko/function/arguments.md +++ b/doc/ko/function/arguments.md @@ -64,7 +64,7 @@ JavaScript의 모든 함수 스코프에는 `arguments`라는 특별한 변수 ### 성능에 대한 오해와 진실. -`arguments` 객체는 항상 만들어지지만 두가지 예외사항이 있다. `arguments`라는 이름으로 변수를 함수 안에 정의하거나 arguments 객체로 넘겨받는 인자중 하나라도 정식 인자로 받아서 사용하면 `arguemnts` 객체는 만들어지지 않는다. 하지만 뭐 이런 경우들은 어차피 arguments 객체를 안쓰겠다는 의미니까 상관 없다. +`arguments` 객체는 항상 만들어지지만 두가지 예외사항이 있다. `arguments`라는 이름으로 변수를 함수 안에 정의하거나 arguments 객체로 넘겨받는 인자중 하나라도 정식 인자로 받아서 사용하면 `arguemnts` 객체는 만들어지지 않는다. 사실 arguments 객체를 쓰든 쓰지않든 중요한건 아니므로 신경쓰지 않아도 된다. 그리고 *getter*와 *setter*는 항상 생성되기 때문에 getter/setter를 사용하는 것은 성능에 별 영향을 끼치지 않는다. 예제처럼 단순한 코드가 아니라 `arguments` 객체를 다방면으로 활용하는 실제 코드에서도 마찬가지다. From 1f2bc63402dbfbc1e2804f72ffb7b1a6824d6947 Mon Sep 17 00:00:00 2001 From: PiNotEqual3 Date: Tue, 22 Jan 2013 10:10:25 +0100 Subject: [PATCH 004/216] Update doc/de/intro/index.md German Intro --- doc/de/intro/index.md | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/doc/de/intro/index.md b/doc/de/intro/index.md index 94250e2f..566e3235 100644 --- a/doc/de/intro/index.md +++ b/doc/de/intro/index.md @@ -1,4 +1,43 @@ ## Einführung -Demnächst. +**JavaScript Garden** ist eine wachsende Sammlung von Erklärungen der verzwicktesten Teile von JavaScript. Es gibt +Hinweise um häufige Fehler, Performance Probleme und schlechten Stil zu vermeiden. +JavaScript Garden ist **keine** Anleitung um JavaScript zu lernen. Ein grundlegendes Verständnis der Sprache wird +wärmstens empfohlen. Eine gute [Einführung][1] findet sich zum Beispiel im Mozilla Developer Network. + +## Die Autoren + +Dieses Dokument wurde von zwei liebenswerten [Stack Overflow][2] Benutzern geschrieben: [Ivo Wetzel][3] +(Text) and [Zhang Yi Jiang][4] (Design). + +## Beitragende + + - [Caio Romão][5] (Rechtschreibung) + - [Andreas Blixt][6] (Sprachanpassungen) + +## Hosting + +JavaScript Garden wird von GitHub bereitgestellt, aber es wird auch von [Cramer Development][7] unterstützt durch +einen Mirror auf [JavaScriptGarden.info][8]. + +## Lizenz + +JavaScript Garden wurde unter der [MIT Lizenz][9] veröffentlich und wird von [GitHub][10] veröffentlicht. Wenn du +Fehler findest mach bitte ein [Ticket][11] auf oder einen pull request ins repository. Du kannst uns auch im +[JavaScript Raum][12] des Stack Overflow Chats finden. + +Mehr demnächst. + +[1]: https://developer.mozilla.org/en/JavaScript/Guide +[2]: http://stackoverflow.com/ +[3]: http://stackoverflow.com/users/170224/ivo-wetzel +[4]: http://stackoverflow.com/users/313758/yi-jiang +[5]: https://github.com/caio +[6]: https://github.com/blixt +[7]: http://cramerdev.com/ +[8]: http://javascriptgarden.info/ +[9]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE +[10]: https://github.com/BonsaiDen/JavaScript-Garden +[11]: https://github.com/BonsaiDen/JavaScript-Garden/issues +[12]: http://chat.[stackoverflow.com/rooms/17/javascript From 1546101896aab6a09303fcecbdf18d72726e6b58 Mon Sep 17 00:00:00 2001 From: Bruno Coelho Date: Thu, 24 Jan 2013 09:22:13 -0300 Subject: [PATCH 005/216] Passing the wrong object to hasOwnProperty method. --- doc/en/object/hasownproperty.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/en/object/hasownproperty.md b/doc/en/object/hasownproperty.md index 0033b4ba..03e3feea 100644 --- a/doc/en/object/hasownproperty.md +++ b/doc/en/object/hasownproperty.md @@ -5,26 +5,26 @@ 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 +> 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 +`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; + 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. +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 @@ -45,7 +45,7 @@ necessary to use an *external* `hasOwnProperty` to get correct results. ({}).hasOwnProperty.call(foo, 'bar'); // true // It's also possible to use the hasOwnProperty property from the Object property for this purpose - Object.prototype.hasOwnProperty.call(obj, 'bar'); // true + Object.prototype.hasOwnProperty.call(foo, 'bar'); // true ### In Conclusion From 4a94b223de56462297fa6e018f64ad13bf3abd86 Mon Sep 17 00:00:00 2001 From: Bruno Coelho Date: Fri, 25 Jan 2013 14:08:19 -0300 Subject: [PATCH 006/216] Cosmetic. --- doc/en/object/prototype.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/doc/en/object/prototype.md b/doc/en/object/prototype.md index abb67c58..cb053526 100644 --- a/doc/en/object/prototype.md +++ b/doc/en/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 +While this is often considered to be one of JavaScript's weaknesses, the prototypal inheritance model is in fact more powerful than the classic model. It is, for example, fairly trivial to build a classic model on top of a prototypal model, while the other way around is a far more difficult task. JavaScript is the only widely used language that features prototypal inheritance, so it can take time to adjust to the differences between the two -models. +models. The first major difference is that inheritance in JavaScript uses *prototype chains*. > **Note:** Simply using `Bar.prototype = Foo.prototype` will result in both objects -> sharing the **same** prototype. Therefore, changes to either object's prototype -> will affect the prototype of the other as well, which in most cases is not the +> 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() { @@ -36,11 +36,11 @@ chains*. // Make sure to list Bar as the actual constructor Bar.prototype.constructor = Bar; - var test = new Bar() // create a new bar instance + var test = new Bar(); // create a new bar instance // 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 @@ chains*. { toString: ... /* etc. */ } In the code above, the object `test` will inherit from both `Bar.prototype` and -`Foo.prototype`; hence, it will have access to the function `method` that was +`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. However, +chains, it is still possible to assign **any** given value to it. However, primitives will simply get ignored when assigned as a prototype. function Foo() {} @@ -85,9 +85,9 @@ creation of prototype chains. The lookup time for properties that are high up on the prototype chain can have a negative impact on performance, and this may be significant in code where performance is critical. Additionally, trying to access non-existent properties -will always traverse the full prototype chain. +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 be enumerated. ### Extension of Native Prototypes @@ -95,12 +95,12 @@ 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 popular frameworks such as [Prototype][2], there is still no good +This technique is called [monkey patching][1] and breaks *encapsulation*. While +used by popular frameworks such as [Prototype][2], there is still no good reason for cluttering built-in types with additional *non-standard* functionality. -The **only** good reason for extending a built-in prototype is to backport -the features of newer JavaScript engines; for example, +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 From 33a89e7857b5b82413af588ac6708117aef3dc4c Mon Sep 17 00:00:00 2001 From: howardchi Date: Wed, 13 Mar 2013 20:08:28 +0800 Subject: [PATCH 007/216] translate to Tranditioin Chinese --- doc/zh(Trandition)/array/constructor.md | 28 +++ doc/zh(Trandition)/array/general.md | 46 ++++ doc/zh(Trandition)/core/delete.md | 85 +++++++ doc/zh(Trandition)/core/eval.md | 42 ++++ doc/zh(Trandition)/core/semicolon.md | 103 +++++++++ doc/zh(Trandition)/core/undefined.md | 70 ++++++ doc/zh(Trandition)/function/arguments.md | 118 ++++++++++ doc/zh(Trandition)/function/closures.md | 98 ++++++++ doc/zh(Trandition)/function/constructors.md | 128 +++++++++++ doc/zh(Trandition)/function/general.md | 48 ++++ doc/zh(Trandition)/function/scopes.md | 233 ++++++++++++++++++++ doc/zh(Trandition)/function/this.md | 111 ++++++++++ doc/zh(Trandition)/index.json | 69 ++++++ doc/zh(Trandition)/intro/index.md | 47 ++++ doc/zh(Trandition)/object/forinloop.md | 51 +++++ doc/zh(Trandition)/object/general.md | 99 +++++++++ doc/zh(Trandition)/object/hasownproperty.md | 57 +++++ doc/zh(Trandition)/object/prototype.md | 118 ++++++++++ doc/zh(Trandition)/other/timeouts.md | 167 ++++++++++++++ doc/zh(Trandition)/types/casting.md | 70 ++++++ doc/zh(Trandition)/types/equality.md | 71 ++++++ doc/zh(Trandition)/types/instanceof.md | 38 ++++ doc/zh(Trandition)/types/typeof.md | 86 ++++++++ 23 files changed, 1983 insertions(+) create mode 100644 doc/zh(Trandition)/array/constructor.md create mode 100644 doc/zh(Trandition)/array/general.md create mode 100644 doc/zh(Trandition)/core/delete.md create mode 100644 doc/zh(Trandition)/core/eval.md create mode 100644 doc/zh(Trandition)/core/semicolon.md create mode 100644 doc/zh(Trandition)/core/undefined.md create mode 100644 doc/zh(Trandition)/function/arguments.md create mode 100644 doc/zh(Trandition)/function/closures.md create mode 100644 doc/zh(Trandition)/function/constructors.md create mode 100644 doc/zh(Trandition)/function/general.md create mode 100644 doc/zh(Trandition)/function/scopes.md create mode 100644 doc/zh(Trandition)/function/this.md create mode 100644 doc/zh(Trandition)/index.json create mode 100644 doc/zh(Trandition)/intro/index.md create mode 100644 doc/zh(Trandition)/object/forinloop.md create mode 100644 doc/zh(Trandition)/object/general.md create mode 100644 doc/zh(Trandition)/object/hasownproperty.md create mode 100644 doc/zh(Trandition)/object/prototype.md create mode 100644 doc/zh(Trandition)/other/timeouts.md create mode 100644 doc/zh(Trandition)/types/casting.md create mode 100644 doc/zh(Trandition)/types/equality.md create mode 100644 doc/zh(Trandition)/types/instanceof.md create mode 100644 doc/zh(Trandition)/types/typeof.md diff --git a/doc/zh(Trandition)/array/constructor.md b/doc/zh(Trandition)/array/constructor.md new file mode 100644 index 00000000..7e768014 --- /dev/null +++ b/doc/zh(Trandition)/array/constructor.md @@ -0,0 +1,28 @@ +## `Array` 的建構函式 + +`Array` 的建構函式在處理參數上一直有模糊的地帶,所以建議使用 `array`的字面語法來使用 - `[]` - 來新增一個的Array + + [1, 2, 3]; // 結果: [1, 2, 3] + new Array(1, 2, 3); // 結果: [1, 2, 3] + + [3]; // 結果: [3] + new Array(3); // 結果: [] + new Array('3') // 結果: ['3'] + +在上面的範例 `new Array(3)` 當只有一個參數傳入到 `Array` 的建構函數 +且那個參數事宜個數字,建構函數會回傳空值 +但是 `Array` 長度的屬性會變成跟那個參數一樣(以此範例來看他回傳的長度為 3) +**注意** 只有他長度的屬性會被設定,整個 Array裡面的數值都不會初始化 + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, 數值沒有被設定進去 + +被設定用來當做 `Array` 的長度只有少數情況使用 +先設定 `Array` 的長度可以用一下的範例來避免使用 `for loop` 的麻煩 + + new Array(count + 1).join(stringToRepeat); + +### 結語 + +`Array` 的建構函式需要避免,建議使用字面語法。因為他們比較簡短、也更增加閱讀性 diff --git a/doc/zh(Trandition)/array/general.md b/doc/zh(Trandition)/array/general.md new file mode 100644 index 00000000..f4bd9252 --- /dev/null +++ b/doc/zh(Trandition)/array/general.md @@ -0,0 +1,46 @@ +## Array 迴圈和屬性 + +雖然在 Javascript 中 Array 都是 Objects,但是沒有好的理由要使用他 +在 [`for in`](#object.forinloop) 的迴圈中。事實上有很多原因要避免使用 `for in` 在 Array 之中 + +> **注意:** Javascript Arrays **不是** *關連性 Arrays* +> 只有 [objects](#object.general) 來管理建值的相對應關係 +> Arrays 是**保持** 順序的,Objects **則沒有** + +因為 `for in` 迴圈會列舉所有在原型 Array 上的屬性因為他會使用[`hasOwnProperty`](#object.hasownproperty), 這會使得 Array 比原本的 `for` 迴圈慢上二十幾倍 + +### 迴圈 + +為了要達到最好的性能所以最好使用 `for` 迴圈來讀取一個 Array 裡面的數值。 + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +在上面的例子中利用 `l = list.length` 來處理 Array 的長度問題。 + +雖然 `length` 屬性是屬於 Array 中其中一個屬性,但是他還使有一定的性能消耗在每次循環的訪問。 +近期 Javascript 使用 **may** 來解決在這上面的效率問題,但是在現在的引擎上還不一定有支援。 + +實際上,不使用暫存 Array 長度的方式比使用暫存的版本還要慢很多。 + +### `length` 的屬性 + +`length` 屬性中的 *getter* 直接回傳在 Array 之中的程度,而 *setter* 可以用來 **刪除** Array。 + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo.push(4); + foo; // [1, 2, 3, undefined, undefined, undefined, 4] + +在上面的例子可以看到,如果給的長度比較小他就會去刪除 Array 中的數值。如果比較大的話,他就會自己增加一些 `undefined` 的數值進去 + +### 結語 + +為了達到更好的效率,建議使用 `for` 迴圈還有暫存 `length` 的屬性。 +而 `for in` 迴圈則是會讓程式中有更多的錯誤和性能問題。 + diff --git a/doc/zh(Trandition)/core/delete.md b/doc/zh(Trandition)/core/delete.md new file mode 100644 index 00000000..067fcfdb --- /dev/null +++ b/doc/zh(Trandition)/core/delete.md @@ -0,0 +1,85 @@ +## The `delete` Operator + +In short, it's *impossible* to delete global variables, functions and some other +stuff in JavaScript which have a `DontDelete` attribute set. + +### Global code and Function code + +When a variable or a function is defined in a global or a [function +scope](#function.scopes) it is a property of either the Activation object or +the Global object. Such properties have a set of attributes, one of which is +`DontDelete`. Variable and function declarations in global and function code +always create properties with `DontDelete`, and therefore cannot be deleted. + + // global variable: + var a = 1; // DontDelete is set + delete a; // false + a; // 1 + + // normal function: + function f() {} // DontDelete is set + delete f; // false + typeof f; // "function" + + // reassigning doesn't help: + f = 1; + delete f; // false + f; // 1 + +### Explicit properties + +Explicitly set properties can be deleted normally. + + // explicitly set property: + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // true + delete obj.y; // true + obj.x; // undefined + obj.y; // undefined + +In the example above, `obj.x` and `obj.y` can be deleted because they have no +`DontDelete` atribute. That's why the example below works too. + + // this works fine, except for IE: + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // true - just a global var + delete GLOBAL_OBJECT.a; // true + GLOBAL_OBJECT.a; // undefined + +Here we use a trick to delete `a`. [`this`](#function.this) here refers +to the Global object and we explicitly declare variable `a` as its property +which allows us to delete it. + +IE (at least 6-8) has some bugs, so the code above doesn't work. + +### Function arguments and built-ins + +Functions' normal arguments, [`arguments` objects](#function.arguments) +and built-in properties also have `DontDelete` set. + + // function arguments and properties: + (function (x) { + + delete arguments; // false + typeof arguments; // "object" + + delete x; // false + x; // 1 + + function f(){} + delete f.length; // false + typeof f.length; // "number" + + })(1); + +### Host objects + +The behaviour of `delete` operator can be unpredictable for hosted objects. Due +to the specification, host objects are allowed to implement any kind of behavior. + +### In conclusion + +The `delete` operator often has unexpected behaviour and can only be safely +used to delete explicitly set properties on normal objects. diff --git a/doc/zh(Trandition)/core/eval.md b/doc/zh(Trandition)/core/eval.md new file mode 100644 index 00000000..e1b14292 --- /dev/null +++ b/doc/zh(Trandition)/core/eval.md @@ -0,0 +1,42 @@ +## 為什麼不要使用 `eval` + +因為 `eval` 函數會在 Javascript 的區域性的區間執行那段程式碼。 + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +但是, `eval` 只接受直接的呼叫而且那個函數只能叫做 `eval`,才能在一個區段中執行。 + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +所有的 `eval` 都應該去比免試用。有 99.9% 的使用情況都可以 **不必** 使用到而達到同等效果。 + +### 偽裝的 `eval` + +[定時函數](#other.timeouts) `setTimeout` 和 `setInterval` 都可以接受一個字串當做他們第一個參數。這些字串 **永遠** 都會在全域範圍內執行,因此在這種情況下 `eval` 沒有被直接的使用。 + +### 安全上的顧慮 + +`eval` 同樣有安全上的問題,因為所有的程式碼都可以被直接執行。 +而他不應去執行一串未知的字串或是來自不幸任的來源。 + +### 結語 + +`eval` 應該永遠不要去只用它,任何的程式在被他執行後都有性能和安全上的考慮。如果有情況需要去使用他,他都不應該列為第一順位的解決方法。 + +應該有更好的方法能夠去使用,但是最好都不要去使用 `eval`。 + diff --git a/doc/zh(Trandition)/core/semicolon.md b/doc/zh(Trandition)/core/semicolon.md new file mode 100644 index 00000000..02a3ac35 --- /dev/null +++ b/doc/zh(Trandition)/core/semicolon.md @@ -0,0 +1,103 @@ +## 自動插入分號 + +雖然 JavaScript 有 C 語言的語法,但是他不強制一定要加上分號。 +所以分號可以被忽略。 + +Javascript 並 **不是** 一個不需要分號的語言。實際上,它需要分號來讓程式碼更容易被理解。因此 Javascript 的編譯器中遇到了缺少分號的情形,它會自動的在程式碼中插入分號。 + + var foo = function() { + } // 編輯錯誤,因沒分號 + test() + +這時候編譯器在編輯的時候,會自動的加上分號,然後重新編輯。 + + var foo = function() { + }; // 沒有錯誤,編輯繼續 + test() + +自動的加入分號是被認為 **最大** 的設計缺陷之一,因為它能改變程式碼的行為。 + +### 工作原理 + +下面的程式碼中沒有使用任何的分號,所以編譯器需要去決定在哪些地方加入分號。 + + (function(window, undefined) { + function test(options) { + log('testing!') + + (options.list || []).forEach(function(i) { + + }) + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ) + + return + { + foo: function() {} + } + } + window.test = test + + })(window) + + (function(window) { + window.someLibrary = {} + + })(window) + +下面的程式碼是編譯器 **猜測** 的結果。 + + (function(window, undefined) { + function test(options) { + + // 沒有加入分號,兩行被合併為一行 + log('testing!')(options.list || []).forEach(function(i) { + + }); // <- 插入分號 + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ); // <- 插入分號 + + return; // <- 插入分號,改變了 return 的表達行為 + { // 作為另一個程式碼的處理 + + // 被當做一個獨立的函數來看 + foo: function() {} + }; // <- 插入分號 + } + window.test = test; // <- 插入分號 + + // 兩行又被合併 + })(window)(function(window) { + window.someLibrary = {}; // <- 插入分號 + + })(window); //<- 插入分號 + +> **注意:** 在這個範例中 Javascript 編譯器沒有正確的處理 `return` ,因為緊接的換行符號。 +> 雖然這不能算是自動分號插入的錯誤,但是它是非常不樂見的效果。 + +編譯器在上面的程式碼中改變了原本程式碼的行為。在一些情況下,會做出 **錯誤的行為** + +### 前置括號 + +在這種前置括號的情況下,編譯器 **不會** 自動的插入分號。 + + log('testing!') + (options.list || []).forEach(function(i) {}) + +上面的程式碼被編譯器轉為只有一行程式 + + log('testing!')(options.list || []).forEach(function(i) {}) + +以上的範例中 `log` 有 **很大** 的可能 **不是** 回傳一個函數。然而這個情況下會出現 `TypeError` 的錯誤或是會出現 `undefined is not a function` . + +### 結語 + +建議永遠 **不要** 忽略分號。同樣的也建議大括號應在他對應的表達式在同一行。在 `if... else...`的表達式中也是如此,不應省略大括號。 +這個習慣可以不僅僅是讓你的程式更一致,也可以避免編譯器因為改變程式而出錯。 + diff --git a/doc/zh(Trandition)/core/undefined.md b/doc/zh(Trandition)/core/undefined.md new file mode 100644 index 00000000..51101912 --- /dev/null +++ b/doc/zh(Trandition)/core/undefined.md @@ -0,0 +1,70 @@ +## `undefined` 和 `null` + +JavaScript 中有兩個表示空值的方式, `null` 和 `undefined` , `undefined`式比較常用的一種。 + +### `undefined` 的值 + +`undefined` 是一個值為 `undefined` 的類型。 + +語言中也定義了一個全域變數,它的值為 `undefined`,這個變數的被稱作 `undefined` 。 +這個變數 **不是** 一個常數,也不是一個關鍵字。這表示它的值可以被輕易的覆蓋。 + +> **ES5 提示: ** `undefined` 在 ECMAScript 5 裡 **不再是** *可寫* 的 +> 但是它的名稱還是可以被隱藏,比如說定義一個函數為 `undefined`。 + +這裡有一些例子會回傳 `undefined` 的值: + + - Accessing the (unmodified) global variable `undefined`. + - Accessing a declared *but not* yet initialized variable. + - Implicit returns of functions due to missing `return` statements. + - `return` statements that do not explicitly return anything. + - Lookups of non-existent properties. + - Function parameters that do not have any explicit value passed. + - Anything that has been set to the value of `undefined`. + - Any expression in the form of `void(expression)` + +### Handling Changes to the Value of `undefined` + +Since the global variable `undefined` only holds a copy of the actual *value* of +`undefined`, assigning a new value to it does **not** change the value of the +*type* `undefined`. + +Still, in order to compare something against the value of `undefined`, it is +necessary to retrieve the value of `undefined` first. + +To protect code against a possible overwritten `undefined` variable, a common +technique used is to add an additional parameter to an [anonymous +wrapper](#function.scopes) that gets no argument passed to it. + + var undefined = 123; + (function(something, foo, undefined) { + // undefined in the local scope does + // now again refer to the value `undefined` + + })('Hello World', 42); + +Another way to achieve the same effect would be to use a declaration inside the +wrapper. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +The only difference here is that this version results in 4 more bytes being +used in case it is minified, and there is no other `var` statement inside the +anonymous wrapper. + +### Uses of `null` + +While `undefined` in the context of the JavaScript language is mostly used in +the sense of a traditional *null*, the actual `null` (both a literal and a type) +is more or less just another data type. + +It is used in some JavaScript internals (like declaring the end of the +prototype chain by setting `Foo.prototype = null`), but in almost all cases, it +can be replaced by `undefined`. + + diff --git a/doc/zh(Trandition)/function/arguments.md b/doc/zh(Trandition)/function/arguments.md new file mode 100644 index 00000000..2394177d --- /dev/null +++ b/doc/zh(Trandition)/function/arguments.md @@ -0,0 +1,118 @@ +## The `arguments` Object + +Every function scope in JavaScript can access the special variable `arguments`. +This variable holds a list of all the arguments that were passed to the function. + +> **Note:** In case `arguments` has already been defined inside the function's +> scope either via a `var` statement or being the name of a formal parameter, +> the `arguments` object will not be created. + +The `arguments` object is **not** an `Array`. While it has some of the +semantics of an array - namely the `length` property - it does not inherit from +`Array.prototype` and is in fact an `Object`. + +Due to this, it is **not** possible to use standard array methods like `push`, +`pop` or `slice` on `arguments`. While iteration with a plain `for` loop works +just fine, it is necessary to convert it to a real `Array` in order to use the +standard `Array` methods on it. + +### Converting to an Array + +The code below will return a new `Array` containing all the elements of the +`arguments` object. + + Array.prototype.slice.call(arguments); + +Because this conversion is **slow**, it is **not recommended** to use it in +performance-critical sections of code. + +### Passing Arguments + +The following is the recommended way of passing arguments from one function to +another. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // do stuff here + } + +Another trick is to use both `call` and `apply` together to 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 only time the `arguments` object is not created is where it is declared as +a name inside of a function or one of its formal parameters. It does not matter +whether it is used or not. + +Both *getters* and *setters* are **always** created; thus, using it has nearly +no performance impact at all, especially not in real world code where there is +more than a simple access to the `arguments` object's properties. + +> **ES5 Note:** These *getters* and *setters* are not created in strict mode. + +However, there is one case which will drastically reduce the performance in +modern JavaScript engines. That case is the use of `arguments.callee`. + + function foo() { + arguments.callee; // do something with this function object + arguments.callee.caller; // and the calling function object + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // Would normally be inlined... + } + } + +In the above code, `foo` can no longer be a subject to [inlining][1] since it +needs to know about both itself and its caller. This not only defeats possible +performance gains that would arise from inlining, but it also breaks encapsulation +because the function may now be dependent on a specific calling context. + +Making use of `arguments.callee` or any of its properties is **highly discouraged**. + +> **ES5 Note:** In strict mode, `arguments.callee` will throw a `TypeError` since +> its use has been deprecated. + +[1]: http://en.wikipedia.org/wiki/Inlining + + diff --git a/doc/zh(Trandition)/function/closures.md b/doc/zh(Trandition)/function/closures.md new file mode 100644 index 00000000..76f2d078 --- /dev/null +++ b/doc/zh(Trandition)/function/closures.md @@ -0,0 +1,98 @@ +## Closures and References + +One of JavaScript's most powerful features is the availability of *closures*. +With closures, scopes **always** keep access to the outer scope, in which they +were defined. Since the only scoping that JavaScript has is +[function scope](#function.scopes), all functions, by default, act as closures. + +### Emulating private variables + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Here, `Counter` returns **two** closures: the function `increment` as well as +the function `get`. Both of these functions keep a **reference** to the scope of +`Counter` and, therefore, always keep access to the `count` variable that was +defined in that scope. + +### Why Private Variables Work + +Since it is not possible to reference or assign scopes in JavaScript, there is +**no** way of accessing the variable `count` from the outside. The only way to +interact with it is via the two closures. + + var foo = new Counter(4); + foo.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 loop's index variable. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +The above will **not** output the numbers `0` through `9`, but will simply print +the number `10` ten times. + +The *anonymous* function keeps a **reference** to `i`. At the time +`console.log` gets called, the `for loop` has already finished, and the value of +`i` 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, which is to return a function +from the anonymous wrapper that will then have the same behavior as the code +above. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + diff --git a/doc/zh(Trandition)/function/constructors.md b/doc/zh(Trandition)/function/constructors.md new file mode 100644 index 00000000..bf9771dc --- /dev/null +++ b/doc/zh(Trandition)/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 by 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 same thing, a newly create object that +has a property called `method`, which is a +[Closure](#function.closures). + +It should also be noted 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 + +It is often recommended to **not** use `new` because forgetting its use may +lead to bugs. + +In order to create a new object, one should rather use a factory and construct a +new object inside of that factory. + + function 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 + is contrary to the spirit of the language. + +### In Conclusion + +While omitting the `new` keyword might lead to bugs, it is certainly **not** a +reason to drop the use of prototypes altogether. In the end it comes down to +which solution is better suited for the needs of the application. It is +especially important to choose a specific style of object creation and use it +**consistently**. + diff --git a/doc/zh(Trandition)/function/general.md b/doc/zh(Trandition)/function/general.md new file mode 100644 index 00000000..b2788e46 --- /dev/null +++ b/doc/zh(Trandition)/function/general.md @@ -0,0 +1,48 @@ +## Function Declarations and Expressions + +Functions in JavaScript are first class objects. That means they can be +passed around like any other value. One common use of this feature is to pass +an *anonymous function* as a callback to another, possibly an asynchronous function. + +### The `function` Declaration + + function foo() {} + +The above function gets [hoisted](#function.scopes) before the execution of the +program starts; thus, it is available *everywhere* in the scope it was +*defined*, even if called before the actual definition in the source. + + foo(); // Works because foo was created before this code runs + function foo() {} + +### The `function` Expression + + var foo = function() {}; + +This example assigns the unnamed and *anonymous* function to the variable `foo`. + + foo; // 'undefined' + foo(); // this raises a TypeError + var foo = function() {}; + +Due to the fact that `var` is a declaration that hoists the variable name `foo` +before the actual execution of the code starts, `foo` is already 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/zh(Trandition)/function/scopes.md b/doc/zh(Trandition)/function/scopes.md new file mode 100644 index 00000000..879e1e8a --- /dev/null +++ b/doc/zh(Trandition)/function/scopes.md @@ -0,0 +1,233 @@ +## Scopes and Namespaces + +Although JavaScript deals fine with the syntax of two matching curly +braces for blocks, it does **not** support block scope; hence, all that is left +in the language is *function scope*. + + function test() { // a scope + for(var i = 0; i < 10; i++) { // not a scope + // count + } + console.log(i); // 10 + } + +> **Note:** When not used in an assignment, return statement or as a function +> argument, the `{...}` notation will get interpreted as a block statement and +> **not** as an object literal. This, in conjunction with +> [automatic insertion of semicolons](#core.semicolon), can lead to subtle errors. + +There are also no distinct namespaces in JavaScript, which means that everything +gets defined in one *globally shared* namespace. + +Each time a variable is referenced, JavaScript will traverse upwards through all +the scopes until it finds it. In the case that it reaches the global scope and +still has not found the requested name, it will raise a `ReferenceError`. + +### The Bane of Global Variables + + // script A + foo = '42'; + + // script B + var foo = '42' + +The above two scripts do **not** have the same effect. Script A defines a +variable called `foo` in the *global* scope, and script B defines a `foo` in the +*current* scope. + +Again, that is **not** at all the *same effect*: not using `var` can have major +implications. + + // global scope + var foo = 42; + function test() { + // local scope + foo = 21; + } + test(); + foo; // 21 + +Leaving out the `var` statement inside the function `test` will override the +value of `foo`. While this might not seem like a big deal at first, having +thousands of lines of JavaScript and not using `var` will introduce horrible, +hard-to-track-down bugs. + + // global scope + var items = [/* some list */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // scope of subLoop + for(i = 0; i < 10; i++) { // missing var statement + // do amazing stuff! + } + } + +The outer loop will terminate after the first call to `subLoop`, since `subLoop` +overwrites the global value of `i`. Using a `var` for the second `for` loop would +have easily avoided this error. The `var` statement should **never** be left out +unless the *desired effect* is to affect the outer scope. + +### Local Variables + +The only source for local variables in JavaScript are +[function](#function.general) parameters and variables declared via the +`var` statement. + + // global scope + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // local scope of the function test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +While `foo` and `i` are local variables inside the scope of the function `test`, +the assignment of `bar` will override the global variable with the same name. + +### Hoisting + +JavaScript **hoists** declarations. This means that both `var` statements and +`function` declarations will be moved to the top of their enclosing scope. + + bar(); + var bar = function() {}; + var someValue = 42; + + test(); + function test(data) { + if (false) { + goo = 1; + + } else { + var goo = 2; + } + for(var i = 0; i < 100; i++) { + var e = data[i]; + } + } + +The above code gets transformed before execution starts. JavaScript moves +the `var` statements, as well as `function` declarations, to the top of the +nearest surrounding scope. + + // var statements got moved here + var bar, someValue; // default to 'undefined' + + // the function declaration got moved up too + function test(data) { + var goo, i, e; // missing block scope moves these here + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // fails with a TypeError since bar is still 'undefined' + someValue = 42; // assignments are not affected by hoisting + bar = function() {}; + + test(); + +Missing block scoping will not only move `var` statements out of loops and +their bodies, it will also make the results of certain `if` constructs +non-intuitive. + +In the original code, although the `if` statement seemed to modify the *global +variable* `goo`, it actually modifies the *local variable* - after hoisting +has been applied. + +Without knowledge of *hoisting*, one might suspect the code below would raise a +`ReferenceError`. + + // check whether SomeImportantThing has been initialized + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +But of course, this works due to the fact that the `var` statement is being +moved to the top of the *global scope*. + + var SomeImportantThing; + + // other code might initialize SomeImportantThing here, or not + + // make sure it's there + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Name Resolution Order + +All scopes in JavaScript, including the *global scope*, have the special name +[`this`](#function.this), defined in them, which refers to the *current object*. + +Function scopes also have the name [`arguments`](#function.arguments), defined in +them, which contains the arguments that were passed to the function. + +For example, when trying to access a variable named `foo` inside the scope of a +function, JavaScript will look up the name in the following order: + + 1. In case there is a `var foo` statement in the current scope, use that. + 2. If one of the function parameters is named `foo`, use that. + 3. If the function itself is called `foo`, use that. + 4. Go to the next outer scope, and start with **#1** again. + +> **Note:** Having a parameter called `arguments` will **prevent** the creation +> of the default `arguments` object. + +### Namespaces + +A common problem associated with having only one global namespace is the +likelihood of running into problems where variable names clash. In JavaScript, +this problem can easily be avoided with the help of *anonymous wrappers*. + + (function() { + // a self contained "namespace" + + window.foo = function() { + // an exposed closure + }; + + })(); // execute the function immediately + + +Unnamed functions are considered [expressions](#function.general); so in order to +being callable, they must first be evaluated. + + ( // evaluate the function inside the parentheses + function() {} + ) // and return the function object + () // call the result of the evaluation + +There are other ways to evaluate and directly call the function expression +which, while different in syntax, behave the same way. + + // A few other styles for directly invoking the + !function(){}() + +function(){}() + (function(){}()); + // and so on... + +### In Conclusion + +It is recommended to always use an *anonymous wrapper* to encapsulate code in +its own namespace. This does not only protect code against name clashes, but it +also allows for better modularization of programs. + +Additionally, the use of global variables is considered **bad practice**. **Any** +use of them indicates badly written code that is prone to errors and hard to maintain. + diff --git a/doc/zh(Trandition)/function/this.md b/doc/zh(Trandition)/function/this.md new file mode 100644 index 00000000..48070d2e --- /dev/null +++ b/doc/zh(Trandition)/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. There are exactly **five** different +ways in which the value of `this` can be bound in the language. + +### The Global Scope + + this; + +When using `this` in global scope, it will simply refer to the *global* object. + + +### Calling a Function + + foo(); + +Here, `this` will again refer to the *global* object. + +> **ES5 Note:** In strict mode, the global case **no longer** exists. +> `this` will instead have the value of `undefined` in that case. + +### Calling a Method + + test.foo(); + +In this example, `this` will refer to `test`. + +### Calling a Constructor + + new foo(); + +A function call that is preceded by the `new` keyword acts as +a [constructor](#function.constructors). Inside the function, `this` will refer +to a *newly created* `Object`. + +### Explicit Setting of `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // array will expand to the below + foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3 + +When using the `call` or `apply` methods of `Function.prototype`, the value of +`this` inside the called function gets **explicitly set** to the first argument +of the corresponding function call. + +As a result, in the above example the *method case* does **not** apply, and `this` +inside of `foo` will be set to `bar`. + +> **Note:** `this` **cannot** be used to refer to the object inside of an `Object` +> literal. So `var obj = {me: this}` will **not** result in `me` referring to +> `obj`, since `this` only gets bound by one of the five listed cases. + +### Common Pitfalls + +While most of these cases make sense, the first can be considered another +mis-design of the language because it **never** has any practical use. + + Foo.method = function() { + function test() { + // this is set to the global object + } + test(); + } + +A common misconception is that `this` inside of `test` refers to `Foo`; while in +fact, it **does not**. + +In order to gain access to `Foo` from within `test`, it is necessary to create a +local variable inside of `method` that 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, which is +**assigning** a method to a variable. + + var test = someObject.methodTest; + test(); + +Due to the first case, `test` now acts like a plain function call; therefore, +`this` inside it will no longer refer to `someObject`. + +While the late binding of `this` might seem like a bad idea at first, in +fact, it is what makes [prototypal inheritance](#object.prototype) work. + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +When `method` gets called on an instance of `Bar`, `this` will now refer to that +very instance. + + diff --git a/doc/zh(Trandition)/index.json b/doc/zh(Trandition)/index.json new file mode 100644 index 00000000..bffcbf2c --- /dev/null +++ b/doc/zh(Trandition)/index.json @@ -0,0 +1,69 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden in English", + "description": "A Guide to JavaScript's Quirks and Flaws.", + "sections": [ + { + "title": "Intro", + "dir": "intro", + "articles": [] + }, + { + "title": "Objects", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "Functions", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Arrays", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "Types", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "Core", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon", + "delete" + ] + }, + { + "title": "Other", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/doc/zh(Trandition)/intro/index.md b/doc/zh(Trandition)/intro/index.md new file mode 100644 index 00000000..134bca80 --- /dev/null +++ b/doc/zh(Trandition)/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 and subtle bugs, as well as performance issues and bad +practices, that non-expert JavaScript programmers may encounter on their +endeavours into the depths of the language. + +JavaScript Garden does **not** aim to teach you JavaScript. Former knowledge +of the language is strongly recommended in order to understand the topics covered +in this guide. In order to learn the basics of the language, please head over to +the excellent [guide][1] on the Mozilla Developer Network. + +## The Authors + +This guide is the work of two lovely [Stack Overflow][2] users, [Ivo Wetzel][3] +(Writing) and [Zhang Yi Jiang][4] (Design). + +## 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/zh(Trandition)/object/forinloop.md b/doc/zh(Trandition)/object/forinloop.md new file mode 100644 index 00000000..3d366f8c --- /dev/null +++ b/doc/zh(Trandition)/object/forinloop.md @@ -0,0 +1,51 @@ +## The `for in` Loop + +Just like the `in` operator, the `for in` loop traverses the prototype +chain when iterating over the properties of an object. + +> **Note:** The `for in` loop will **not** iterate over any properties that +> have their `enumerable` attribute set to `false`; for example, the `length` +> property of an array. + + // Poisoning Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // prints both bar and moo + } + +Since it is not possible to change the behavior of the `for in` loop itself, it +is necessary to filter out the unwanted properties inside the loop body; +this is done using the [`hasOwnProperty`](#object.hasownproperty) method of +`Object.prototype`. + +> **Note:** Since `for in` always traverses the complete prototype chain, it +> will get slower with each additional layer of inheritance added to an object. + +### Using `hasOwnProperty` for Filtering + + // still the foo from above + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +This version is the only correct one to use. 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 that extends `Object.prototype` 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`. Assumptions should never +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(Trandition)/object/general.md b/doc/zh(Trandition)/object/general.md new file mode 100644 index 00000000..9300b18e --- /dev/null +++ b/doc/zh(Trandition)/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 that can be used to make number literals act +as objects too. + + 2..toString(); // the second point is correctly recognized + 2 .toString(); // note the space left to the dot + (2).toString(); // 2 is evaluated first + +### Objects as a Data Type + +Objects in JavaScript can also be used as [*Hashmaps*][1]; they mainly consist +of named properties mapping to values. + +Using an object literal - `{}` notation - it is possible to create a +plain object. This new object [inherits](#object.prototype) from `Object.prototype` and +does not have [own properties](#object.hasownproperty) defined. + + var foo = {}; // a new empty object + + // a new object with a 'test' property 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 + +The notations work almost identically, with the only difference being that the +square bracket notation allows for dynamic setting of properties and +the use of property names that would otherwise lead to a syntax error. + +### Deleting Properties + +The only way to remove a property from an object is to use the `delete` +operator; setting the property to `undefined` or `null` only removes the +*value* associated with the property, but not the *key*. + + var obj = { + 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, so me too' // raises SyntaxError + }; + +Object properties can be both notated as plain characters and as strings. Due to +another mis-design in JavaScript's parser, the above will throw +a `SyntaxError` prior to ECMAScript 5. + +This error arises from the fact that `delete` is a *keyword*; therefore, it must be +notated as a *string literal* to ensure that it will be correctly interpreted by +older JavaScript engines. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/doc/zh(Trandition)/object/hasownproperty.md b/doc/zh(Trandition)/object/hasownproperty.md new file mode 100644 index 00000000..0033b4ba --- /dev/null +++ b/doc/zh(Trandition)/object/hasownproperty.md @@ -0,0 +1,57 @@ +## `hasOwnProperty` + +To check whether an object has a property defined on *itself* and not somewhere +on its [prototype chain](#object.prototype), it is necessary to use the +`hasOwnProperty` method which all objects inherit from `Object.prototype`. + +> **Note:** It is **not** enough to check whether a property is `undefined`. The +> property might very well exist, but its value just happens to be set to +> `undefined`. + +`hasOwnProperty` is the only thing in JavaScript which deals with properties and +does **not** traverse the prototype chain. + + // Poisoning Object.prototype + Object.prototype.bar = 1; + var foo = {goo: undefined}; + + foo.bar; // 1 + 'bar' in foo; // true + + foo.hasOwnProperty('bar'); // false + foo.hasOwnProperty('goo'); // true + +Only `hasOwnProperty` will give the correct and expected result; 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` to get correct results. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // always returns false + + // Use another Object's hasOwnProperty and call it with 'this' set to foo + ({}).hasOwnProperty.call(foo, 'bar'); // true + + // It's also possible to use the hasOwnProperty property from the Object property for this purpose + Object.prototype.hasOwnProperty.call(obj, 'bar'); // true + + +### In Conclusion + +Using `hasOwnProperty` is the **only** reliable method to check for the +existence of a property on an object. It is recommended that `hasOwnProperty` +is used in **every** [`for in` loop](#object.forinloop) to avoid errors from +extended native [prototypes](#object.prototype). + diff --git a/doc/zh(Trandition)/object/prototype.md b/doc/zh(Trandition)/object/prototype.md new file mode 100644 index 00000000..abb67c58 --- /dev/null +++ b/doc/zh(Trandition)/object/prototype.md @@ -0,0 +1,118 @@ +## The Prototype + +JavaScript does not feature a classical inheritance model; instead, it uses a +*prototypal* one. + +While this is often considered to be one of JavaScript's weaknesses, the +prototypal inheritance model is in fact more powerful than the classic model. +It is, for example, fairly trivial to build a classic model on top of a +prototypal model, while the other way around is a far more difficult task. + +JavaScript is the only widely used language that features prototypal +inheritance, so it can take time to adjust to the differences between the two +models. + +The first major difference is that inheritance in JavaScript uses *prototype +chains*. + +> **Note:** Simply using `Bar.prototype = Foo.prototype` will result in both objects +> sharing the **same** prototype. Therefore, changes to either object's prototype +> will affect the prototype of the other as well, which in most cases is not the +> desired effect. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Set Bar's prototype to a new instance of Foo + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Make sure to list Bar as the actual constructor + Bar.prototype.constructor = Bar; + + var test = new Bar() // create a new bar instance + + // The resulting prototype chain + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World' } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +In the code above, the object `test` will inherit from both `Bar.prototype` and +`Foo.prototype`; hence, it will have access to the function `method` that was +defined on `Foo`. It will also have access to the property `value` of the +**one** `Foo` instance that is its prototype. It is important to note that `new +Bar()` does **not** create a new `Foo` instance, but reuses the one assigned to +its prototype; thus, all `Bar` instances will share the **same** `value` property. + +> **Note:** Do **not** use `Bar.prototype = Foo`, since it will not point to +> the prototype of `Foo` but rather to the function object `Foo`. So the +> prototype chain will go over `Function.prototype` and not `Foo.prototype`; +> therefore, `method` will not be on the prototype chain. + +### Property Lookup + +When accessing the properties of an object, JavaScript will traverse the +prototype chain **upwards** until it finds a property with the requested name. + +If it reaches the top of the chain - namely `Object.prototype` - and still +hasn't found the specified property, it will return the value +[undefined](#core.undefined) instead. + +### The Prototype Property + +While the prototype property is used by the language to build the prototype +chains, it is still possible to assign **any** given value to it. However, +primitives will simply get ignored when assigned as a prototype. + + function Foo() {} + Foo.prototype = 1; // no effect + +Assigning objects, as shown in the example above, will work, and allows for dynamic +creation of prototype chains. + +### Performance + +The lookup time for properties that are high up on the prototype chain can have +a negative impact on performance, and this may be significant in code where +performance is critical. Additionally, trying to access non-existent properties +will always traverse the full prototype chain. + +Also, when [iterating](#object.forinloop) over the properties of an object +**every** property that is on the prototype chain will be enumerated. + +### Extension of Native Prototypes + +One mis-feature that is often used is to extend `Object.prototype` or one of the +other built in prototypes. + +This technique is called [monkey patching][1] and breaks *encapsulation*. While +used by popular frameworks such as [Prototype][2], there is still no good +reason for cluttering built-in types with additional *non-standard* functionality. + +The **only** good reason for extending a built-in prototype is to backport +the features of newer JavaScript engines; for example, +[`Array.forEach`][3]. + +### In Conclusion + +It is **essential** to understand the prototypal inheritance model before +writing complex code that makes use of it. Also, be aware of the length of the +prototype chains in your code and break them up if necessary to avoid possible +performance problems. Further, the native prototypes should **never** be +extended unless it is for the sake of compatibility with newer JavaScript +features. + +[1]: http://en.wikipedia.org/wiki/Monkey_patch +[2]: http://prototypejs.org/ +[3]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach + diff --git a/doc/zh(Trandition)/other/timeouts.md b/doc/zh(Trandition)/other/timeouts.md new file mode 100644 index 00000000..578fad5f --- /dev/null +++ b/doc/zh(Trandition)/other/timeouts.md @@ -0,0 +1,167 @@ +### `setTimeout` and `setInterval` + +Since JavaScript is asynchronous, it is possible to schedule the execution of a +function using the `setTimeout` and `setInterval` functions. + +> **Note:** Timeouts are **not** part of the ECMAScript Standard. They are +> implemented as part of the [DOM][1]. + + function foo() {} + var id = setTimeout(foo, 1000); // returns a Number > 0 + +When `setTimeout` is called, it returns the ID of the timeout and schedule +`foo` to run **approximately** one thousand milliseconds in the future. +`foo` will then be executed **once**. + +Depending on the timer resolution of the JavaScript engine running the code, as +well as the fact that JavaScript is single threaded and other code that gets +executed might block the thread, it is by **no means** a safe bet that one will +get the exact delay specified in the `setTimeout` call. + +The function that was passed as the first parameter will get called by the +*global object*, which means that [`this`](#function.this) inside the called function +refers to the global object. + + function Foo() { + this.value = 42; + this.method = function() { + // this refers to the global object + console.log(this.value); // will log undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **Note:** As `setTimeout` takes a **function object** as its first parameter, an +> common mistake is to use `setTimeout(foo(), 1000)`, which will use the +> **return value** of the call `foo` and **not** `foo`. This is, most of the time, +> a silent error, since when the function returns `undefined` `setTimeout` will +> **not** raise any error. + +### Stacking Calls with `setInterval` + +While `setTimeout` only runs the function once, `setInterval` - as the name +suggests - will execute the function **every** `X` milliseconds, but its use is +discouraged. + +When code that is being executed blocks the timeout call, `setInterval` will +still issue more calls to the specified function. This can, especially with small +intervals, result in function calls stacking up. + + function foo(){ + // something that blocks for 1 second + } + setInterval(foo, 1000); + +In the above code, `foo` will get called once and will then block for one second. + +While `foo` blocks the code, `setInterval` will still schedule further calls to +it. Now, when `foo` has finished, there will already be **ten** further calls to +it waiting for execution. + +### Dealing with Possible Blocking Code + +The easiest solution, as well as most controllable solution, is to use `setTimeout` within +the function itself. + + function foo(){ + // something that blocks for 1 second + setTimeout(foo, 1000); + } + foo(); + +Not only does this encapsulate the `setTimeout` call, but it also prevents the +stacking of calls and gives additional control. `foo` itself can now decide +whether it wants to run again or not. + +### Manually Clearing Timeouts + +Clearing timeouts and intervals works by passing the respective ID to +`clearTimeout` or `clearInterval`, depending on which `set` function was used +in the first place. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Clearing All Timeouts + +As there is no built-in method for clearing all timeouts and/or intervals, +it is necessary to use brute force in order to achieve this functionality. + + // clear "all" timeouts + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +But there might still be timeouts that are unaffected by this arbitrary number. +Another way of doing this is to consider that the ID given to a timeout is +incremented by one every time you call `setTimeout`. + + // clear "all" timeouts + var biggestTimeoutId = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= biggestTimeoutId; i++) { + clearTimeout(i); + } + +Even though this works on all major browsers today, it isn't specified that +the IDs should be ordered that way and it may change. Therefore, it is instead +recommended to keep track of all the timeout IDs, so they can be cleared +specifically. + +### Hidden Use of `eval` + +`setTimeout` and `setInterval` can also take a string as their first parameter. +This feature should **never** be used because it internally makes use of `eval`. + +> **Note:** 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 uses +> the `Function` constructor in place of `eval`. + + function foo() { + // will get called + } + + function bar() { + function foo() { + // never gets called + } + setTimeout('foo()', 1000); + } + bar(); + +Since `eval` is not getting called [directly](#core.eval) in this case, the string +passed to `setTimeout` will be executed in the *global scope*; thus, it will +not use the local variable `foo` from the scope of `bar`. + +It is further recommended to **not** use a string to pass arguments to the +function that will get called by either of the timeout functions. + + function foo(a, b, c) {} + + // NEVER use this + setTimeout('foo(1, 2, 3)', 1000) + + // Instead use an anonymous function + setTimeout(function() { + foo(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 + +A string should **never** be used as the parameter of `setTimeout` or +`setInterval`. It is a clear sign of **really** bad code, when arguments need +to be supplied to the function that gets called. An *anonymous function* should +be passed that then takes care of the actual call. + +Furthermore, the use of `setInterval` should be avoided because its scheduler is not +blocked by executing JavaScript. + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" + diff --git a/doc/zh(Trandition)/types/casting.md b/doc/zh(Trandition)/types/casting.md new file mode 100644 index 00000000..0dcbc92b --- /dev/null +++ b/doc/zh(Trandition)/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. + +To avoid the issues above, use of the [strict equal operator](#types.equality) +is **highly** recommended. Although this avoids a lot of common pitfalls, there +are still many further issues that arise from JavaScript's weak typing system. + +### Constructors of Built-In Types + +The constructors of the built in types like `Number` and `String` behave +differently when being used with the `new` keyword and without it. + + new Number(10) === 10; // False, Object and Number + Number(10) === 10; // True, Number and Number + new Number(10) + 0 === 10; // True, due to implicit conversion + +Using a built-in type like `Number` as a constructor will create a new `Number` +object, but leaving out the `new` keyword will make the `Number` function behave +like a converter. + +In addition, passing literals or non-object values will result in even more +type coercion. + +The best option is to cast to one of the three possible types **explicitly**. + +### Casting to a String + + '' + 10 === '10'; // true + +By prepending an empty string, a value can easily be cast to a string. + +### Casting to a Number + + +'10' === 10; // true + +Using the **unary** plus operator, it is possible to cast to a number. + +### Casting to a Boolean + +By using the **not** operator twice, a value can be converted a boolean. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + + diff --git a/doc/zh(Trandition)/types/equality.md b/doc/zh(Trandition)/types/equality.md new file mode 100644 index 00000000..e47752a4 --- /dev/null +++ b/doc/zh(Trandition)/types/equality.md @@ -0,0 +1,71 @@ +## Equality and Comparisons + +JavaScript has two different ways of comparing the values of objects for equality. + +### The Equality Operator + +The equality operator consists of two equal signs: `==` + +JavaScript features *weak typing*. This means that the equality operator +**coerces** types in order to compare them. + + "" == "0" // false + 0 == "" // true + 0 == "0" // true + false == "false" // false + false == "0" // true + false == undefined // false + false == null // false + null == undefined // true + " \t\r\n" == 0 // true + +The above table shows the results of the type coercion, and it is the main reason +why the use of `==` is widely regarded as bad practice. It introduces +hard-to-track-down bugs due to its complicated conversion rules. + +Additionally, there is also a performance impact when type coercion is in play; +for example, a string has to be converted to a number before it can be compared +to another number. + +### The Strict Equality Operator + +The strict equality operator consists of **three** equal signs: `===`. + +It works like the normal equality operator, except that strict equality +operator does **not** perform type coercion between its operands. + + "" === "0" // false + 0 === "" // false + 0 === "0" // false + false === "false" // false + false === "0" // false + false === undefined // false + false === null // false + null === undefined // false + " \t\r\n" === 0 // false + +The above results are a lot clearer and allow for early breakage of code. This +hardens code to a certain degree and also gives performance improvements in case +the operands are of different types. + +### Comparing Objects + +While both `==` and `===` are called **equality** operators, they behave +differently when at least one of their operands is an `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Here, both operators compare for **identity** and **not** equality; that is, they +will compare for the same **instance** of the object, much like `is` in Python +and pointer comparison in C. + +### In Conclusion + +It is highly recommended to only use the **strict equality** operator. In cases +where types need to be coerced, it should be done [explicitly](#types.casting) +and not left to the language's complicated coercion rules. + diff --git a/doc/zh(Trandition)/types/instanceof.md b/doc/zh(Trandition)/types/instanceof.md new file mode 100644 index 00000000..2fe41064 --- /dev/null +++ b/doc/zh(Trandition)/types/instanceof.md @@ -0,0 +1,38 @@ +## The `instanceof` Operator + +The `instanceof` operator compares the constructors of its two operands. It is +only useful when comparing custom made objects. Used on built-in types, it is +nearly as useless as the [typeof operator](#types.typeof). + +### Comparing Custom Objects + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // This just sets Bar.prototype to the function object Foo, + // but not to an actual instance of Foo + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### Using `instanceof` with Native Types + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +One important thing to note here is that `instanceof` does not work on objects +that originate from different JavaScript contexts (e.g. different documents +in a web browser), since their constructors will not be the exact same object. + +### In Conclusion + +The `instanceof` operator should **only** be used when dealing with custom made +objects that originate from the same JavaScript context. Just like the +[`typeof`](#types.typeof) operator, every other use of it should be **avoided**. + diff --git a/doc/zh(Trandition)/types/typeof.md b/doc/zh(Trandition)/types/typeof.md new file mode 100644 index 00000000..637ea2bc --- /dev/null +++ b/doc/zh(Trandition)/types/typeof.md @@ -0,0 +1,86 @@ +## The `typeof` Operator + +The `typeof` operator (together with +[`instanceof`](#types.instanceof)) is probably the biggest +design flaw of JavaScript, as it is almost **completely broken**. + +Although `instanceof` still has limited uses, `typeof` really has only one +practical use case, which does **not** happen to be checking the type of an +object. + +> **Note:** While `typeof` can also be called with a function like syntax, i.e. +> `typeof(obj)`, this is not a function call. The parentheses behave as normal +> and the return value will be used as the operand of the `typeof` operator. +> There is **no** `typeof` function. + +### The JavaScript Type Table + + Value Class Type + ------------------------------------- + "foo" String string + new String("foo") String object + 1.2 Number number + new Number(1.2) Number object + true Boolean boolean + new Boolean(true) Boolean object + new Date() Date object + new Error() Error object + [1,2,3] Array object + new Array(1, 2, 3) Array object + new Function("") Function function + /abc/g RegExp object (function in Nitro/V8) + new RegExp("meow") RegExp object (function in Nitro/V8) + {} Object object + new Object() Object object + +In the above table, *Type* refers to the value that the `typeof` operator returns. +As can be clearly seen, this value is anything but consistent. + +The *Class* refers to the value of the internal `[[Class]]` property of an object. + +> **From the Specification:** The value of `[[Class]]` can be one of the +> following strings. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +In order to retrieve the value of `[[Class]]`, one has to make use of the +`toString` method of `Object.prototype`. + +### The Class of an Object + +The specification gives exactly one way of accessing the `[[Class]]` value, +with the use of `Object.prototype.toString`. + + function is(type, obj) { + var clas = Object.prototype.toString.call(obj).slice(8, -1); + return obj !== undefined && obj !== null && clas === type; + } + + is('String', 'test'); // true + is('String', new String('test')); // true + +In the above example, `Object.prototype.toString` gets called with the value of +[this](#function.this) being set to the object whose `[[Class]]` value should be +retrieved. + +> **ES5 Note:** For convenience the return value of `Object.prototype.toString` +> for both `null` and `undefined` was **changed** from `Object` to `Null` and +> `Undefined` in ECMAScript 5. + +### Testing for Undefined Variables + + typeof foo !== 'undefined' + +The above will check whether `foo` was actually declared or not; just +referencing it would result in a `ReferenceError`. This is the only thing +`typeof` is actually useful for. + +### In Conclusion + +In order to check the type of an object, it is highly recommended to use +`Object.prototype.toString` because this is the only reliable way of doing so. +As shown in the above type table, some return values of `typeof` are not defined +in the specification; thus, they can differ between implementations. + +Unless checking whether a variable is defined, `typeof` should be avoided. + + From 151456f3bed2db1a99d7e197f3cecdb946acd519 Mon Sep 17 00:00:00 2001 From: howardchi Date: Wed, 13 Mar 2013 22:09:32 +0800 Subject: [PATCH 008/216] finished the part in Array & some in function section --- .gitignore | 1 + doc/zh(Trandition)/core/undefined.md | 70 - doc/zh(Trandition)/function/arguments.md | 118 - .../array/constructor.md | 0 .../array/general.md | 0 doc/{zh(Trandition) => zh-TW}/core/delete.md | 0 doc/{zh(Trandition) => zh-TW}/core/eval.md | 0 .../core/semicolon.md | 0 doc/zh-TW/core/undefined.md | 55 + doc/zh-TW/function/arguments.md | 103 + .../function/closures.md | 0 .../function/constructors.md | 0 .../function/general.md | 0 .../function/scopes.md | 0 .../function/this.md | 0 doc/{zh(Trandition) => zh-TW}/index.json | 0 doc/{zh(Trandition) => zh-TW}/intro/index.md | 0 .../object/forinloop.md | 0 .../object/general.md | 0 .../object/hasownproperty.md | 0 .../object/prototype.md | 0 .../other/timeouts.md | 0 .../types/casting.md | 0 .../types/equality.md | 0 .../types/instanceof.md | 0 doc/{zh(Trandition) => zh-TW}/types/typeof.md | 0 site/zh-TW/index.html | 1913 +++++++++++++++++ 27 files changed, 2072 insertions(+), 188 deletions(-) delete mode 100644 doc/zh(Trandition)/core/undefined.md delete mode 100644 doc/zh(Trandition)/function/arguments.md rename doc/{zh(Trandition) => zh-TW}/array/constructor.md (100%) rename doc/{zh(Trandition) => zh-TW}/array/general.md (100%) rename doc/{zh(Trandition) => zh-TW}/core/delete.md (100%) rename doc/{zh(Trandition) => zh-TW}/core/eval.md (100%) rename doc/{zh(Trandition) => zh-TW}/core/semicolon.md (100%) create mode 100644 doc/zh-TW/core/undefined.md create mode 100644 doc/zh-TW/function/arguments.md rename doc/{zh(Trandition) => zh-TW}/function/closures.md (100%) rename doc/{zh(Trandition) => zh-TW}/function/constructors.md (100%) rename doc/{zh(Trandition) => zh-TW}/function/general.md (100%) rename doc/{zh(Trandition) => zh-TW}/function/scopes.md (100%) rename doc/{zh(Trandition) => zh-TW}/function/this.md (100%) rename doc/{zh(Trandition) => zh-TW}/index.json (100%) rename doc/{zh(Trandition) => zh-TW}/intro/index.md (100%) rename doc/{zh(Trandition) => zh-TW}/object/forinloop.md (100%) rename doc/{zh(Trandition) => zh-TW}/object/general.md (100%) rename doc/{zh(Trandition) => zh-TW}/object/hasownproperty.md (100%) rename doc/{zh(Trandition) => zh-TW}/object/prototype.md (100%) rename doc/{zh(Trandition) => zh-TW}/other/timeouts.md (100%) rename doc/{zh(Trandition) => zh-TW}/types/casting.md (100%) rename doc/{zh(Trandition) => zh-TW}/types/equality.md (100%) rename doc/{zh(Trandition) => zh-TW}/types/instanceof.md (100%) rename doc/{zh(Trandition) => zh-TW}/types/typeof.md (100%) create mode 100644 site/zh-TW/index.html diff --git a/.gitignore b/.gitignore index 237404f0..63b10902 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ /site/es *.md~ *.src.md +*.DS_store diff --git a/doc/zh(Trandition)/core/undefined.md b/doc/zh(Trandition)/core/undefined.md deleted file mode 100644 index 51101912..00000000 --- a/doc/zh(Trandition)/core/undefined.md +++ /dev/null @@ -1,70 +0,0 @@ -## `undefined` 和 `null` - -JavaScript 中有兩個表示空值的方式, `null` 和 `undefined` , `undefined`式比較常用的一種。 - -### `undefined` 的值 - -`undefined` 是一個值為 `undefined` 的類型。 - -語言中也定義了一個全域變數,它的值為 `undefined`,這個變數的被稱作 `undefined` 。 -這個變數 **不是** 一個常數,也不是一個關鍵字。這表示它的值可以被輕易的覆蓋。 - -> **ES5 提示: ** `undefined` 在 ECMAScript 5 裡 **不再是** *可寫* 的 -> 但是它的名稱還是可以被隱藏,比如說定義一個函數為 `undefined`。 - -這裡有一些例子會回傳 `undefined` 的值: - - - Accessing the (unmodified) global variable `undefined`. - - Accessing a declared *but not* yet initialized variable. - - Implicit returns of functions due to missing `return` statements. - - `return` statements that do not explicitly return anything. - - Lookups of non-existent properties. - - Function parameters that do not have any explicit value passed. - - Anything that has been set to the value of `undefined`. - - Any expression in the form of `void(expression)` - -### Handling Changes to the Value of `undefined` - -Since the global variable `undefined` only holds a copy of the actual *value* of -`undefined`, assigning a new value to it does **not** change the value of the -*type* `undefined`. - -Still, in order to compare something against the value of `undefined`, it is -necessary to retrieve the value of `undefined` first. - -To protect code against a possible overwritten `undefined` variable, a common -technique used is to add an additional parameter to an [anonymous -wrapper](#function.scopes) that gets no argument passed to it. - - var undefined = 123; - (function(something, foo, undefined) { - // undefined in the local scope does - // now again refer to the value `undefined` - - })('Hello World', 42); - -Another way to achieve the same effect would be to use a declaration inside the -wrapper. - - var undefined = 123; - (function(something, foo) { - var undefined; - ... - - })('Hello World', 42); - -The only difference here is that this version results in 4 more bytes being -used in case it is minified, and there is no other `var` statement inside the -anonymous wrapper. - -### Uses of `null` - -While `undefined` in the context of the JavaScript language is mostly used in -the sense of a traditional *null*, the actual `null` (both a literal and a type) -is more or less just another data type. - -It is used in some JavaScript internals (like declaring the end of the -prototype chain by setting `Foo.prototype = null`), but in almost all cases, it -can be replaced by `undefined`. - - diff --git a/doc/zh(Trandition)/function/arguments.md b/doc/zh(Trandition)/function/arguments.md deleted file mode 100644 index 2394177d..00000000 --- a/doc/zh(Trandition)/function/arguments.md +++ /dev/null @@ -1,118 +0,0 @@ -## The `arguments` Object - -Every function scope in JavaScript can access the special variable `arguments`. -This variable holds a list of all the arguments that were passed to the function. - -> **Note:** In case `arguments` has already been defined inside the function's -> scope either via a `var` statement or being the name of a formal parameter, -> the `arguments` object will not be created. - -The `arguments` object is **not** an `Array`. While it has some of the -semantics of an array - namely the `length` property - it does not inherit from -`Array.prototype` and is in fact an `Object`. - -Due to this, it is **not** possible to use standard array methods like `push`, -`pop` or `slice` on `arguments`. While iteration with a plain `for` loop works -just fine, it is necessary to convert it to a real `Array` in order to use the -standard `Array` methods on it. - -### Converting to an Array - -The code below will return a new `Array` containing all the elements of the -`arguments` object. - - Array.prototype.slice.call(arguments); - -Because this conversion is **slow**, it is **not recommended** to use it in -performance-critical sections of code. - -### Passing Arguments - -The following is the recommended way of passing arguments from one function to -another. - - function foo() { - bar.apply(null, arguments); - } - function bar(a, b, c) { - // do stuff here - } - -Another trick is to use both `call` and `apply` together to 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 only time the `arguments` object is not created is where it is declared as -a name inside of a function or one of its formal parameters. It does not matter -whether it is used or not. - -Both *getters* and *setters* are **always** created; thus, using it has nearly -no performance impact at all, especially not in real world code where there is -more than a simple access to the `arguments` object's properties. - -> **ES5 Note:** These *getters* and *setters* are not created in strict mode. - -However, there is one case which will drastically reduce the performance in -modern JavaScript engines. That case is the use of `arguments.callee`. - - function foo() { - arguments.callee; // do something with this function object - arguments.callee.caller; // and the calling function object - } - - function bigLoop() { - for(var i = 0; i < 100000; i++) { - foo(); // Would normally be inlined... - } - } - -In the above code, `foo` can no longer be a subject to [inlining][1] since it -needs to know about both itself and its caller. This not only defeats possible -performance gains that would arise from inlining, but it also breaks encapsulation -because the function may now be dependent on a specific calling context. - -Making use of `arguments.callee` or any of its properties is **highly discouraged**. - -> **ES5 Note:** In strict mode, `arguments.callee` will throw a `TypeError` since -> its use has been deprecated. - -[1]: http://en.wikipedia.org/wiki/Inlining - - diff --git a/doc/zh(Trandition)/array/constructor.md b/doc/zh-TW/array/constructor.md similarity index 100% rename from doc/zh(Trandition)/array/constructor.md rename to doc/zh-TW/array/constructor.md diff --git a/doc/zh(Trandition)/array/general.md b/doc/zh-TW/array/general.md similarity index 100% rename from doc/zh(Trandition)/array/general.md rename to doc/zh-TW/array/general.md diff --git a/doc/zh(Trandition)/core/delete.md b/doc/zh-TW/core/delete.md similarity index 100% rename from doc/zh(Trandition)/core/delete.md rename to doc/zh-TW/core/delete.md diff --git a/doc/zh(Trandition)/core/eval.md b/doc/zh-TW/core/eval.md similarity index 100% rename from doc/zh(Trandition)/core/eval.md rename to doc/zh-TW/core/eval.md diff --git a/doc/zh(Trandition)/core/semicolon.md b/doc/zh-TW/core/semicolon.md similarity index 100% rename from doc/zh(Trandition)/core/semicolon.md rename to doc/zh-TW/core/semicolon.md diff --git a/doc/zh-TW/core/undefined.md b/doc/zh-TW/core/undefined.md new file mode 100644 index 00000000..4c9378be --- /dev/null +++ b/doc/zh-TW/core/undefined.md @@ -0,0 +1,55 @@ +## `undefined` 和 `null` + +JavaScript 中有兩個表示空值的方式, `null` 和 `undefined` , `undefined`式比較常用的一種。 + +### `undefined` 的值 + +`undefined` 是一個值為 `undefined` 的類型。 + +語言中也定義了一個全域變數,它的值為 `undefined`,這個變數的被稱作 `undefined` 。 +這個變數 **不是** 一個常數,也不是一個關鍵字。這表示它的值可以被輕易的覆蓋。 + +> **ES5 提示: ** `undefined` 在 ECMAScript 5 裡 **不再是** *可寫* 的 +> 但是它的名稱還是可以被隱藏,比如說定義一個函數為 `undefined`。 + +這裡有一些例子會回傳 `undefined` 的值: + + - 進入尚未修改的全域變數 `undefined`。 + - 進入一個宣告但 **尚未** 初始化的變數。 + - `return` 表示式中沒有返回任何內容。 + - 呼叫不存在的屬性。 + - 函式參數沒有被傳遞數值。 + - 任何被被設定為 `undefined` 的變數。 + - 任何表達式中形式為 `void(expression)` + +### 處理 `undefined` 值的改變 + +由於全域變數 `undefined` 只有保存 `undefined` 類型實際值的一個副本,指定了一個新的值並 **不會** 改變 `undefined`類型裡面的值。 + +為了避免去改變 `undefined` 的值,常用的技巧就是加上一個新的變數到 [匿名包裝器](#function.scopes)。在使用的時候,這個參數不會接受任何的值。 + + var undefined = 123; + (function(something, foo, undefined) { + // undefined 在區域區間內得到了 `undefined` 的值 + + })('Hello World', 42); + +另外一個可以得到同樣的效果就是在內部宣告一個變數 + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +唯一的不同就是在下者會多 4 個多 bytes 用來壓縮檔案,而且函數內野沒有其他需要使用 `var` + +### 使用 `null` + +JavaScript 中所使用的 `undefined` 類似別的語言中的 *null* , 但實際上在 JavaScript 中的 `null` 算是另外一個類型。 + +它在 JavaScript 有些可以使用的地方 (例如說宣告一個原型的終結,例如 `Foo.prototype = null` )。 +但是在大部分的時候可以用 `undefined`,來取代。 + + diff --git a/doc/zh-TW/function/arguments.md b/doc/zh-TW/function/arguments.md new file mode 100644 index 00000000..428a575d --- /dev/null +++ b/doc/zh-TW/function/arguments.md @@ -0,0 +1,103 @@ +## `arguments` 物件 + +所有函數在 JavaScript 中都可以有個特別的參數 `arguments`。 +這個變數掌握了一列傳入函數中的參數 + +> **注意:** 由於 `arguments` 都已經在函數中被定義了 +> 經過 `var` 定義或是用 `arguments` 宣告參數 +> `arguments` 物件都不會被建立 + +`arguments` 物件 **不是** 一個 `Array`,雖然都有很多 Array 的語法 - 就像是 `length` 屬性 - 但是它沒有繼承來自 `Array.prototype` 事實上它繼承 `object`。 + +由於這些原因,這 **不可能** 用 Array 的一些功能像是 `push`、`pop`或是 `slice` 在 `arguments`。 +但是像 `for` 迴圈這些迴圈都是可以用的,如果真的需要使用一些標準的 `Array` 功能可以先把它轉成真的 `Array` 再去使用。 + +### 轉為 Array + +下面的程式可以回傳一個新的 `Array` 包含所有的元素在 `Arguments`的物件中 + + Array.prototype.slice.call(arguments); + +這種轉化方式比較 **慢** ,不建議使用這種作法如果再追求效率的程式中。 + + +### 傳遞參數 + +下面是建議用這種方式去傳參數到另一個函數 + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // 在這裡做一些事情 + } + +另一個技巧是用 `call` 和 `apply` 放在一起來創造一個更快的解綁定包裝器 + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Create an unbound version of "method" + // 輸入的參數: this, arg1, arg2...argN + Foo.method = function() { + + // 結果: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +### 自動更新 + +在 `Arguments` 物件創造的 *getter* 和 *setter* 的函數方法,可以被視為原本函數的變數。 + +因此,改變了一個變數會跟著改變它的值而且也間接的改變稻香對應的 `arguments` 的物件,反之亦然。 + + function foo(a, b, c) { + arguments[0] = 2; + a; // 2 + + b = 4; + arguments[1]; // 4 + + var d = c; + d = 9; + c; // 3 + } + foo(1, 2, 3); + +### 性能 + +`arguments` 總是會被宣告,但除了兩個情況,一個是在一個函式中或是在其中一個參入。而不論他是否有被使用。 + +*getters* 和 *setter* 會永遠被創造。然而,他們對任何性能都沒有影響,除非對它的屬性有多次的訪問 + + +> **ES5 提示:** 那些 *getters* 和 *setters* 在嚴格的模式像不會被建立 + +然而會有一種情況來降低 JavaScript 引擎的效能。就是使用 `arguments.callee`。 + + function foo() { + arguments.callee; // 做一些在這個函數物件 + arguments.callee.caller; // 然後呼叫這個函數物件 + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // 通常會在內聯 + } + } + +在上面的程式中, `foo` 不再是一個單存的互聯函數 +因為它需要知道他自己和它的調用者。 +這不僅減低了它的性能,而且還破壞的封裝 + +**強烈建議不要使用** `arguments.callee` 或是其他它的屬性 + +> **ES5 Note:** 在嚴格的模式下 `arguments.callee` 會丟出一個 `TypeError`, 因為這種方法已經被廢除了 + +[1]: http://en.wikipedia.org/wiki/Inlining + + diff --git a/doc/zh(Trandition)/function/closures.md b/doc/zh-TW/function/closures.md similarity index 100% rename from doc/zh(Trandition)/function/closures.md rename to doc/zh-TW/function/closures.md diff --git a/doc/zh(Trandition)/function/constructors.md b/doc/zh-TW/function/constructors.md similarity index 100% rename from doc/zh(Trandition)/function/constructors.md rename to doc/zh-TW/function/constructors.md diff --git a/doc/zh(Trandition)/function/general.md b/doc/zh-TW/function/general.md similarity index 100% rename from doc/zh(Trandition)/function/general.md rename to doc/zh-TW/function/general.md diff --git a/doc/zh(Trandition)/function/scopes.md b/doc/zh-TW/function/scopes.md similarity index 100% rename from doc/zh(Trandition)/function/scopes.md rename to doc/zh-TW/function/scopes.md diff --git a/doc/zh(Trandition)/function/this.md b/doc/zh-TW/function/this.md similarity index 100% rename from doc/zh(Trandition)/function/this.md rename to doc/zh-TW/function/this.md diff --git a/doc/zh(Trandition)/index.json b/doc/zh-TW/index.json similarity index 100% rename from doc/zh(Trandition)/index.json rename to doc/zh-TW/index.json diff --git a/doc/zh(Trandition)/intro/index.md b/doc/zh-TW/intro/index.md similarity index 100% rename from doc/zh(Trandition)/intro/index.md rename to doc/zh-TW/intro/index.md diff --git a/doc/zh(Trandition)/object/forinloop.md b/doc/zh-TW/object/forinloop.md similarity index 100% rename from doc/zh(Trandition)/object/forinloop.md rename to doc/zh-TW/object/forinloop.md diff --git a/doc/zh(Trandition)/object/general.md b/doc/zh-TW/object/general.md similarity index 100% rename from doc/zh(Trandition)/object/general.md rename to doc/zh-TW/object/general.md diff --git a/doc/zh(Trandition)/object/hasownproperty.md b/doc/zh-TW/object/hasownproperty.md similarity index 100% rename from doc/zh(Trandition)/object/hasownproperty.md rename to doc/zh-TW/object/hasownproperty.md diff --git a/doc/zh(Trandition)/object/prototype.md b/doc/zh-TW/object/prototype.md similarity index 100% rename from doc/zh(Trandition)/object/prototype.md rename to doc/zh-TW/object/prototype.md diff --git a/doc/zh(Trandition)/other/timeouts.md b/doc/zh-TW/other/timeouts.md similarity index 100% rename from doc/zh(Trandition)/other/timeouts.md rename to doc/zh-TW/other/timeouts.md diff --git a/doc/zh(Trandition)/types/casting.md b/doc/zh-TW/types/casting.md similarity index 100% rename from doc/zh(Trandition)/types/casting.md rename to doc/zh-TW/types/casting.md diff --git a/doc/zh(Trandition)/types/equality.md b/doc/zh-TW/types/equality.md similarity index 100% rename from doc/zh(Trandition)/types/equality.md rename to doc/zh-TW/types/equality.md diff --git a/doc/zh(Trandition)/types/instanceof.md b/doc/zh-TW/types/instanceof.md similarity index 100% rename from doc/zh(Trandition)/types/instanceof.md rename to doc/zh-TW/types/instanceof.md diff --git a/doc/zh(Trandition)/types/typeof.md b/doc/zh-TW/types/typeof.md similarity index 100% rename from doc/zh(Trandition)/types/typeof.md rename to doc/zh-TW/types/typeof.md diff --git a/site/zh-TW/index.html b/site/zh-TW/index.html new file mode 100644 index 00000000..8ae0e924 --- /dev/null +++ b/site/zh-TW/index.html @@ -0,0 +1,1913 @@ +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 and subtle bugs, as well as performance issues and bad +practices, that non-expert JavaScript programmers may encounter on their +endeavours into the depths of the language.

+ +

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

+ +

The Authors

+ +

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

+ +

Contributors

+ + + +

Hosting

+ +

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

+ +

License

+ +

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

Objects

Object Usage and Properties

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

+ +
false.toString(); // 'false'
+[1, 2, 3].toString(); // '1,2,3'
+
+function 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 that can be used to make number literals act +as objects too.

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

Objects as a Data Type

+ +

Objects in JavaScript can also be used as Hashmaps; they mainly consist +of named properties mapping to values.

+ +

Using an object literal - {} notation - it is possible to create a +plain object. This new object inherits from Object.prototype and +does not have own properties defined.

+ +
var foo = {}; // a new empty object
+
+// a new object with a 'test' property 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
+
+ +

The notations work almost identically, with the only difference being that the +square bracket notation allows for dynamic setting of properties and +the use of property names that would otherwise lead to a syntax error.

+ +

Deleting Properties

+ +

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

+ +
var obj = {
+    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, so me too' // raises SyntaxError
+};
+
+ +

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

+ +

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

The Prototype

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

+ +

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

+ +

JavaScript is the only widely used language that features prototypal +inheritance, so it can take time to adjust to the differences between the two +models.

+ +

The first major difference is that inheritance in JavaScript uses prototype +chains.

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

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

+ + + +

Property Lookup

+ +

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

+ +

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

+ +

The Prototype Property

+ +

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

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

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

+ +

Performance

+ +

The lookup time for properties that are high up on the prototype chain can have +a negative impact on performance, and this may be significant in code where +performance is critical. Additionally, trying to access non-existent properties +will always traverse the full prototype chain.

+ +

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

+ +

Extension of Native Prototypes

+ +

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

+ +

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

+ +

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

+ +

In Conclusion

+ +

It is essential to understand the prototypal inheritance model before +writing complex code that makes use of it. Also, be aware of the length of the +prototype chains in your code and break them up if necessary to avoid possible +performance problems. Further, the native prototypes should never be +extended unless it is for the sake of compatibility with newer JavaScript +features.

hasOwnProperty

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

+ + + +

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

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

Only hasOwnProperty will give the correct and expected result; 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 to get correct results.

+ +
var foo = {
+    hasOwnProperty: function() {
+        return false;
+    },
+    bar: 'Here be dragons'
+};
+
+foo.hasOwnProperty('bar'); // always returns false
+
+// Use another Object's hasOwnProperty and call it with 'this' set to foo
+({}).hasOwnProperty.call(foo, 'bar'); // true
+
+// It's also possible to use the hasOwnProperty property from the Object property for this purpose
+Object.prototype.hasOwnProperty.call(obj, 'bar'); // true
+
+ +

In Conclusion

+ +

Using hasOwnProperty is the only reliable method to check for the +existence of a property on an object. It is recommended that hasOwnProperty +is used in every for in loop to avoid errors from +extended native prototypes.

The for in Loop

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

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

Since it is not possible to change the behavior of the for in loop itself, it +is necessary to filter out the unwanted properties inside the loop body; +this is done 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 that extends Object.prototype 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. Assumptions should never +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 an asynchronous function.

+ +

The function Declaration

+ +
function foo() {}
+
+ +

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

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

The function Expression

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

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

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

Due to the fact that var is a declaration that hoists the variable name foo +before the actual execution of the code starts, foo is already 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. There are exactly five different +ways in which the value of this can be bound in the language.

+ +

The Global Scope

+ +
this;
+
+ +

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

+ +

Calling a Function

+ +
foo();
+
+ +

Here, this will again refer to the global object.

+ + + +

Calling a Method

+ +
test.foo(); 
+
+ +

In this example, this will refer to test.

+ +

Calling a Constructor

+ +
new foo(); 
+
+ +

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

+ +

Explicit Setting of this

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

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

+ +

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

+ + + +

Common Pitfalls

+ +

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

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

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

+ +

In order to gain access to Foo from within test, it is necessary to create a +local variable inside of method that 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, which is +assigning a method to a variable.

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

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

+ +

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

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

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

Closures and References

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

+ +

Emulating private variables

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

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

+ +

Why Private Variables Work

+ +

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

+ +
var foo = new Counter(4);
+foo.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 loop's index variable.

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

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

+ +

The anonymous function keeps a reference to i. At the time +console.log gets called, the for loop has already finished, and the value of +i 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, which is to return a function +from the anonymous wrapper that will then have the same behavior as the code +above.

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

arguments 物件

所有函數在 JavaScript 中都可以有個特別的參數 arguments。 +這個變數掌握了一列傳入函數中的參數

+ + + +

arguments 物件 不是 一個 Array,雖然都有很多 Array 的語法 - 就像是 length 屬性 - 但是它沒有繼承來自 Array.prototype 事實上它繼承 object

+ +

由於這些原因,這 不可能 用 Array 的一些功能像是 pushpop或是 slicearguments。 +但是像 for 迴圈這些迴圈都是可以用的,如果真的需要使用一些標準的 Array 功能可以先把它轉成真的 Array 再去使用。

+ +

轉為 Array

+ +

下面的程式可以回傳一個新的 Array 包含所有的元素在 Arguments的物件中

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

這種轉化方式比較 ,不建議使用這種作法如果再追求效率的程式中。

+ +

傳遞參數

+ +

下面是建議用這種方式去傳參數到另一個函數

+ +
function foo() {
+    bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+    // 在這裡做一些事情
+}
+
+ +

另一個技巧是用 callapply 放在一起來創造一個更快的解綁定包裝器

+ +
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+    console.log(this, a, b, c);
+};
+
+// Create an unbound version of "method" 
+// 輸入的參數: this, arg1, arg2...argN
+Foo.method = function() {
+
+    // 結果: Foo.prototype.method.call(this, arg1, arg2... argN)
+    Function.call.apply(Foo.prototype.method, arguments);
+};
+
+ +

自動更新

+ +

Arguments 物件創造的 gettersetter 的函數方法,可以被視為原本函數的變數。

+ +

因此,改變了一個變數會跟著改變它的值而且也間接的改變稻香對應的 arguments 的物件,反之亦然。

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

性能

+ +

arguments 總是會被宣告,但除了兩個情況,一個是在一個函式中或是在其中一個參入。而不論他是否有被使用。

+ +

getterssetter 會永遠被創造。然而,他們對任何性能都沒有影響,除非對它的屬性有多次的訪問

+ + + +

然而會有一種情況來降低 JavaScript 引擎的效能。就是使用 arguments.callee

+ +
function foo() {
+    arguments.callee; // 做一些在這個函數物件
+    arguments.callee.caller; // 然後呼叫這個函數物件
+}
+
+function bigLoop() {
+    for(var i = 0; i < 100000; i++) {
+        foo(); // 通常會在內聯
+    }
+}
+
+ +

在上面的程式中, foo 不再是一個單存的互聯函數 +因為它需要知道他自己和它的調用者。 +這不僅減低了它的性能,而且還破壞的封裝

+ +

強烈建議不要使用 arguments.callee 或是其他它的屬性

+ +

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 by 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 same thing, a newly create object that +has a property called method, which is a +Closure.

+ +

It should also be noted 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

+ +

It is often recommended to not use new because forgetting its use may +lead to bugs.

+ +

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

+ +
function 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 +is contrary to the spirit of the language.
  6. +
+ +

In Conclusion

+ +

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

Scopes and Namespaces

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

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

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

+ +

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

+ +

The Bane of Global Variables

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

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

+ +

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

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

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

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

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

+ +

Local Variables

+ +

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

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

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

+ +

Hoisting

+ +

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

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

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

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

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

+ +

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

+ +

Without knowledge of hoisting, one might suspect the code below would raise a +ReferenceError.

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

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

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

Name Resolution Order

+ +

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

+ +

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

+ +

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

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

Namespaces

+ +

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

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

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

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

There are other ways to evaluate and directly call the function expression +which, while different in syntax, behave the same way.

+ +
// A few other styles for directly invoking the 
+!function(){}()
++function(){}()
+(function(){}());
+// and so on...
+
+ +

In Conclusion

+ +

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

+ +

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

Arrays

Array 迴圈和屬性

雖然在 Javascript 中 Array 都是 Objects,但是沒有好的理由要使用他 +在 for in 的迴圈中。事實上有很多原因要避免使用 for in 在 Array 之中

+ + + +

因為 for in 迴圈會列舉所有在原型 Array 上的屬性因為他會使用hasOwnProperty, 這會使得 Array 比原本的 for 迴圈慢上二十幾倍

+ +

迴圈

+ +

為了要達到最好的性能所以最好使用 for 迴圈來讀取一個 Array 裡面的數值。

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

在上面的例子中利用 l = list.length 來處理 Array 的長度問題。

+ +

雖然 length 屬性是屬於 Array 中其中一個屬性,但是他還使有一定的性能消耗在每次循環的訪問。 +近期 Javascript 使用 may 來解決在這上面的效率問題,但是在現在的引擎上還不一定有支援。

+ +

實際上,不使用暫存 Array 長度的方式比使用暫存的版本還要慢很多。

+ +

length 的屬性

+ +

length 屬性中的 getter 直接回傳在 Array 之中的程度,而 setter 可以用來 刪除 Array。

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

在上面的例子可以看到,如果給的長度比較小他就會去刪除 Array 中的數值。如果比較大的話,他就會自己增加一些 undefined 的數值進去

+ +

結語

+ +

為了達到更好的效率,建議使用 for 迴圈還有暫存 length 的屬性。 +而 for in 迴圈則是會讓程式中有更多的錯誤和性能問題。

Array 的建構函式

Array 的建構函式在處理參數上一直有模糊的地帶,所以建議使用 array的字面語法來使用 - [] - 來新增一個的Array

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

在上面的範例 new Array(3) 當只有一個參數傳入到 Array 的建構函數 +且那個參數事宜個數字,建構函數會回傳空值 +但是 Array 長度的屬性會變成跟那個參數一樣(以此範例來看他回傳的長度為 3) +注意 只有他長度的屬性會被設定,整個 Array裡面的數值都不會初始化

+ +
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // false, 數值沒有被設定進去
+
+ +

被設定用來當做 Array 的長度只有少數情況使用 +先設定 Array 的長度可以用一下的範例來避免使用 for loop 的麻煩

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

結語

+ +

Array 的建構函式需要避免,建議使用字面語法。因為他們比較簡短、也更增加閱讀性

Types

Equality and Comparisons

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

+ +

The Equality Operator

+ +

The equality operator consists of two equal signs: ==

+ +

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

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

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

+ +

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

+ +

The Strict Equality Operator

+ +

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

+ +

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

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

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

+ +

Comparing Objects

+ +

While both == and === are called equality operators, they behave +differently when at least one of their operands is an Object.

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

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

+ +

In Conclusion

+ +

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

The typeof Operator

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

+ +

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

+ + + +

The JavaScript Type Table

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

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

+ +

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

+ + + +

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

+ +

The Class of an Object

+ +

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

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

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

+ + + +

Testing for Undefined Variables

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

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

+ +

In Conclusion

+ +

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

+ +

Unless checking whether a variable is defined, typeof should be avoided.

The instanceof Operator

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

+ +

Comparing Custom Objects

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

Using instanceof with Native Types

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

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

+ +

In Conclusion

+ +

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

Type Casting

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

+ +
// These are true
+new Number(10) == 10; // Number.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';
+
+ + + +

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

+ +

Constructors of Built-In Types

+ +

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

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

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

+ +

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

+ +

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

+ +

Casting to a String

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

By prepending an empty string, a value can easily be cast to a string.

+ +

Casting to a Number

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

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

+ +

Casting to a Boolean

+ +

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

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

Core

為什麼不要使用 eval

因為 eval 函數會在 Javascript 的區域性的區間執行那段程式碼。

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

但是, eval 只接受直接的呼叫而且那個函數只能叫做 eval,才能在一個區段中執行。

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

所有的 eval 都應該去比免試用。有 99.9% 的使用情況都可以 不必 使用到而達到同等效果。

+ +

偽裝的 eval

+ +

定時函數 setTimeoutsetInterval 都可以接受一個字串當做他們第一個參數。這些字串 永遠 都會在全域範圍內執行,因此在這種情況下 eval 沒有被直接的使用。

+ +

安全上的顧慮

+ +

eval 同樣有安全上的問題,因為所有的程式碼都可以被直接執行。 +而他不應去執行一串未知的字串或是來自不幸任的來源。

+ +

結語

+ +

eval 應該永遠不要去只用它,任何的程式在被他執行後都有性能和安全上的考慮。如果有情況需要去使用他,他都不應該列為第一順位的解決方法。

+ +

應該有更好的方法能夠去使用,但是最好都不要去使用 eval

undefinednull

JavaScript 中有兩個表示空值的方式, nullundefinedundefined式比較常用的一種。

+ +

undefined 的值

+ +

undefined 是一個值為 undefined 的類型。

+ +

語言中也定義了一個全域變數,它的值為 undefined,這個變數的被稱作 undefined 。 +這個變數 不是 一個常數,也不是一個關鍵字。這表示它的值可以被輕易的覆蓋。

+ + + +

這裡有一些例子會回傳 undefined 的值:

+ +
    +
  • 進入尚未修改的全域變數 undefined
  • +
  • 進入一個宣告但 尚未 初始化的變數。
  • +
  • return 表示式中沒有返回任何內容。
  • +
  • 呼叫不存在的屬性。
  • +
  • 函式參數沒有被傳遞數值。
  • +
  • 任何被被設定為 undefined 的變數。
  • +
  • 任何表達式中形式為 void(expression)
  • +
+ +

處理 undefined 值的改變

+ +

由於全域變數 undefined 只有保存 undefined 類型實際值的一個副本,指定了一個新的值並 不會 改變 undefined類型裡面的值。

+ +

為了避免去改變 undefined 的值,常用的技巧就是加上一個新的變數到 匿名包裝器。在使用的時候,這個參數不會接受任何的值。

+ +
var undefined = 123;
+(function(something, foo, undefined) {
+    // undefined 在區域區間內得到了 `undefined` 的值
+
+})('Hello World', 42);
+
+ +

另外一個可以得到同樣的效果就是在內部宣告一個變數

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

唯一的不同就是在下者會多 4 個多 bytes 用來壓縮檔案,而且函數內野沒有其他需要使用 var

+ +

使用 null

+ +

JavaScript 中所使用的 undefined 類似別的語言中的 null , 但實際上在 JavaScript 中的 null 算是另外一個類型。

+ +

它在 JavaScript 有些可以使用的地方 (例如說宣告一個原型的終結,例如 Foo.prototype = null )。 +但是在大部分的時候可以用 undefined,來取代。

自動插入分號

雖然 JavaScript 有 C 語言的語法,但是他不強制一定要加上分號。 +所以分號可以被忽略。

+ +

Javascript 並 不是 一個不需要分號的語言。實際上,它需要分號來讓程式碼更容易被理解。因此 Javascript 的編譯器中遇到了缺少分號的情形,它會自動的在程式碼中插入分號。

+ +
var foo = function() {
+} // 編輯錯誤,因沒分號
+test()
+
+ +

這時候編譯器在編輯的時候,會自動的加上分號,然後重新編輯。

+ +
var foo = function() {
+}; // 沒有錯誤,編輯繼續
+test()
+
+ +

自動的加入分號是被認為 最大 的設計缺陷之一,因為它能改變程式碼的行為。

+ +

工作原理

+ +

下面的程式碼中沒有使用任何的分號,所以編譯器需要去決定在哪些地方加入分號。

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

下面的程式碼是編譯器 猜測 的結果。

+ +
(function(window, undefined) {
+    function test(options) {
+
+        // 沒有加入分號,兩行被合併為一行
+        log('testing!')(options.list || []).forEach(function(i) {
+
+        }); // <- 插入分號
+
+        options.value.test(
+            'long string to pass here',
+            'and another long string to pass'
+        ); // <- 插入分號
+
+        return; // <- 插入分號,改變了 return 的表達行為
+        { // 作為另一個程式碼的處理
+
+            // 被當做一個獨立的函數來看
+            foo: function() {} 
+        }; // <- 插入分號
+    }
+    window.test = test; // <- 插入分號
+
+// 兩行又被合併
+})(window)(function(window) {
+    window.someLibrary = {}; // <- 插入分號
+
+})(window); //<- 插入分號
+
+ + + +

編譯器在上面的程式碼中改變了原本程式碼的行為。在一些情況下,會做出 錯誤的行為

+ +

前置括號

+ +

在這種前置括號的情況下,編譯器 不會 自動的插入分號。

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

上面的程式碼被編譯器轉為只有一行程式

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

以上的範例中 log很大 的可能 不是 回傳一個函數。然而這個情況下會出現 TypeError 的錯誤或是會出現 undefined is not a function .

+ +

結語

+ +

建議永遠 不要 忽略分號。同樣的也建議大括號應在他對應的表達式在同一行。在 if... else...的表達式中也是如此,不應省略大括號。 +這個習慣可以不僅僅是讓你的程式更一致,也可以避免編譯器因為改變程式而出錯。

The delete Operator

In short, it's impossible to delete global variables, functions and some other +stuff in JavaScript which have a DontDelete attribute set.

+ +

Global code and Function code

+ +

When a variable or a function is defined in a global or a function +scope it is a property of either the Activation object or +the Global object. Such properties have a set of attributes, one of which is +DontDelete. Variable and function declarations in global and function code +always create properties with DontDelete, and therefore cannot be deleted.

+ +
// global variable:
+var a = 1; // DontDelete is set
+delete a; // false
+a; // 1
+
+// normal function:
+function f() {} // DontDelete is set
+delete f; // false
+typeof f; // "function"
+
+// reassigning doesn't help:
+f = 1;
+delete f; // false
+f; // 1
+
+ +

Explicit properties

+ +

Explicitly set properties can be deleted normally.

+ +
// explicitly set property:
+var obj = {x: 1};
+obj.y = 2;
+delete obj.x; // true
+delete obj.y; // true
+obj.x; // undefined
+obj.y; // undefined
+
+ +

In the example above, obj.x and obj.y can be deleted because they have no +DontDelete atribute. That's why the example below works too.

+ +
// this works fine, except for IE:
+var GLOBAL_OBJECT = this;
+GLOBAL_OBJECT.a = 1;
+a === GLOBAL_OBJECT.a; // true - just a global var
+delete GLOBAL_OBJECT.a; // true
+GLOBAL_OBJECT.a; // undefined
+
+ +

Here we use a trick to delete a. this here refers +to the Global object and we explicitly declare variable a as its property +which allows us to delete it.

+ +

IE (at least 6-8) has some bugs, so the code above doesn't work.

+ +

Function arguments and built-ins

+ +

Functions' normal arguments, arguments objects +and built-in properties also have DontDelete set.

+ +
// function arguments and properties:
+(function (x) {
+
+  delete arguments; // false
+  typeof arguments; // "object"
+
+  delete x; // false
+  x; // 1
+
+  function f(){}
+  delete f.length; // false
+  typeof f.length; // "number"
+
+})(1);
+
+ +

Host objects

+ +

The behaviour of delete operator can be unpredictable for hosted objects. Due +to the specification, host objects are allowed to implement any kind of behavior.

+ +

In conclusion

+ +

The delete operator often has unexpected behaviour and can only be safely +used to delete explicitly set properties on normal objects.

Other

setTimeout and setInterval

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

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

When setTimeout is called, it returns the ID of the timeout and schedule +foo to run approximately one thousand milliseconds in the future. +foo will then be executed once.

+ +

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

+ +

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

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

Stacking Calls with setInterval

+ +

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

+ +

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

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

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

+ +

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

+ +

Dealing with Possible Blocking Code

+ +

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

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

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

+ +

Manually Clearing Timeouts

+ +

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

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

Clearing All Timeouts

+ +

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

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

But there might still be timeouts that are unaffected by this arbitrary number. +Another way of doing this is to consider that the ID given to a timeout is +incremented by one every time you call setTimeout.

+ +
// clear "all" timeouts
+var biggestTimeoutId = window.setTimeout(function(){}, 1),
+i;
+for(i = 1; i <= biggestTimeoutId; i++) {
+    clearTimeout(i);
+}
+
+ +

Even though this works on all major browsers today, it isn't specified that +the IDs should be ordered that way and it may change. Therefore, it is instead +recommended to keep track of all the timeout IDs, so they can be cleared +specifically.

+ +

Hidden Use of eval

+ +

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

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

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

+ +

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

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

In Conclusion

+ +

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

+ +

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

\ No newline at end of file From 4a2617698774b12942c24b7411e9088db5f53513 Mon Sep 17 00:00:00 2001 From: howardchi Date: Thu, 14 Mar 2013 02:57:50 +0800 Subject: [PATCH 009/216] finished function/ closures, general --- doc/zh-TW/function/closures.md | 63 ++++++++++++---------------------- doc/zh-TW/function/general.md | 45 +++++++++++------------- 2 files changed, 42 insertions(+), 66 deletions(-) diff --git a/doc/zh-TW/function/closures.md b/doc/zh-TW/function/closures.md index 76f2d078..8c558efa 100644 --- a/doc/zh-TW/function/closures.md +++ b/doc/zh-TW/function/closures.md @@ -1,11 +1,10 @@ -## Closures and References +## Closures 和 References -One of JavaScript's most powerful features is the availability of *closures*. -With closures, scopes **always** keep access to the outer scope, in which they -were defined. Since the only scoping that JavaScript has is -[function scope](#function.scopes), all functions, by default, act as closures. +JavaScript 有一個很重要的特徵就是 **closures** +因為有 Closures,所以作用域 **永遠** 能夠去訪問作用區間外面的變數。 +[函數區間](#function.scopes) 是JavaScript 中唯一擁有自生作用域的結構,因此 Closures 的創立需要依賴函數 -### Emulating private variables +### 模仿私有變數 function Counter(start) { var count = start; @@ -24,30 +23,24 @@ were defined. 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 scope. +這裡,`Counter` 返回兩個 Closures,函數 `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` 變數。 +唯一的途徑就是經過那兩個 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`. +在上面的例子中 `count` **不會** 改變到 `Counter` 裡面的 `count` 的值。因為 `foo.hack` 沒有在 **那個** 作用域內被宣告。它只有會覆蓋或者建立在一個 **全域** 的變數 `count` -### Closures Inside Loops +### 在循環內的 Closures -One often made mistake is to use closures inside of loops, as if they were -copying the value of the loop's index variable. +一個常見的錯誤就是在 Closures 中使用迴圈,假設我們要使用每次迴圈中所使用的進入變數 for(var i = 0; i < 10; i++) { setTimeout(function() { @@ -55,20 +48,14 @@ copying the value of the loop's index variable. }, 1000); } -The above will **not** output the numbers `0` through `9`, but will simply print -the number `10` ten times. - -The *anonymous* function keeps a **reference** to `i`. At the time -`console.log` gets called, the `for loop` has already finished, and the value of -`i` as been set to `10`. - -In order to get the desired behavior, it is necessary to create a **copy** of -the value of `i`. +在上面的例子中它 **不會** 輸出數字從 `0` 到 `9`,但只會出現數字 `10` 十次。 +在 `console.log` 被呼叫的時候,這個 *匿名* 函數中保持一個 **參考** 到 i ,此時 `for`迴圈已經結束, `i` 的值被修改成了 `10`。 +為了要達到想要的結果,需要在每次創造 **副本** 來儲存 `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) { @@ -78,15 +65,9 @@ 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`. - -The anonymous function that gets passed to `setTimeout` now has a reference to -`e`, whose value does **not** get changed by the loop. - -There is another possible way of achieving this, which is to return a function -from the anonymous wrapper that will then have the same behavior as the code -above. +匿名外部的函數被呼叫,並把 `i` 作為它第一個參數,此時函數內 `e` 變數就擁有了一個 `i` 的拷貝。 +當傳遞給 `setTimeout` 這個匿名函數執行時,它就擁有了對 `e` 的引用,而這個值 **不會** 被循環改變。 +另外有一個方法也可以完成這樣的工作,那就是在匿名函數中返回一個函數,這和上面的程式碼有同樣的效果。 for(var i = 0; i < 10; i++) { setTimeout((function(e) { diff --git a/doc/zh-TW/function/general.md b/doc/zh-TW/function/general.md index b2788e46..57f4810f 100644 --- a/doc/zh-TW/function/general.md +++ b/doc/zh-TW/function/general.md @@ -1,48 +1,43 @@ -## Function Declarations and Expressions +## 函式的宣告和表達方式 -Functions in JavaScript are first class objects. That means they can be -passed around like any other value. One common use of this feature is to pass -an *anonymous function* as a callback to another, possibly an asynchronous function. +函式在 JavaScript 是第一等物件。這表示他們可以把函式當做值一樣傳遞。 +一個常見的用法是用 *匿名函式* 當做一個回傳去呼叫另一個函式,這是一種非同步函式 -### The `function` Declaration +### 函式的宣告 function foo() {} -The above function gets [hoisted](#function.scopes) before the execution of the -program starts; thus, it is available *everywhere* in the scope it was -*defined*, even if called before the actual definition in the source. +上面的函式在被執行之前會被 [解析(hoisted)](#function.scopes),因此它可以在 **任意** 的地方都是 *有宣告的* ,就算是在比這個函式還早呼叫。 - foo(); // Works because foo was created before this code runs + + foo(); // 可以執行,因為 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 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(); // 可以運行 } - 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` 不可以在外部的區域被執行,因為它只有在 `foo` 的函式內才可以去執行。 +然而在 `bar` 內部還是可以看見。這是由於 JavaScript的 [命名處理](#function.scopes)所致。 +函式名在函式內 *都* 可以去使用。 From 5804c903bc52bdd878ea635fbf24b06405fcbf3e Mon Sep 17 00:00:00 2001 From: howardchi Date: Thu, 14 Mar 2013 12:38:22 +0800 Subject: [PATCH 010/216] modify language.json, index.json, constructors.md --- doc/language.json | 2 +- doc/zh-TW/function/constructors.md | 81 +++++++++++------------------- doc/zh-TW/function/scopes.md | 18 +++---- doc/zh-TW/function/this.md | 42 +++++++--------- doc/zh-TW/index.json | 20 ++++---- 5 files changed, 67 insertions(+), 96 deletions(-) diff --git a/doc/language.json b/doc/language.json index 19ef049f..c45b38b6 100644 --- a/doc/language.json +++ b/doc/language.json @@ -1,5 +1,5 @@ { "default": "en", - "listed": ["en", "fi", "ru", "zh", "tr", "pl", "ko", "ja", "es"] + "listed": ["en", "fi", "ru", "zh", "tr", "pl", "ko", "ja", "es", "zh-Tw"] } diff --git a/doc/zh-TW/function/constructors.md b/doc/zh-TW/function/constructors.md index bf9771dc..8c493854 100644 --- a/doc/zh-TW/function/constructors.md +++ b/doc/zh-TW/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. - -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. +JavaScript 中的建構函式和其他語言中的建構函式是不同的。 +用 `new` 的關鍵字方式調用的函式都被認為是建構函式。 +在建構函式內部 - 被呼叫的函式 - `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,16 +18,13 @@ 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`. - -In case of an explicit `return` statement, the function returns the value -specified by that statement, but **only** if the return value is an `Object`. +在上面的例子中 `Foo` 建立一個建構函式,並設立一個 `prototype` 來創建一個新的物件叫 `Foo.prototype`。 +這個情況下它顯示的 `return` 一個表達式,但他 **只** 返回一個 `Object`。 function Bar() { return 2; } - new Bar(); // a new object + new Bar(); // 返回一個新物件 function Test() { this.value = 2; @@ -39,23 +33,20 @@ specified by 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`. +雖然上面有些情況也能正常運行,但是由於 JavaScript 中 [`this`](#funciton.this) 的工作原理,這裡的 `this` 指向 *全域對象*。 -### 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 +63,17 @@ explicitly return a value. new Bar(); Bar(); -Both calls to `Bar` return the same thing, a newly create object that -has a property called `method`, which is a -[Closure](#function.closures). - -It should also be noted 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. +上面兩個呼叫 `Bar` 的方法回傳的值都一樣,一個新創建的擁有 `method` 屬性被返回,這裡創建了一個 [Closure](#function.closures). +還有注意, `new Bar()` 並 **不會** 改變返回物件的原型。 +因為建構函式的原型會指向剛剛創立的新物件,而在這裡的 `Bar` 沒有把這個新物件返回。 +在上面的例子中,使用或者不使用 `new` 關鍵字沒有什麼功能性的區別 -### Creating New Objects via Factories -It is often recommended to **not** use `new` because forgetting its use may -lead to bugs. +### 通過工廠模式創建的新對象 -In order to create a new object, one should rather use a factory and construct a -new object inside of that factory. +常聽到建議 **不要** 使用 `new`,因為如果忘記如何使用它會造成錯誤。 +為了創建一個新的物件,我們可以用工廠方法,來創造一個新的物件在那個方法中。 function Foo() { var obj = {}; @@ -107,22 +90,14 @@ 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 - is contrary to the spirit of the language. -### In Conclusion +1. 會占用更多的記憶體,因為創建的物件 **沒有** 辦法放在在同一個原型上。 +2. 為了要用繼承的方式,工廠方法需要複製所有的屬性或是把一個物件作為新的物件的原型。 +3. 放棄原型鏈僅僅是因為防止遺漏 `new` 所帶來的問題,這與語言本身的思想鄉違背。 -While omitting the `new` keyword might lead to bugs, it is certainly **not** a -reason to drop the use of prototypes altogether. In the end it comes down to -which solution is better suited for the needs of the application. It is -especially important to choose a specific style of object creation and use it -**consistently**. +### 結語 +雖然遺漏 `new` 關鍵字可能會導致問題,但這並 **不是** 放棄只用原型的藉口。 +最終使用哪種方式取決於應用程式的需求,選擇一種程式語言風格並堅持下去才是最重要的。 diff --git a/doc/zh-TW/function/scopes.md b/doc/zh-TW/function/scopes.md index 879e1e8a..00263804 100644 --- a/doc/zh-TW/function/scopes.md +++ b/doc/zh-TW/function/scopes.md @@ -1,12 +1,11 @@ -## Scopes and Namespaces +## 作用域和命名空間 -Although JavaScript deals fine with the syntax of two matching curly -braces for blocks, it does **not** support block scope; hence, all that is left -in the language is *function scope*. +儘管 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 } @@ -16,6 +15,7 @@ in the language is *function scope*. > **not** as an object literal. This, in conjunction with > [automatic insertion of semicolons](#core.semicolon), can lead to subtle errors. + There are also no distinct namespaces in JavaScript, which means that everything gets defined in one *globally shared* namespace. @@ -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 +### 全域變數的壞處 // script A foo = '42'; @@ -222,7 +222,7 @@ which, while different in syntax, behave the same way. (function(){}()); // and so on... -### In Conclusion +### 結語 It is recommended to always use an *anonymous wrapper* to encapsulate code in its own namespace. This does not only protect code against name clashes, but it diff --git a/doc/zh-TW/function/this.md b/doc/zh-TW/function/this.md index 48070d2e..31081883 100644 --- a/doc/zh-TW/function/this.md +++ b/doc/zh-TW/function/this.md @@ -1,50 +1,46 @@ -## How `this` Works +## `this` 的工作原理 -JavaScript has a different concept of what the special name `this` refers to -than most other programming languages. There are exactly **five** different -ways in which the value of `this` can be bound in the language. +JavaScript 有移到完全部屬於其他語言處理 `this` 的處理機制。 +在 **五** 種物同的情況下, `this` 指向的個不相同 -### The Global Scope +### 全域變數 this; -When using `this` in global scope, it will simply refer to the *global* object. +如果再全域範圍內使用 `this`,會指向 *全域* 的物件 -### Calling a Function +### 呼叫一個函式 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 注意:** 在嚴格模式下,不存在全域變數。 +> `this` 將會是 `undefined`。 -### Calling a Method +### 方法調用 test.foo(); -In this example, `this` will refer to `test`. +這個例子中, `this` 指向 `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`. +如果函式傾向用 `new` 關鍵詞使用,我們稱這個函式為 [建構函式](#function.constructors)。 +在函式內部, `this` 指向 *新物件的創立* -### Explicit Setting of `this` +### 顯示的設置 `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]); // Array 會被擴展,如下所示 + foo.call(bar, 1, 2, 3); // 傳遞參數 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` 上的 `call` 或只 `apply` 方法時,函式內的 `this` 將會被 **顯示設置** 為函式調用的第一個參數。 As a result, in the above example the *method case* does **not** apply, and `this` inside of `foo` will be set to `bar`. @@ -53,7 +49,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 +### 常見誤解 While most of these cases make sense, the first can be considered another mis-design of the language because it **never** has any practical use. diff --git a/doc/zh-TW/index.json b/doc/zh-TW/index.json index bffcbf2c..362fcb44 100644 --- a/doc/zh-TW/index.json +++ b/doc/zh-TW/index.json @@ -1,15 +1,15 @@ { - "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": "Intro", + "title": "簡介", "dir": "intro", "articles": [] }, { - "title": "Objects", + "title": "物件", "dir": "object", "articles": [ "general", @@ -19,7 +19,7 @@ ] }, { - "title": "Functions", + "title": "函式", "dir": "function", "articles": [ "general", @@ -31,7 +31,7 @@ ] }, { - "title": "Arrays", + "title": "陣列", "dir": "array", "articles": [ "general", @@ -39,7 +39,7 @@ ] }, { - "title": "Types", + "title": "類型", "dir": "types", "articles": [ "equality", @@ -49,7 +49,7 @@ ] }, { - "title": "Core", + "title": "核心", "dir": "core", "articles": [ "eval", @@ -59,7 +59,7 @@ ] }, { - "title": "Other", + "title": "其他", "dir": "other", "articles": [ "timeouts" From 3cb2af35117fea202c740ac1708dd8300b3512bc Mon Sep 17 00:00:00 2001 From: howardchi Date: Thu, 14 Mar 2013 15:51:02 +0800 Subject: [PATCH 011/216] finished function/ scope.md --- doc/zh-TW/function/scopes.md | 158 +++++++++------------- site/zh-TW/index.html | 251 ++++++++++++++--------------------- 2 files changed, 161 insertions(+), 248 deletions(-) diff --git a/doc/zh-TW/function/scopes.md b/doc/zh-TW/function/scopes.md index 00263804..3f7c72dd 100644 --- a/doc/zh-TW/function/scopes.md +++ b/doc/zh-TW/function/scopes.md @@ -10,18 +10,13 @@ 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),可能會造成一些不易察覺的錯誤。 +JavaScript 中沒有寫示的命名空間定義,這代表著它所有定義的東西都是 *全域共享* 在同一個命名空間下。 -There are also no distinct namespaces in JavaScript, which means that everything -gets defined in one *globally shared* namespace. - -Each time a variable is referenced, JavaScript will traverse upwards through all -the scopes until it finds it. In the case that it reaches the global scope and -still has not found the requested name, it will raise a `ReferenceError`. +每次引用一個變數,JavaScript 會向上找整個作用域直到找到這個變數為止。 +如果在全域中無法找到那個變數,它會拋出 `ReferenceError` 錯誤碼。 ### 全域變數的壞處 @@ -31,17 +26,14 @@ 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(); @@ -51,38 +43,39 @@ Leaving out the `var` statement inside the function `test` will override the value of `foo`. While this might not seem like a big deal at first, having thousands of lines of JavaScript and not using `var` will introduce horrible, hard-to-track-down bugs. +在函數 `test` 中部使用 `var` 會覆蓋到原本在外面的 `foo`。 +雖然看起來不是什麼大問題,但是當程式有幾千行的時候沒有使用 `var` 會照成難以追蹤的臭蟲。 + - // global scope + // 全域作用域 var items = [/* some list */]; for(var i = 0; i < 10; i++) { subLoop(); } function subLoop() { - // scope of subLoop - for(i = 0; i < 10; i++) { // 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. +在外面的迴圈在呼叫第一次 `subLoop` 之後就會停止,因為 `subLoop` 全域變數中的 `i` 被覆蓋了。 +在第二次使用 `for` 迴圈的時候,使用 `var` 就可以避免這種錯誤。 +在宣告變數的時候 **絕對不要** 忘記 `var`,除非就是 `希望他的效果` 是取改變外部的作用域。 -### Local Variables +### 局部變數 -The only source for local variables in JavaScript are -[function](#function.general) parameters and variables 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 +83,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 +106,14 @@ JavaScript **hoists** declarations. This means that both `var` statements and } } -The above code gets transformed before execution starts. JavaScript moves -the `var` statements, as well as `function` declarations, to the top of the -nearest surrounding scope. +在上面的程式碼會被轉化在執行之前。 JavaScript 會把 `var`,和 `function` 宣告,放到最頂端最接近的作用區間 - // var statements got moved here - var bar, someValue; // default to 'undefined' + // var 被移到這裡 + var bar, someValue; // 值等於 'undefined' - // the function declaration got moved up too + // function 的宣告也被搬上來 function test(data) { - var goo, i, e; // missing block scope moves these here + var goo, i, e; // 沒有作用域的也被搬至頂端 if (false) { goo = 1; @@ -136,87 +125,72 @@ 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, although the `if` statement seemed to modify the *global -variable* `goo`, it actually modifies the *local variable* - after hoisting -has been applied. +在一般的程式中,雖然 `if` 表達式中看起來修改了 *全域變數* `goo`,但實際上在提昇規則被運用後,卻是在修改 *局部變數* -Without knowledge of *hoisting*, one might suspect the code below would raise a -`ReferenceError`. +如果沒有提昇規則的話,可能會出現像下面的看起來會出現 `ReferenceError` 的錯誤。 - // check whether SomeImportantThing has been initialized + // 檢查 SomeImportantThing 是否已經被初始化 if (!SomeImportantThing) { var SomeImportantThing = {}; } -But of course, this works due to the fact that the `var` statement is being -moved to the top of the *global scope*. +但是它沒有錯誤,因為 `var` 的表達式會被提升到 *全域作用域* 的頂端。 var SomeImportantThing; - // other code might initialize SomeImportantThing here, or not + // 有些程式,可能會初始化。 + 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*. +### 名稱解析順序 + +JavaScript 中所有的作用區,包括 *全域作用域*,都有一個特殊的名字 [`this`](#function.this), 在它們裡面被定義,指向當前的物件 -Function scopes also have the name [`arguments`](#function.arguments), defined in -them, which contains the arguments that were passed to the function. +函式作用域也有一個名稱叫做 [`arguments`](#function.arguments), 定義它們,其中包括傳到函式內的參數。 -For example, when trying to access a variable named `foo` inside the scope of a -function, JavaScript will look up the name in the following order: +例如,它們開始試著進入到 `foo` 的作用域裡面, JavaScript 會依照下面的順序去查詢: - 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. 回溯到上一個層級然後再從第一個開始往下去查。 -> **Note:** Having a parameter called `arguments` will **prevent** the creation -> of the default `arguments` object. +> **注意: ** 自定義 `arguments` 參數會阻止原生的 `arguments` 的物件創立 -### Namespaces +### 命名空間 -A common problem associated with having only one global namespace is the -likelihood of running into problems where variable names clash. In JavaScript, -this problem can easily be avoided with the help of *anonymous wrappers*. +只有一個全域作用域會導致常見的錯誤是命名衝突。在 JavaScript 中可以透過 *匿名包裝器* 來解決。 (function() { - // a self contained "namespace" + // 自己本身的匿名空間 window.foo = function() { - // an exposed closure + // 對外公開的函式 }; - })(); // execute the function immediately + })(); // 馬上執行這個匿名函式 +匿名函式被認為是 [表達式](#function.general)因此為了要可以調用,它們會先被執行。 -Unnamed functions are considered [expressions](#function.general); so in order to -being callable, they must first be evaluated. - - ( // evaluate the function inside the parentheses + ( // 小括號內的先被執行 function() {} - ) // and return the function object - () // call the result of the evaluation + ) // 回傳函數對象 + () // 調用上面的執行結果 -There are other ways to evaluate and directly call the function expression -which, while different in syntax, behave the same way. +還有其他方式也可以像上面一樣調用函式的方式達到 - // A few other styles for directly invoking the !function(){}() +function(){}() (function(){}()); @@ -224,10 +198,6 @@ which, while different in syntax, behave the same way. ### 結語 -It is recommended to always use an *anonymous wrapper* to encapsulate code in -its own namespace. This does not only protect code against name clashes, but it -also allows for better modularization of programs. - -Additionally, the use of global variables is considered **bad practice**. **Any** -use of them indicates badly written code that is prone to errors and hard to maintain. +建議最好是都用 *匿名包裝器* 來封裝你的程式碼在自己的命名區間內。這不僅是要防止命名衝突也可以使得程序更有模組化。 +另外,全域變數是個 **不好的** 習慣,因為它會帶來錯誤和更難去維護。 \ No newline at end of file diff --git a/site/zh-TW/index.html b/site/zh-TW/index.html index 8ae0e924..94c2d741 100644 --- a/site/zh-TW/index.html +++ b/site/zh-TW/index.html @@ -1,7 +1,7 @@ -JavaScript Garden -

Intro

JavaScript Garden is a growing collection of documentation about the most +

簡介

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

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 +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'
@@ -357,105 +357,94 @@ 

License

It is recommended to always use hasOwnProperty. Assumptions should never 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 an asynchronous function.

+prototypes have been extended or not.

函式

函式的宣告和表達方式

函式在 JavaScript 是第一等物件。這表示他們可以把函式當做值一樣傳遞。 +一個常見的用法是用 匿名函式 當做一個回傳去呼叫另一個函式,這是一種非同步函式

-

The function Declaration

+

函式的宣告

function foo() {}
 
-

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

+

上面的函式在被執行之前會被 解析(hoisted),因此它可以在 任意 的地方都是 有宣告的 ,就算是在比這個函式還早呼叫。

-
foo(); // Works because foo was created before this code runs
+
foo(); // 可以執行,因為 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

-

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(); // 可以運行
 }
-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 in JavaScript works, the name of the -function is always made available in the local scope of the function itself.

How this Works

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

+

bar 不可以在外部的區域被執行,因為它只有在 foo 的函式內才可以去執行。 +然而在 bar 內部還是可以看見。這是由於 JavaScript的 命名處理所致。 +函式名在函式內 可以去使用。

this 的工作原理

JavaScript 有移到完全部屬於其他語言處理 this 的處理機制。 +在 種物同的情況下, this 指向的個不相同

-

The Global Scope

+

全域變數

this;
 
-

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

+

如果再全域範圍內使用 this,會指向 全域 的物件

-

Calling a Function

+

呼叫一個函式

foo();
 
-

Here, this will again refer to the global object.

+

這裡 this 也會指向 全域 對象。

-

Calling a Method

+

方法調用

test.foo(); 
 
-

In this example, this will refer to test.

+

這個例子中, this 指向 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.

+

如果函式傾向用 new 關鍵詞使用,我們稱這個函式為 建構函式。 +在函式內部, this 指向 新物件的創立

-

Explicit Setting of this

+

顯示的設置 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]); // Array 會被擴展,如下所示
+foo.call(bar, 1, 2, 3); // 傳遞參數 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 上的 call 或只 apply 方法時,函式內的 this 將會被 顯示設置 為函式調用的第一個參數。

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

@@ -466,7 +455,7 @@

License

obj, since this only gets bound by one of the five listed cases.

-

Common Pitfalls

+

常見誤解

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

@@ -523,12 +512,11 @@

License

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

Closures and References

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

+very instance.

Closures 和 References

JavaScript 有一個很重要的特徵就是 closures +因為有 Closures,所以作用域 永遠 能夠去訪問作用區間外面的變數。 +函數區間 是JavaScript 中唯一擁有自生作用域的結構,因此 Closures 的創立需要依賴函數

-

Emulating private variables

+

模仿私有變數

function Counter(start) {
     var count = start;
@@ -548,16 +536,12 @@ 

License

foo.get(); // 5
-

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

+

這裡,Counter 返回兩個 Closures,函數 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 變數。 +唯一的途徑就是經過那兩個 Closures

var foo = new Counter(4);
 foo.hack = function() {
@@ -565,14 +549,11 @@ 

License

};
-

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 裡面的 count 的值。因為 foo.hack 沒有在 那個 作用域內被宣告。它只有會覆蓋或者建立在一個 全域 的變數 count

-

Closures Inside Loops

+

在循環內的 Closures

-

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

+

一個常見的錯誤就是在 Closures 中使用迴圈,假設我們要使用每次迴圈中所使用的進入變數

for(var i = 0; i < 10; i++) {
     setTimeout(function() {
@@ -581,20 +562,14 @@ 

License

}
-

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

- -

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

+

在上面的例子中它 不會 輸出數字從 09,但只會出現數字 10 十次。 +在 console.log 被呼叫的時候,這個 匿名 函數中保持一個 參考 到 i ,此時 for迴圈已經結束, i 的值被修改成了 10。 +為了要達到想要的結果,需要在每次創造 副本 來儲存 i 的變數。

-

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) {
@@ -605,15 +580,9 @@ 

License

}
-

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

- -

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

- -

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

+

匿名外部的函數被呼叫,並把 i 作為它第一個參數,此時函數內 e 變數就擁有了一個 i 的拷貝。 +當傳遞給 setTimeout 這個匿名函數執行時,它就擁有了對 e 的引用,而這個值 不會 被循環改變。 +另外有一個方法也可以完成這樣的工作,那就是在匿名函數中返回一個函數,這和上面的程式碼有同樣的效果。

for(var i = 0; i < 10; i++) {
     setTimeout((function(e) {
@@ -724,16 +693,13 @@ 

License

強烈建議不要使用 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.

+

建構函式

JavaScript 中的建構函式和其他語言中的建構函式是不同的。 +用 new 的關鍵字方式調用的函式都被認為是建構函式。 +在建構函式內部 - 被呼叫的函式 - this 指向一個新建立的 objectprototype 這是一個新的物件一個被指向函式的 prototype 的建構函式。

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

+implicitly returns the value of this - the new object. +如果被使用的函式沒有明顯的呼叫 return 的表達式,它會回傳一個隱性的 this 的新物件。

function Foo() {
     this.bla = 1;
@@ -746,16 +712,13 @@ 

License

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 by that statement, but only if the return value is an Object.

+

在上面的例子中 Foo 建立一個建構函式,並設立一個 prototype 來創建一個新的物件叫 Foo.prototype。 +這個情況下它顯示的 return 一個表達式,但他 返回一個 Object

function Bar() {
     return 2;
 }
-new Bar(); // a new object
+new Bar(); // 返回一個新物件
 
 function Test() {
     this.value = 2;
@@ -764,25 +727,22 @@ 

License

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 in JavaScript, it will use the -global object as the value of this.

+

雖然上面有些情況也能正常運行,但是由於 JavaScript 中 this 的工作原理,這裡的 this 指向 全域對象

-

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;
@@ -800,24 +760,16 @@ 

License

Bar();
-

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

+

上面兩個呼叫 Bar 的方法回傳的值都一樣,一個新創建的擁有 method 屬性被返回,這裡創建了一個 Closure.

-

It should also be noted 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 沒有把這個新物件返回。 +在上面的例子中,使用或者不使用 new 關鍵字沒有什麼功能性的區別

-

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

+

通過工廠模式創建的新對象

-

Creating New Objects via Factories

- -

It is often recommended to not use new because forgetting its use may -lead to bugs.

- -

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

+

常聽到建議 不要 使用 new,因為如果忘記如何使用它會造成錯誤。 +為了創建一個新的物件,我們可以用工廠方法,來創造一個新的物件在那個方法中。

function Foo() {
     var obj = {};
@@ -835,32 +787,23 @@ 

License

}
-

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 -is contrary to the spirit of the language.
  6. +
  7. 會占用更多的記憶體,因為創建的物件 沒有 辦法放在在同一個原型上。
  8. +
  9. 為了要用繼承的方式,工廠方法需要複製所有的屬性或是把一個物件作為新的物件的原型。
  10. +
  11. 放棄原型鏈僅僅是因為防止遺漏 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 use it -consistently.

Scopes and Namespaces

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

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

雖然遺漏 new 關鍵字可能會導致問題,但這並 不是 放棄只用原型的藉口。 +最終使用哪種方式取決於應用程式的需求,選擇一種程式語言風格並堅持下去才是最重要的。

作用域和命名空間

儘管 JavaScript 支持一個大括號創建的程式碼,但並不支持塊級作用域。 +而僅僅支援 函式作用域

+ +
function test() { // 一個作用域
+    for(var i = 0; i < 10; i++) { // 不是一個作用域
+        // 算數
     }
     console.log(i); // 10
 }
@@ -880,7 +823,7 @@ 

License

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';
@@ -1093,14 +1036,14 @@ 

License

// and so on...
-

In Conclusion

+

結語

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

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

Arrays

Array 迴圈和屬性

雖然在 Javascript 中 Array 都是 Objects,但是沒有好的理由要使用他 +use of them indicates badly written code that is prone to errors and hard to maintain.

陣列

Array 迴圈和屬性

雖然在 Javascript 中 Array 都是 Objects,但是沒有好的理由要使用他 在 for in 的迴圈中。事實上有很多原因要避免使用 for in 在 Array 之中

結語

-

Array 的建構函式需要避免,建議使用字面語法。因為他們比較簡短、也更增加閱讀性

Types

Equality and Comparisons

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

+

Array 的建構函式需要避免,建議使用字面語法。因為他們比較簡短、也更增加閱讀性

類型

Equality and Comparisons

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

The Equality Operator

@@ -1440,7 +1383,7 @@

License

!!'-1' // true !!{}; // true !!true; // true -

Core

為什麼不要使用 eval

因為 eval 函數會在 Javascript 的區域性的區間執行那段程式碼。

+

核心

為什麼不要使用 eval

因為 eval 函數會在 Javascript 的區域性的區間執行那段程式碼。

var foo = 1;
 function test() {
@@ -1728,7 +1671,7 @@ 

License

In conclusion

The delete operator often has unexpected behaviour and can only be safely -used to delete explicitly set properties on normal objects.

Other

setTimeout and setInterval

Since JavaScript is asynchronous, it is possible to schedule the execution of a +used to delete explicitly set properties on normal objects.

其他

setTimeout and setInterval

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