-
Notifications
You must be signed in to change notification settings - Fork 557
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #108 from dogfeet/ko
Korean Translation
- Loading branch information
Showing
24 changed files
with
1,583 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
## `Array` 생성자 | ||
|
||
`Array` 생성자가 파라미터를 처리하는 방법은 모호하기 때문에 항상 `[]` 노테이션으로 Array를 만들어야 한다. | ||
|
||
[1, 2, 3]; // Result: [1, 2, 3] | ||
new Array(1, 2, 3); // Result: [1, 2, 3] | ||
|
||
[3]; // Result: [3] | ||
new Array(3); // Result: [] | ||
new Array('3') // Result: ['3'] | ||
|
||
`Array` 생성자에 인자로 숫자 하나를 넘기면 생성자는 `length`가 그 숫자인 텅 빈 `Array` 하나를 반환한다. 생성자는 **오직** `length` 프로퍼티에 할당하기만 하고 실제 `Array`는 초기화하지 않는다는 것을 기억해야 한다. | ||
|
||
var arr = new Array(3); | ||
arr[1]; // undefined | ||
1 in arr; // false, 이 인덱스는 초기화되지 않음. | ||
|
||
Array의 length 프로퍼티에 숫자를 할당해주는 이 기능이 유용할 때도 있긴 있다. `for loop`을 사용하지 않고 스트링을 더하는 경우가 그렇다. | ||
|
||
new Array(count + 1).join(stringToRepeat); | ||
|
||
### 결론 | ||
|
||
`Array` 생성자는 가능하면 사용하지 말아야 한다. `[]` 노테이션이 더 알맞다. 더 간략하고 명확하기 때문에 보기도 좋다. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
## Array Iteration과 프로퍼티 | ||
|
||
JavaScript에서는 Array도 객체지만 Iterate를 할 때 [`for in`](#object.forinloop)을 사용해서 좋을 게 없다. | ||
|
||
> **Note:** JavaScript의 Array는 *Associative Array*가 **아니다**. JavaScript [객체](#object.general)는 key/value만 mapping 할 뿐이다. Associative Array는 순서를 보장하지만, 객체는 보장하지 않는다. | ||
`for in`은 프로토타입 체인에 있는 프로퍼티를 모두 훑는(enumerate) 데다가 객체 자신의 프로퍼티만 훑으려면 [`hasOwnProperty`](#object.hasownproperty)를 사용해야 하기 때문에 `for`보다 20배 느리다. | ||
|
||
### Iteration | ||
|
||
Array를 Iterate 할 때에는 구식인 `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 값을 캐시 했다는 것이다. | ||
|
||
Array에 있는 `length` 프로퍼티를 iterate마다 사용하는 것은 좀 부담스럽다. 최신 JavaScript 엔진은 이 일을 알아서 처리하기도 하지만 코드가 새 엔진에서 실행되도록 보장할 방법이 없다. | ||
|
||
실제로 캐시 하지 않으면 성능이 반으로 줄어든다. | ||
|
||
### `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; // [1, 2, 3] | ||
|
||
현재 크기보다 더 작은 값을 할당하면 Array를 자르지만, 현재 크기보다 더 큰 값을 할당하면 아무것도 하지 않는다. | ||
|
||
### 결론 | ||
|
||
최적의 성능을 위해서 `for`를 사용하고 `length` 프로퍼티 값을 캐시 하길 바란다. Array에 `for in`을 사용하면 성능도 떨어지고 버그 나기도 쉽다. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
## `delete` | ||
|
||
간단히 말해서 global 변수, 함수, 등은 `DontDelete` 속성이기 때문에 삭제 못 한다. | ||
|
||
### Global 코드와 함수 코드 | ||
|
||
Global이나 Function scope에 정의된 함수나 변수는 모두 Activation 객체나 Global 객체의 프로퍼티다. 이 프로퍼티는 모두 `DontDelete`속성을 가진다. Global이나 Function 코드에서 변수나 함수의 정의하면 항상 `DontDelete` 프로퍼티로 만들어진다. 그러니까 삭제할 수 없다: | ||
|
||
// Global 변수: | ||
var a = 1; // DontDelete가 설정된다. | ||
delete a; // false | ||
a; // 1 | ||
|
||
// 함수: | ||
function f() {} // DontDelete가 설정된다. | ||
delete f; // false | ||
typeof f; // "function" | ||
|
||
// 다시 할당해도 삭제할 수 없다: | ||
f = 1; | ||
delete f; // false | ||
f; // 1 | ||
|
||
### Explicit 프로퍼티 | ||
|
||
다음 예제에서 만드는 property는 정상적으로 지워진다. 이런 걸 Explicit 프로퍼티라고 부른다: | ||
|
||
// Explicit 프로퍼티를 만든다: | ||
var obj = {x: 1}; | ||
obj.y = 2; | ||
delete obj.x; // true | ||
delete obj.y; // true | ||
obj.x; // undefined | ||
obj.y; // undefined | ||
|
||
`obj.x`와 `obj.y`는 `DontDelete` 속성이 아녀서 삭제된다. 그러나 다음과 같은 코드도 잘 동작하기 때문에 헷갈린다.: | ||
|
||
// IE를 빼고 잘 동작한다.: | ||
var GLOBAL_OBJECT = this; | ||
GLOBAL_OBJECT.a = 1; | ||
a === GLOBAL_OBJECT.a; // true - 진짜 Global 변순지 확인하는 것 | ||
delete GLOBAL_OBJECT.a; // true | ||
GLOBAL_OBJECT.a; // undefined | ||
|
||
[`this`](#function.this)가 Global 객체를 가리키는 것을 이용해서 명시적으로 프로퍼티 `a`를 선언하면 삭제할 수 있다. 이런 꼼수가 가능하다. | ||
|
||
IE (적어도 6-8)는 버그가 있어서 안 된다. | ||
|
||
### arguments와 함수의 기본 프로퍼티 | ||
|
||
함수의 [`arguments` 객체](#function.arguments)와 built-in 프로퍼티도 `DontDelete` 속성이다. | ||
|
||
// 함수의 arguments와 프로퍼티: | ||
(function (x) { | ||
|
||
delete arguments; // false | ||
typeof arguments; // "object" | ||
delete x; // false | ||
x; // 1 | ||
function f(){} | ||
delete f.length; // false | ||
typeof f.length; // "number" | ||
})(1); | ||
|
||
### Host 객체 | ||
|
||
(역주, Host 객체들은 document같은 DOM 객체를 말한다.) | ||
|
||
Host 객체를 delete하면 어떻게 될지 알 수 없다. 어떻게 Host 객체를 delete해야 하는지 표준에 정의되지 않았다. | ||
|
||
### 결론 | ||
|
||
`delete` 연산자는 엉뚱하게 동작할 때가 잦다. 명시적으로 정의한 일반 객체의 프로퍼티만 delete하는 것이 안전하다. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
## 왜 `eval`을 사용하면 안 될까? | ||
|
||
`eval` 함수는 스트링으로 된 JavaScript 코드를 Local Scope에서 실행한다. | ||
|
||
var foo = 1; | ||
function test() { | ||
var foo = 2; | ||
eval('foo = 3'); | ||
return foo; | ||
} | ||
test(); // 3 | ||
foo; // 1 | ||
|
||
`eval`을 `eval`이라는 이름으로 **직접** 직행할 때에만 Local Scope에서 실행된다. | ||
|
||
var foo = 1; | ||
function test() { | ||
var foo = 2; | ||
var bar = eval; | ||
bar('foo = 3'); | ||
return foo; | ||
} | ||
test(); // 2 | ||
foo; // 3 | ||
|
||
어쨌든 `eval`은 사용하지 말아야 한다. eval을 사용하는 경우의 99.9%는 사실 eval이 필요 없다. | ||
|
||
### 가짜 `eval` | ||
|
||
[timeout functions](#other.timeouts)인 `setTimeout`과 `setInterval`은 첫 번째 인자로 스트링을 입력받을 수 있다. 이 경우에는 `eval`을 직접 호출하는 것이 아녀서 항상 global scope에서 실행된다. | ||
|
||
### 보안 이슈 | ||
|
||
`eval`은 보안 문제도 있다. 단순히 **모든** 코드를 실행하기 때문에 신뢰하지 못하는 코드가 **절대로** 포함되지 않도록 주의해야 한다. | ||
|
||
### 결론 | ||
|
||
`eval`은 사용하지 않는 게 좋다. `eval`을 사용하는 모든 코드는 성능, 보안, 버그의 문제를 일으킬 수 있다. 만약 `eval`이 필요해지면 *설계를 변경*하여 `eval`이 필요 없게 만들어야 한다. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
## 세미콜론을 자동으로 삽입해준다. | ||
|
||
JavaScript는 C와 문법이 비슷하지만, 꼭 코드에 semicolon을 사용하도록 강제하지 않는다. 그래서 생략할 수 있다. | ||
|
||
사실 JavaScript는 semicolon이 꼭 있어야 하고 없으면 이해하지 못한다. 그래서 JavaScript 파서는 semicolon이 없으면 **자동으로** semicolon을 추가한다. | ||
|
||
var foo = function() { | ||
} // 세미콜론이 없으니 에러 난다. | ||
test() | ||
|
||
파서는 세미콜론을 삽입하고 다시 시도한다. | ||
|
||
var foo = function() { | ||
}; // 에러가 없어짐. | ||
test() | ||
|
||
JavaScript에서 세미콜론을 자동으로 삽입한 것은 **대표적인** 설계 오류 중 하나다. 세미콜론 유무에 따라 *전혀* 다른 코드가 될 수 있다. | ||
|
||
### 어떻게 다를까? | ||
|
||
코드에 세미콜론이 없으면 파서가 어디에 넣을지 결정한다. | ||
|
||
(function(window, undefined) { | ||
function test(options) { | ||
log('testing!') | ||
|
||
(options.list || []).forEach(function(i) { | ||
|
||
}) | ||
|
||
options.value.test( | ||
'long string to pass here', | ||
'and another long string to pass' | ||
) | ||
|
||
return | ||
{ | ||
foo: function() {} | ||
} | ||
} | ||
window.test = test | ||
|
||
})(window) | ||
|
||
(function(window) { | ||
window.someLibrary = {} | ||
|
||
})(window) | ||
|
||
파서는 다음과 같이 삽입한다. | ||
|
||
(function(window, undefined) { | ||
function test(options) { | ||
|
||
// 세미콜론을 넣는 것이 아니라 줄을 합친다. | ||
log('testing!')(options.list || []).forEach(function(i) { | ||
|
||
}); // <- 여기 | ||
|
||
options.value.test( | ||
'long string to pass here', | ||
'and another long string to pass' | ||
); // <- 여기 | ||
|
||
return; // <- 여기에 넣어서 그냥 반환시킨다. | ||
{ // 파서는 단순 블럭이라고 생각하고 | ||
|
||
// 단순한 레이블과 함수 | ||
foo: function() {} | ||
}; // <- 여기 | ||
} | ||
window.test = test; // <- 여기 | ||
|
||
// 이 줄도 합쳐진다. | ||
})(window)(function(window) { | ||
window.someLibrary = {}; // <- 여기 | ||
|
||
})(window); //<- 여기에 파서는 세미콜론을 넣는다. | ||
|
||
> **주의:** JavaScript 파서는 new line 문자가 뒤따라 오는 return 구문을 제대로 처리하지 못한다. 자동으로 세미콜론을 넣는 것 자체의 문제는 아니지만 어쨌든 여전히 문제로 남아있다. | ||
파서는 완전히 다른 코드로 만들어 버린다. 이것은 **오류**다. | ||
|
||
### Parenthesis | ||
|
||
세미콜론 없이 괄호가 붙어 있으면 파서는 세미콜론을 넣지 않는다. | ||
|
||
log('testing!') | ||
(options.list || []).forEach(function(i) {}) | ||
|
||
파서는 다음과 같이 코드를 바꾼다. | ||
|
||
log('testing!')(options.list || []).forEach(function(i) {}) | ||
|
||
`log` 함수가 함수를 반환할 가능성은 거의 없다. 아마도 `undefined is not a function`이라는 `TypeError`가 발생할 거다. | ||
|
||
### 결론 | ||
|
||
세미콜론은 반드시 사용해야 한다. 그리고 `{}`도 생략하지 않고 꼭 사용하는 것이 좋다. 한 줄밖에 안 되는 `if` / `else` 블럭에서도 꼭 사용해야 한다. 이 두 가지 규칙을 잘 지키면 JavaScript 파서가 잘못 해석하는 일을 미리 방지하고 코드도 튼튼해진다. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
## `undefined`와 `null` | ||
|
||
JavaScript는 `nothing`을 두 가지로 표현할 수 있고 그중 `undefined`가 더 유용하다. | ||
|
||
### `undefined`도 변수 | ||
|
||
`undefined`는 `undefined`라는 값을 가지는 데이터 형식이다. | ||
|
||
`undefined`는 상수도 아니고 JavaScript의 키워드도 아니다. 그냥 `undefined`라는 이름의 Global 변수이고 이 변수에는 `undefined`라고 할당돼 있다. 그래서 이 Global 변수의 값을 쉽게 바꿀 수 있다. | ||
|
||
> **ES5 Note:** ECMAScript 5의 strict 모드에서는 `undefined`를 더는 바꿀 수 없도록 했다. 하지만 `undefined`라는 함수를 만들면 여전히 할당할 수 있다. | ||
`undefined` 값이 반환될 때: | ||
|
||
- global 변수 `undefined`에 접근할 때. | ||
- `return` 구문이 없는 함수는 `undefined`를 반환함. | ||
- `return` 구문으로 아무것도 반환하지 않을 때. | ||
- 없는 프로퍼티를 찾을 때. | ||
- 함수 인자가 생략될 때. | ||
- `undefined`가 할당된 모든 것. | ||
|
||
### `undefined`가 바뀔 때를 대비하기 | ||
|
||
global 변수 `undefined`는 `undefined`라는 객체를 가리키는 것뿐이기 때문에 새로운 값을 할당한다고 해도 `undefined`의 값 자체가 바뀌는 것이 아니다. | ||
|
||
그래서 `undefined`와 비교하려면 먼저 `undefined`의 값을 찾아와야 한다. | ||
|
||
보통 `undefined` 변수가 바뀌어 있을 때를 대비해서 undefined라는 변수를 인자로 받는 [anonymous wrapper](#function.scopes)로 감싸고 아무런 인자를 넘기지 않는 꼼수를 사용한다. | ||
|
||
var undefined = 123; | ||
(function(something, foo, undefined) { | ||
// Local Scope에 undefined를 만들어서 | ||
// 원래 값을 가리키도록 했다. | ||
|
||
})('Hello World', 42); | ||
|
||
wrapper 안에서 변수를 새로 정의하는 방법으로도 같은 효과를 볼 수 있다. | ||
|
||
var undefined = 123; | ||
(function(something, foo) { | ||
var undefined; | ||
... | ||
|
||
})('Hello World', 42); | ||
|
||
이 두 방법의 차이는 minified했을 때 4바이트만큼 차이 난다는 것과 한쪽은 wrapper 안에 var 구문이 없다는 것밖에 없다. | ||
|
||
### `Null` 객체의 용도 | ||
|
||
JavaScript 언어에서는 `undefined`를 다른 언어에서 *null*을 사용하듯이 쓰고 진짜 `null`은 그냥 다른 데이터 타입 중 하나일 뿐이다. | ||
|
||
JavaScript 내부적인 곳에 사용하는 경우가 아니면 null 대신 `undefined`를 사용해도 된다(`Foo.prototype = null`같이 프로토타입 체인을 끊을 때는 null을 사용한다). |
Oops, something went wrong.