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

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

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 Properites

JavaScript 에서는null과 undefined 이 두가지 예외를 제외한 모든 것이 객체처럼 동작합니다.Everything in JavaScript acts like an object, with the only two exceptions being null and undefined.

그것은 JavaScript 의 파서가 number 의 dot(.) 표기법을 부동소수점 리터럴로 파싱하려는 결함(?) 때문입니다.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

아래와 같이 number 리터럴을 객체처럼 사용하기 위한 몇가지 방법들이 있습니다.There are a couple of workarounds which can be used in order make number literals act as objects too.

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

Objects as a Data Type

JavaScript 의 객체는 Hashmap으로 사용될 수도 있는데, 그것은 주로 이름과 값이 매핑된 프로퍼티들로 이루어져 있습니다.Objects in JavaScript can also be used as a Hashmap, they mainly consist of named properties mapping to values.

객체 리터럴 -중괄호({}) 구문- 을 사용하여 일반적인 객체를 생성하는 것이 가능합니다.

이렇게 생성된 객체는 Object.prototype 으로부터 상속(inherits)되며 자신의 프로퍼티(own properties)가 없습니다.Using a object literal - {} notation - it is possible to create a plain object. This new object inherits from Object.prototype and has no own properties defined on it.

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

Accessing Properties

객체의 프로퍼티는 두가지 방법, 즉 dot(.) 표기법이나 대괄호([]) 표기법을 사용하여 접근할 수 있습니다.The properties of an object can be accessed in two ways, via either the dot notation, or the square bracket notation.

dot(.) 표기법을 사용하여 프로퍼티를 동적으로 할당하려 할 경우는 문법오류(Syntax Error)가 발생합니다.Both notations are identical in their workings, with the only difference being that the square bracket notation allows for dynamic setting of properties, as well as the use of property names that would otherwise lead to a syntax error.

Deleting Properties

객체에서 프로퍼티를 실제 삭제하는 유일한 방법은 delete 연산자를 사용하는 것입니다.

프로퍼티에 undefined 혹은 null 을 셋팅하는 것은 프로퍼티 자체를 삭제하는 것이 아니라 단지 프로퍼티에 할당된 값을 삭제하는 것일 뿐입니다.The only way to actually remove a property from an object is to use the delete operator; setting the property to undefined or null only remove the value associated with the property, but not the key.

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

객체의 프로퍼티는 순수 문자나 문자열로 표기할 수 있습니다.

JavaScript 파서의 잘못된 설계때문에 위 코드는 ECMAScript 5 이전에서는 문법오류(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.

delete 가 키워드라서 발생하는 에러이기 때문에 이전 JavaScript 엔진에서 정확히 해석되게 하려면 문자열 리터럴로 표기해야만 한다.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.

이것은 JavaScript 의 약점 중 하나라고 자주 언급되지만 사실은 프로토타입 기반의 상속 모델이 클래스 기반의 상속 모델보다 더 강력합니다. 예를 들면 코드 상단에 클래스 기반의 모델을 구현하는 것은 별거 아니지만 그 반대로는 훨씬 더 어려운 작업이 됩니다.While this is often considered to be one of JavaScript's weaknesses, the prototypal inheritance model is in fact more powerful than the classic model. It is for example fairly trivial to build a classic model on top of it, while the other way around is a far more difficult task.

프로토타입 기반의 상속이라는 특징을 가진 언어들 중에 JavaScript 가 유일하게 널리 사용된 언어이기 때문에 두 가지 모델 사이의 차이점에 적응하는 것은 시간이 다소 걸립니다.Due to the fact that JavaScript is basically the only widely used language that features prototypal inheritance, it takes some time to adjust to the differences between the two models.

우선 가장 큰 차이점은 JavaScript 는 프로토타입 체인(prototype chains)이라는 것을 사용하여 상속을 한다는 것입니다.The first major difference is that inheritance in JavaScript is done by using so called prototype chains.

요컨대 모든 Bar 인스턴스는 같은 value 프로퍼티를 공유한다는 것입니다.In the above, the object test will inherit from both Bar.prototype and Foo.prototype; hence, it will have access to the function method that was defined on Foo. It will also have access to the property value of the one Foo instance that is its prototype. It is important to note that new Bar() does not create a new Foo instance, but reuses the one assigned to its prototype; thus, all Bar instances will share the same value property.

Object.prototype 이라는 체인의 가장 상단까지 가서도 해당 프로퍼티를 찾지 못하면 대신에 undefined 를 반환하게 됩니다.When it reaches the top of the chain - namely Object.prototype - and still hasn't found the specified property, it will return the value undefined instead.

The Prototype Property

prototype 프로퍼티가 프로토타입 체인을 구현하기 위해 언어 차원에서 사용이 되고는 있지만 그것에 다른 어떤 값을 할당하는 것도 가능은 합니다. 다만 원시타입(primitives)이 할당될 경우에는 단순히 무시해 버립니다.While the prototype property is used by the language to build the prototype chains, it is still possible to assign any given value to it. Although primitives will simply get ignored when assigned as a prototype.

또한, 객체의 프로퍼티들을 반복(iterating)하여 접근할 경우(for 구문 등을 통해)에는 프로토타입 체인 상에 있는 모든 프로퍼티가 열거되게 됩니다.Also, when iterating over the properties of an object every property that is on the prototype chain will get enumerated.

Extension of Native Prototypes

종종 사용되는 한가지 잘못된 특성은 Object.prototype또는 이미 프로토타입에 내장되어 있는 것을 확장한다는 것입니다.One mis-feature that is often used is to extend Object.prototype or one of the other built in prototypes.

나아가서, JavaScript 의 새로운 특성과의 호환성을 목적으로 하지 않는 한 원시(native) 프로토타입은 확장하지 않아야 합니다.It is a must to understand the prototypal inheritance model completely before writing complex code which makes use of it. Also, watch the length of the prototype chains and break them up if necessary to avoid possible performance issues. Further, the native prototypes should never be extended unless it is for the sake of compatibility with newer JavaScript features.

hasOwnProperty

프로토타입 체인(prototype chain) 상에 있는 것이 아닌 해당 객체 자체에 정의되어 있는 프로퍼티인지를 체크하기 위해서는 Object.prototype으로부터 상속받아 모든 객체가 가지고 있는 hasOwnProperty 메소드를 사용해야만 합니다.In order to check whether a object has a property defined on itself and not somewhere on its prototype chain, it is necessary to use the hasOwnProperty method which all objects inherit from Object.prototype.

hasOwnProperty 는 JavaScript 에서 프로토타입 체인을 순회하지 않으면서 프로퍼티를 다루는 유일한 방법입니다.hasOwnProperty is the only thing in JavaScript which deals with properties and does not traverse the 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

JavaScript 에서는 hasOwnProperty 라는 프로퍼티의 이름을 보호하지는 않습니다. 그래서 그 이름의 프로퍼티를 가지는 객체가 존재할 가능성도 있습니다. 그럴 경우에는 정확한 결과를 얻기 위해 외부의(external) hasOwnProperty 를 사용해야 할 필요가 있습니다.JavaScript does not protect the property name hasOwnProperty; thus, if the possibility exists that an object might have a property with this name, it is necessary to use an external hasOwnProperty in order to get correct results.

또한, 모든for in loop에서 hasOwnProperty 를 사용할 것을 권장드리는데 이것은 원시(native) 프로토타입(prototypes)을 확장함으로써 발생하는 오류를 피할 수 있을 것입니다.When checking for the existence of a property on a object, hasOwnProperty is the only method of doing so. It is also recommended to make hasOwnProperty part of every for in loop, this will avoid errors from extended native prototypes.

The for in Loop

in 연산자와 마찬가지로 for in loop 역시 객체의 프로퍼티들를 반복하여 다루는 경우 프로토타입 체인을 순회하게 됩니다.Just like the in operator, the for in loop also traverses the prototype chain when iterating over the properties of an object.

for in loop 자체의 기능을 수정할 수는 없기 때문에 loop 의 안에서 원하지 않는 프로퍼티를 걸러줘야 하는데 이것은 Object.prototype 의 hasOwnProperty메소드를 사용하면 됩니다.Since it is not possible to change the behavior of the for in loop itself, it is necessary to filter out the unwanted properties inside the loop body , this is done by using the hasOwnProperty method of Object.prototype.

Using hasOwnProperty for Filtering

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

이렇게 사용해야지만이 정확한 결과를 얻을 수 있습니다. hasOwnProperty 를 사용했기 때문에 moo 만 출력이 될 것입니다. hasOwnProperty 를 사용하지 않으면 원시(native) 프로토타입- e.g. Object.prototype - 이 확장된 경우 코드에서 에러가 발생하기 쉽습니다.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.

널리 사용되는 프레임워크인 Prototype 이 이렇게 동작을 합니다. 이 프레임워크를 사용하면 hasOwnProperty 를 사용하지 않는 for in loop 에서 break(?)를 보장합니다.One widely used framework which does this is Prototype. When this framework is included, for in loops that do not use hasOwnProperty are guaranteed to break.

In Conclusion

항상 hasOwnProperty 를 사용하기를 권장합니다.

원시(native) 프로토타입의 확장 여부와 상관 없이 코드가 운영이 되는 환경에 대해 어떠한 가정도 해서는 안됩니다.It is recommended to always use hasOwnProperty. Never should any assumptions be made about the environment the code is running in, or whether the native prototypes have been extended or not.

Functions

Function Declarations and Expressions

JavaScript 에서 함수(function)는 일급객체(first class object)입니다. 그것은 일반적인 다른 값들처럼 주변으로 전달이 될 수 있다는 것을 의미합니다. 이러한 특성이 주로 사용되는 것은 비동기(asynchronous) 함수와 같은 다른 함수에 콜백(callback)함수로써 익명(anonymous) 함수를 전달하는 것입니다.Functions in JavaScript are first class objects. That means they can be passed around like any other value. One common use of this feature is to pass an anonymous function as a callback to another, possibly asynchronous function.

The function Declaration

function foo() {}

위 함수는 프로그램의 시작부가 실행되기 전에 위로 올려집니다(hoisted). 그래서, 함수가 선언되어 있는 범위(scope) 안에서는 어디서든지 접근이 가능하며, 심지어 소스에 실제 정의되기 전에 호출하는 것도 가능합니다.The above function gets hoisted before the execution of the program starts; thus, it is available everywhere in the scope it was defined in, even if called before the actual definition in the source.

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

foo 는 스크립트가 실행될때 이미 선언이 된 것입니다.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.

여기서 bar 는 foo 에 할당되어 있는 함수이기 때문에 바깥쪽 범위에서는 접근할 수 없게 됩니다. 그러나, bar 안에서는 유효하며 이것이 JavaScript 에서 이름을 판단(name resolution) 하는 방법으로 함수의 이름은 함수 자신의 지역 범위 안에서는 항상 접근이 가능합니다.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 에서는 this 라는 이 특별한 변수가 무엇을 참조하고 있는지에 대해 대부분의 다른 프로그래밍 언어와는 다른 개념을 가지고 있습니다. this 의 값이 바운딩되는 방법은 정확하게 다섯가지 방법이 있습니다.JavaScript has a different concept of what the special name this refers to than most other programming languages do. There are exactly five different ways in which the value of this can be bound in the language.

The Global Scope

this;

전역 범위에서 this 가 사용되면 전역 객체를 참조하게 됩니다.When using this in global scope, it will simply refer to the global object.

Calling a Function

foo();

여기에서도 this 는 적역 객체를 참조하게 됩니다.Here this will again refer to the global object.

Calling a Method

test.foo();

이 예제에서 this 는 test 를 참조하게 됩니다.In this example this will refer to test.

Calling a Constructor

new foo();

new 키워드가 선행되어 함수가 호출되면 생성자(constructor)로써 동작을 하게 됩니다. 그 함수 안에서의 this 는 새롭게 생성된 객체(Object)를 참조하게 됩니다.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.

해당 함수 호출 시의 첫번째 인수로 명시적으로 설정됩니다.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.

결국 위 예제는 메소드의 경우(method case, 위의 다섯가지 중 Calling a Method)가 적용되지 않고 foo 안에서의 this 로 bar 가 설정되게 됩니다.As a result, the above example the method case does not apply, and this inside of foo will be set to bar.

Common Pitfalls

이러한 경우들 대부분은 이해가 가지만 첫번째(The Global Scope)는 전혀 실용성이 없는 언어의 잘못된 설계라고 생각이 듭니다.While most of these cases make sense, the first one is to be considered another mis-design of the language, as it never has any practical use.

또한, this 의 값을 주변에 전달하기 위해 클로저(closure)와 함께 사용될 수도 있습니다.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.

이 두 가지 함수 모두 Counter 의 범위에 있는 참조를 유지하고 있으며 따라서 그 범위 안에서 선언된 count 변수에 항상 접근이 가능합니다.Here, Counter returns two closures. The function increment as well as the function get. Both of these functions keep a reference to the scope of Counter and, therefore, always keep access to the count variable that was defined in that very scope.

Why Private Variables Work

JavaScript 에서는 범위에 대한 참조나 할당이 불가능하기 때문에 바깥 범위에서 count 변수를 접근할 수 있는 방법이 없습니다. 오직 두개의 클로저를 통해서만이 접근이 가능합니다.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.

setTimeout 안의 익명 함수는 i 의 참조를 유지하고 있으며 console.log 가 호출되는 순간에는 이미 for loop 가 종료가 된 상태입니다. 그 때 i 의 값은 10으로 할당되어 있는 상태입니다.The anonymous function keeps a reference to i and at the time console.log gets called, the for loop has already finished and the value of i as been set to 10.

원하는 결과를 얻기 위해서는 i 의 값의 복사본을 생성해야만 합니다.In order to get the desired behavior, it is necessary to create a copy of the value of i.

Avoiding the Reference Problem

loop 의 인덱스 변수의 값을 복사하기 위해서는 익명의 랩퍼(anonymous wrapper)를 사용하는 것이 가장 좋습니다.In order to copy the value of the loop's index variable, it is best to use an anonymous wrapper.

바깥쪽의 익명 함수가 i 를 첫번째 인수로 하여 바로 즉시 호출이 되면 매개변수 e 에 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.

또 다른 방법은 익명의 랩퍼(anonymous wrapper)로부터 함수를 반환받는 것입니다. 이것은 위 코드와 동일한 기능을 수행하게 될 것입니다.There is another possible way of achieving this; that is to return a function from the anonymous wrapper, that will then have the same behavior as the code above.

arguments 객체는 배열(Array)은 아닙니다. length 라는 이름의 프로퍼티도 있는 것처럼 어느정도 배열의 의미를 가지고는 있지만 Array.prototype 을 상속받지 않는 사실은 객체(Object)일 뿐입니다.The arguments object is not an Array. While it has some of the semantics of an array - namely the length property - it does not inherit from Array.prototype and is in fact an Object.

그렇기 때문에 arguments 에는 push, pop, slice 와 같은 배열의 표준 메소드를 사용할 수 없는 것입니다. 보통의 for loop 를 사용하여 열거(반복)는 잘 되겠지만 배열(Array)의 표준 메소드를 사용하기 위해서는 실제 배열(Array)로 변환해야만 합니다.Due to this, it is not possible to use standard array methods like push, pop or slice on arguments. While iteration with a plain for loop works just fine, it is necessary to convert it to a real Array in order to use the standard Array methods on it.

결국 다시 말해서 공식적인 매개변수의 값이 변경되면 arguments 객체의 해당 프로퍼티의 값도 변경이 된다는 것입니다.As a result, changing the value of a formal parameter will also change the value of the corresponding property on the arguments object, and the other way around.

arguments 객체는 "arguments"를 함수 안에서 변수명으로 사용을 하거나 함수의 공식적인 매개변수명으로 사용하는 경우에만 예외를 발생시킵니다. 사용을 하든지 안 하든지는 문제가 되지 않습니다.The arguments object is always created with the only two exceptions being the cases where it is declared as a name inside of a function or one of its formal parameters. It does not matter whether it is used or not.

그러나, 최근의 JavaScript 엔진에서는 성능이 급격하게 감소되는 경우가 하나 있는데 바로 arguments.callee 를 사용하는 경우입니다.However, there is one case which will drastically reduce the performance in modern JavaScript engines. That case is the use of arguments.callee.

이것은 단지 inlining 으로부터 얻을 수 있는 성능상의 이점을 얻을 수 없게 하는 것 뿐만이 아니라 함수가 명시적인 호출문맥(calling context)상에 종속되어 버리기 때문에 캡슐화(encapsulation)에도 어긋나게 됩니다.In the above code, foo can no longer be a subject to inlining since it needs to know about both itself and its caller. This not only defeats possible performance gains that would arise from inlining, it also breaks encapsulation since the function may now be dependent on a specific calling context.

arguments.callee 나 그것의 어떠한 프로퍼티도 사용하지 말 것을 강력히 권장합니다.It is highly recommended to never make use of arguments.callee or any of its properties.

Constructors

JavaScript 에서 생성자(constructor)는 다른 많은 언어들과는 조금 다릅니다. new 키워드를 앞에 두고 어떤 함수를 호출하면 그 함수는 생성자로써 실행이 됩니다.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.

생성자를 호출하면 그 안의 this 의 값은 새로 생성된 객체(Object)를 참조하게 됩니다. 이 새로 생성된 객체의 prototype에는 생성자로써 호출된 함수 객체의 prototype 이 할당됩니다.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.

호출된 함수가 명시적으로 return 구문을 사용하지 않았다면 암묵적으로 새로 생성된 객체인 this 를 반환하게 됩니다.If the function that was called has no explicit return statement, then it implicitly returns the value of this - the new object.

위 예제의 경우 정상적으로 수행은 되지만 this 의 값이 전역 객체가 됩니다.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

new 키워드를 빠뜨릴 가능성에 대비하기 위해서는 생성자 함수에서 값을 명시적으로 반환해줘야 합니다.In order to be able to omit the new keyword, the constructor function has to explicitly return a value.

또한 주목해야 할 것은 new Bar() 의 호출은 반환되는 객체의 프로토타입에 영향을 끼치지 않는다는 것입니다. 그 프로토타입은 새로 생성된 객체에 할당될 것입니다. Bar 는 그 새로운 객체를 반환하는 것이 아닙니다.It is also to note that the call new Bar() does not affect the prototype of the returned object. While the prototype will be set on the newly created object, Bar never returns that new object.

위 예제에서는 new 키워드를 사용하든 안하든 기능적으로 차이가 없습니다.In the above example, there is no functional difference between using and not using the new keyword.

Creating New Objects via Factories

자주 권장되는 것은 빠뜨리면 버그를 유발할 수 있는 new 를 사용하지 않는 것입니다.An often made recommendation is to not use new since forgetting its use may lead to bugs.

새로운 객체를 생성하기 위해서는 차라리 하나의 팩토리를 사용하여 그 팩토리 안에서 새로운 객체를 구성하라는 것입니다.In order to create new object, one should rather use a factory and construct a new object inside of that factory.

위 코드는 new 키워드의 실수에 대해 안전하고 private 변수의 사용을 확실히 쉽게 해주지만 반면에 몇가지 단점이 있습니다.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. 상속을 하려면 그 팩토리는 다른 객체의 모든 메소드를 복사하거나 새로운 객체의 프로토타입에 그 객체를 할당해줘야만 합니다.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. new 키워드를 빠뜨렸다고해서 프로토타입 체인이 끊어지게 되는 것은 어찌됐던간에 언어의 철학적인 면에도 위배됩니다.Dropping the prototype chain just because of a left out new keyword somehow goes against the spirit of the language.

In Conclusion

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 stick with it.

Scopes and Namespaces

비록 JavaScript 가 블럭을 지정하기 위해 중괄호 두개의 쌍을 문법으로 사용하고 있지만 블럭 범위를 지원하지는 않습니다. 함수 범위만을 지원하고 있습니다.Although JavaScript deals fine with the syntax of two matching curly braces for blocks, it does not support block scope; hence, all that is left is in the language is function scope.

변수는 매순간 참조가 되는데 JavaScript 는 그 변수를 찾을 때까지 현재 범위에서 상위 범위로 올라가면서 모든 범위를 순회합니다. 그러다가 마지막 전역 범위에서도 요청한 변수명을 찾지 못하면 ReferenceError 를 발생시킵니다.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'

위 두개의 스크립트는 같은 결과가 나오지 않습니다. 스크립트 A 는 전역 범위에 foo 라고 하는 변수를 선언하는 것이고

스크립트 B 는 현재 범위에 foo 를 선언하는 것입니다.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.

다시 말하지만 조금도 같은 결과가 나오지 않습니다. var 를 사용하지 않는 것은 중요한 의미를 내포할 수도 있는 것입니다.Again, that is not at all the same effect, not using var can have major implications.

JavaScript 는 전역 범위를 포함한 모든 범위에서 현재 객체를 참조하고 있는 특별한 이름의 this 를 가지고 있습니다.All scopes in JavaScript, including the global scope, have the special name this defined in them, which refers to the current object.

또한 함수 범위에는 그 함수에 전달되는 인수들을 포함하고 있는 arguments를 가지고 있습니다.Function scopes also have the name arguments defined in them which contains the arguments that were passed to a function.

예를 들어 함수 범위내에서 foo 라는 이름의 변수를 접근하려고 하면 JavaScript 는 다음 순서에 따라 그 이름을 찾게 됩니다.For example, when trying to access a variable named foo inside the scope of a function, JavaScript will lookup the name in the following order:

1. 현재 범위내에 var foo 구문이 있는 경우 그것을 사용합니다.In case there is a var foo statement in the current scope use that.

2. 함수의 매개변수 중 하나의 이름이 foo 라면 그것을 사용합니다.If one of the function parameters is named foo use that.

JavaScript 에서 배열은 객체이긴 하지만 그들을 열거하기 위해서 for in loop 를 사용해야 할 이유는 없습니다. 사실 for in 을 사용하지 말아야 할 많은 이유들이 있습니다.Although arrays in JavaScript are objects, there are no good reasons to use the for in loop in for iteration on them. In fact there are a number of good reasons against the use of for in on arrays.

for in loop 는 프로토타입 체인 상의 모든 프로퍼티를 열거하게 되며 그 프로퍼티를 제외할 수 있는 유일한 방법이 hasOwnProperty 을 사용하는 것이기 때문에 그것은 이미 일반적인 for loop 보다 스무배 정도는 더 늦게 됩니다.Since the for in loop enumerates all the properties that are on the prototype chain and the only way to exclude those properties is to use hasOwnProperty, it is already up to twenty times slower than a normal for loop.

Iteration

배열을 열거시킬때 가장 뛰어난 성능을 보이기 위해서 가장 좋은 방법은 전형적인 for loop 를 사용하는 것입니다.In order to achieve the best performance when iterating over arrays, it is best to use the classic for loop.

비록 length 가 배열 자체에 정의되어 있는 프로퍼티임에도 불구하고 loop 를 매번 반복할때는 검색을 수행하는데 overhead(성능상 비용)가 발생합니다. 물론 최근의 JavaScript 엔진의 경우 이런 경우 최적화를 시키긴 하지만 코드가 새로운 엔진에서 수행이 되는지 아닌지를 따질 것은 없습니다.Although the length property is defined on the array itself, there is still an overhead for doing the lookup on each iteration of the loop. And while recent JavaScript engines may apply optimization in this case, there is no way of telling whether the code will run on one of these newer engines or not.

사실 length 를 따로 저장을 하지 않으면 따로 저장하는 것에 비해서 속도가 반정도밖에는 나오지 않습니다.In fact, leaving out the caching may result in the loop being only half as fast as with the cached length.

The length Property

length 프로퍼티의 getter 는 단순히 배열이 포함하고 있는 요소의 갯수를 반환하는데 비해서 setter 는 배열을 비우는데에도 사용될 수가 있습니다.While the getter of the length property simply returns the number of elements that are contained in the array, the setter can be used to truncate the array.

length 에 작은 수를 할당하면 배열을 비울 수는 있지만 length 를 증가시켜도 그 배열에는 아무런 영향도 없습니다.Assigning a smaller length does truncate the array, but increasing the length does not have any effect on the array.

In Conclusion

최고의 성능을 내기 위해서는 항상 일반적인 for loop 를 사용하고 length 프로퍼티를 따로 저장하는 것을 권장합니다. 배열에서 for in 을 사용하는 것은 버그와 좋지 않은 성능을 유발하는 안좋은 코드를 작성했다는 신호입니다.For the best performance it is recommended to always use the plain for loop and cache the length property. The use of for in on an array is a sign of badly written code that is prone to bugs and bad performance.

배열(Array)의 생성자에 전달되는 인자가 하나뿐이고 그 인자가 숫자(Number)일 경우에는 그 생성자는 length 프러퍼티에 그 인자의 값을 할당한 비어있는 새 배열을 반환할 것입니다. 여기서 주목해야 할 점은 실제 배열의 색인값이 초기화되는 것이 아니라 단지 새 배열의 length 프로퍼티의 값만이 설정될 뿐이라는 것입니다.In cases when there is only one argument passed to the Array constructor, and that argument is a Number, the constructor will return a new sparse array with the length property set to the value of the argument. It should be noted that only the length property of the new array will be set this way, the actual indexes of the array will not be initialized.

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

배열의 초기 length 값을 설정하는 일은 for loop 코드를 사용하지 않고 한 문자열을 반복하는 일과 같은 몇가지의 경우에만 유용할 뿐입니다.The behavior of being able to set the length of the array upfront only comes in handy in a few cases, like repeating a string, in which it avoids the use of a for loop code.

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

In Conclusion

배열(Array)의 생성자를 사용하는 것은 가능한 피해야만 합니다. 리터럴을 사용하는 것이 확실히 더 좋은 방법입니다. 그것이 더 짧고 더 명확한 문법을 가지고 있습니다. 따라서, 그것은 코드의 가독성 또한 높이게 됩니다.The use of the Array constructor should be avoided as much as possible. Literals are definitely preferred. They are shorter and have a clearer syntax; therefore, they also increase the readability of the code.

게다가 실행중 형변환 시에는 성능에도 영향이 있습니다. 예를 들어 문자열은 다른 숫자와 비교되기 전에 숫자로 변환되어야만 합니다.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.

== 와 === 둘 다 동등연산자로써 명시적이긴 하지만 피연산자들중 하나라도 객체(Object)인 경우에는 다르게 수행을 합니다.While both == and === are stated as equality operators, they behave different when at least one of their operands happens to be an Object.

여기에서는 두개의 피연산자가 동일한지를 비교하는데 결과는 동일하지 않는 것으로 나옵니다. 그것은 그 객체의 같은 인스턴스인지를 비교하게 되는 것인데 마치 Python 의 is 나 C 의 포인터 비교와 같습니다.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

엄격한 동등연산자만을 사용할 것을 강력히 권장합니다. 강제 형변환이 필요한 곳인 경우라도 명시적으로(explicitly) 형변환을 해서 언어 자체의 복잡한 강제 변환 규칙을 남겨두어서는 안됩니다.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

typeof 연산자는(instanceof 와 함께) 아마 JavaScript 에서 가장 큰 설계적 결함일 것입니다. The typeof operator (together with instanceof) is probably the biggest design flaw of JavaScript, as it is near of being completely broken.

instanceof 는 아직 제한적이나마 사용을 하고 있지만 typeof 는 실제 사용하는 경우가 정말 한가지 뿐입니다. 그것은 객체의 타입이 체크가 되지 않는 경우입니다.Although instanceof still has its limited uses, typeof really has only one practical use case, which does not happen to be checking the type of an object.

객체의 타입을 체크하기 위해서는 Object.prototype.toString 을 사용할 것을 강력히 권장합니다. 이것이 신뢰할 수 있는 유일한 방법입니다. 위의 타입 테이블에서 보았던 것처럼 typeof 의 어떤 반환값들은 명세서에 정의되지 않은 것들도 있습니다. 따라서 그것들은 다양한 구현(엔진)마다 달라질 수 있습니다.In order to check the type of an object, it is highly recommended to use Object.prototype.toString; as this is the only reliable way of doing so. As shown in the above type table, some return values of typeof are not defined in the specification; thus, they can differ across various implementations.

instanceof 연산자는 같은 JavaScript 컨텍스트 상에 있는 사용자 정의 객체를 다룰 때에만 유용할 뿐입니다. typeof 연산자와 마찬가지로 다른 모든 곳에서의 사용은 피해야만 합니다.The instanceof operator should only be used when dealing with custom made objects that origin from the same JavaScript context. Just like the typeof operator, every other use of it should be avoided.

Number 나 String 과 같은 내장타입의 생성자들은 new 키워드가 있을때와 없을때가 서로 다르게 실행이 됩니다.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

Number 같은 내장타입을 생성자로써 사용하면 새로운 Number 객체가 생성이 됩니다. 하지만 new 키워드를 빼먹게 되면 그냥 변환기처럼 Number 함수가 실행이 됩니다.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.

timeout function 인 setTimeout 과 setInterval 은 첫번째 인자로 문자열을 받을 수 있습니다. 이 문자열은 항상 전역 범위에서 실행이 되는데 그것은 이경우에 eval 이 직접 호출되는 것이 아니기 때문입니다.The timeout functions setTimeout and setInterval can both take a string as their first argument. This string will always get executed in the global scope since eval is not being called directly in that case.

Security Issues

또한 eval 은 주어진 어떠한 코드도 실행을 하게 됨으로써 보안 문제가 발생하게 됩니다. 알수 없는 문자열이나 출처를 신뢰할 수 없는 문자열들은 사용해서는 안됩니다.eval also is a security problem as it executes any code given to it, it should never be used with strings of unknown or untrusted origins.

In Conclusion

eval 은 실행중 성능이나 보안에 문제를 일으킬 수 있는 어떤 코드도 사용해서는 안됩니다. 프로그램 실행을 위해 eval 이 요구되는 경우라면 그렇게 설계한 것 자체가 문제인 것이므로 애초에 사용하지 말아야 합니다. 따라서 eval 의 사용이 요구되지 않는 더 나은 설계를 해야만 합니다.eval should never be used, any code that makes use of it is to be questioned in its workings, performance and security. In case something requires eval in order to work, its design is to be questioned and should not be used in the first place, a better design should be used, that does not require the use of eval.

또한 언어 차원에서 undefined 라는 값을 가진 전역변수를 정의해 놓고 있으며 이 변수명 또한 undefined 입니다. 그러나 이 변수는 상수가 아니며 또한 언어의 키워드도 아닙니다. 이것은 값을 쉽게 덮어쓸수 있다는 것을 의미합니다.The language also defines a global variable that has the value of undefined, this variable is also called undefined. But this variable is not a constant, nor is it a keyword of the language. This means that its value can be easily overwritten.

여기에서 유일하게 다른점이라고 한다면 코드가 최소화되어 있고 익명의 랩퍼(anonymous wrapper) 안에 다른 var 구문이 없는 경우 최소 4Byte 이상이 더 사용된다는 것입니다.The only difference being here, that this version results in 4 more bytes being used in case it is minified and there is no other var statement inside the anonymous wrapper.

Uses of null

JavaScript 언어의 문맥에서 undefined 는 대부분 전통적인 null 의 의미로 사용되지만 실제 null(리터럴과 타입 둘 다)은 단지 또다른 데이터 타입일 뿐입니다.While undefined in the context of the JavaScript language is mostly used in the sense of a traditional null, the actual null (both a literal and a type) is more or less just another data type.

그것은 (Foo.prototype = null 의 설정으로 프로토타입 체인의 끝임을 선언하는 것과 같이) JavaScript 의 내부에서 사용되기도 하지만 대부분은 undefined 로 대체할 수 있습니다.It is used in some JavaScript internals (like declaring the end of the prototype chain by setting Foo.prototype = null), but in almost all cases it can be replaced by undefined.

Automatic Semicolon Insertion

JavaScript 는 C언어 스타일의 문법을 따르기는 하지만 소스코드에 반드시 세미콜론(;)을 사용해야하는 것은 아닙니다. 세미콜론을 생략하는 것도 가능합니다.Although JavaScript has C style syntax, it does not enforce the use of semicolons in the source code, it is possible to omit them.

이렇게 세미콜론이 자동으로 삽입되는 것은 코드의 동작이 바뀔수도 있는만큼 언어 설계의 가장 큰 결함중에 하나라고 고려됩니다.The automatic insertion of semicolon is considered to be one of biggest design flaws in the language, as it can change the behavior of code.

How it Works

아래 코드에는 세미콜론이 없기 때문에 세미콜론을 어디에 삽입할지의 결정은 파서에게 달려있습니다.The code below has no semicolons in it, so it is up to the parser to decide where to insert them.

소괄호가 먼저 오는 경우 파서는 세미콜론을 삽입하지 않습니다.In case of a leading parenthesis, the parser will not insert a semicolon.

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

이 코드는 한 라인으로 변환이 됩니다.This code gets transformed into one line.

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

log 는 함수를 반환하지 않을 가능성이 크기 때문에 위 코드는 undefined is not a function 라는 TypeError 를 발생하게 될 것입니다.Chances are very high that log does not return a function; therefore, the above will yield a TypeError stating that undefined is not a function.

In Conclusion

세미콜론을 빠뜨리지 말 것을 강력히 권장하며 또한 해당 문장과 같은 라인에서 중괄호를 사용하고 if / else 구문에서 한 라인이라도 세미콜론을 생략하지 말아야 합니다. 이 두가지 대책은 코드의 일관성을 향상시키는 것뿐만 아니라 코드의 동작이 변경되는 것으로부터 JavaScript 파서를 보호해 줄 것입니다.It is highly recommended to never omit semicolons, it is also advocated to keep braces on the same line with their corresponding statements and to never omit them for one single-line if / else statements. Both of these measures will not only improve the consistency of the code, they will also prevent the JavaScript parser from changing its behavior.

Other

setTimeout and setInterval

JavaScript 는 비동기적이기 때문에 setTimeout 과 setInterval 함수를 사용하여 함수의 실행을 스케줄링할 수 있습니다.Since JavaScript is asynchronous, it is possible to schedule the execution of a function by using the setTimeout and setInterval functions.

이것은 코드가 실행이 되는 JavaScript 엔진의 타이머 해상도(resolution)에 의존적입니다. 또한 JavaScript 가 싱글 스레드이기 때문에 실행되는 다른 코드가 그 스레드를 블럭시킬지도 모릅니다. 그것은 setTimeout 호출 시 명시한 만큼 정확히 지연시킬 수 있을지는 확실하지만은 않다는 것입니다. Depending on the timer resolution of the JavaScript engine that is running the code, as well as the fact that JavaScript is single threaded and other code that gets executed might block the thread, it is by no means a safe bet that one will get the exact delay that was specified in the setTimeout call.

첫번째 매개변수로 전달되는 함수는 전역객체에 의해 호출되게 됩니다. 이것은 호출된 함수 안에서 사용되는 this 가 바로 그 객체(전역객체)를 참조한다는 의미입니다.The function that was passed as the first parameter will get called by the global object, that means, that this inside the called function refers to that very object.

foo 안에서 계속 실행할지 말지를 원하는대로 결정할 수 있습니다.Not only does this encapsulate the setTimeout call, but it also prevents the stacking of calls and it gives additional control.foo itself can now decide whether it wants to run again or not.

Manually Clearing Timeouts

timeout 과 interval 을 삭제하려면 애초에 사용된 set 함수에 해당하는 각각의 ID 를 clearTimeout 또는 clearInterval 에 전달하면 됩니다.Clearing timeouts and intervals works by passing the respective ID to clearTimeout or clearInterval, depending which set function was used in the first place.

setTimeout 과 setInterval 은 첫번째 매개변수로 문자열을 받을 수도 있습니다. 이러한 특성은 내부적으로 eval 을 사용하는 것이기 때문에 사용을 하지 말아야 합니다.setTimeout and setInterval can also take a string as their first parameter. This feature should never be used, since it internally makes use of eval.

setTimeout 이나 setInterval 의 매개변수로 문자열을 사용하지 말아야 합니다. 그것은 함수 호출 시 인자를 전달해야만 할 때 정말로 좋지 않은 코드임이 분명합니다. 실제로 호출이 될 익명 함수가 전달되어야만 합니다.Never should a string be used as the parameter of setTimeout or setInterval. It is a clear sign of really bad code, when arguments need to be supplied to the function that gets called. An anonymous function should be passed that then takes care of the actual call.

나아가서, setInterval 은 스케줄링이 JavaScript 실행에 의해 블럭되지 않기 때문에 사용을 피해야만 합니다.Further, the use of setInterval should be avoided since its scheduler is not blocked by executing JavaScript.