from 더글라스 크락포드 - 자바스크립트 핵심가이드
아래 사항들은 피해야 한다.
==
결코 ==/!=를 사용하지 말고 항상 ===/!==를 사용하라.
;`` == '0' // 거짓
0 == '' // 참
0 == '0' // 참
false == 'false' // 거짓
false == '0' //참
false == undefined // 거짓
false == null // 거짓
null == undefined // 참
'\t\r\n' == 0 // 참위에서 ==을 ===로 바꾸면 모두 거짓이 된다.
예측 못한 결과들을 낳을 수 있다. 사용하지 않는게 좋다. 게다가 자바스크립트 프로세서의 속도를 현저하게 느리게 만든다. 왜냐하면 with 문은 변수 이름의 어휘적 바인딩을 어렵게 하기 때문.
with (obj) {
a = b
}는 다음과 같은 의미이다.
if (obj.a === undefined) {
a = obj.b === undefined ? b : obj.b
} else {
obj.a = obj.b === undefined ? b : obj.b
}결국 이 with 문은
a = b
a = obj.b
obj.a = b
obj.a = obj.b이들 중 하나와 같게 되는데 어떤 문장으로 실행될지는 프로그램을 봐서는 알 수 없다. 또한 프로그램 실행할 때마다 달라질 수 있고, 심지어 프로그램을 실행시키는 동안 달라질 수 있다.
프로그램을 제대로 읽을 수 없고 어떻게 실행할지를 예측할 수 없다면, 프로그램이 원하는 대로 제대로 실행할 것이라고 확신할 수 없다.
eval을 쓴 코드는 매우 읽기 어렵고, 속도 또한 매우 느려질 수 있다. JSLint 와도 충돌하고 보안도 위태롭게 만든다. 함수 생성자도 eval의 또 다른 형태이며 같은 이유로 사용을 피해야만 한다. setTimeout과 setInterval 함수는 문자열 인수나 함수 인수를 취할 수 있다. 이 함수들에 문자열 인수를 제공하면 eval처럼 동작하게 되므로, 문자열 인수를 넘기는 것은 피해야 한다.
eval('myValue = myObject.' + myKey + ';')아래와 같이 바꿀 수 있다.
myvalue = myObjec[myKey]continue
리팩토링을 통해 continue를 제거했을 때 성능이 향상되지 않는 경우를 본 적이 없다.
다음 case 절까지 실행하는 switch 문 (switch Fall through)
의도하지 않은 다음 case 절까지 실행하는 패턴 발생.
언어에서 가장 나쁜 점은 명백하게 위험하거나 불필요한 속성들이 아니다. 이러한 점들은 쉽게 피할 수 있다. 그보다는 매력적으로 보이는 폐단이 가장 나쁘다. 이러한 속성들은 유용하면서 위험하기 때문.
블럭이 없는 문장
원칙적이고 일관된 블럭의 사용은 프로그램의 이해를 훨씬 쉽게 한다.
// 다음 코드는
if (ok) t = true
는
// 다음과 같은 코드가 될 수 있습니다
if (ok) t = true
advance()
// 이 코드는 마치 다음과 같아 보이지만
if (ok) {
t = true
advance()
}
// 실제로는 다음과 같은 의미입니다.
if (ok) {
t = true
}
advance()이렇게 일을 처리하는 것처럼 보이는데 실제로 다르게 일을 처리하는 프로그램은 바르게 이해하기 어렵다.
++ --증감 연산자를 사용하면 코드를 매우 간결한 스타일로 작성할 수 있고, C 같은 언어에서는 다음과 같이 문자열 복사를 한 줄로 작성하게 만들었으며, 이런 스타일을 권장하기도 하지만
for ( p = src, q = dest; !*p; p++, q++) *q = *p;
심각한 보안 취약점을 만드는 대부분의 버퍼 오버런 버그는 위와 같은 스타일의 코드 때문에 발생한다. 필자는 실무에서 ++와 --를 사용하게 되면 코드가 더 빽빽해지고 더 까다로워지며, 보다 더 암호처럼 보이게 된다는 것을 경험했다. 그래서 규칙처럼 더 이상 증감 연산자를 사용하지 않는다.
& and
| or
^ xor
~ not
>> 부호 있는 오른쪽 시프트
>>> 부호 없는 오른쪽 시프트
<< 왼쪽 시프트
자바스크립트는 자바처럼 위와 같은 비트 연산자가 있다.자바에서 비트 연산자는 정수에 대해서 동작한다. 그런데, 자바스크립트에는 정수형은 없고 단지 배 정도의 부동 소수점 숫자형만이 존재한다. 그래서 비트 연산자는 대상이 되는 숫자를 일단 정수형으로 변환한 다음에 비트 연산을 수행하고 다시 원래 타입으로 되돌린다. 대부분의 언어에서 비트 연산자는 하드웨어에 친근하고 속도도 매우 빠르지만, 자바스크립트에서 비트 연산자는 하드웨어와 전혀 동떨어져 있고 속도도 매우 느리다. 자바스크립트가 비트 연산을 위해 사용되는 경우는 매우 드물다. 그래서 자바스크립트 프로그램에서 원래 &을 사용하는 경우보다 &&을 사용하려다가 &으로 사용하는 경우가 더 많다. 비트 연산자의 존재는 오히려 잠재된 버그를 만들어내기 쉽다.
함수 문장 vs 함수 표현식
함수 문장은 var 문장과 함수값 조합의 축약형이다.
책에서는 함수 문장이라고 번역되어 있지만, 아무래도 함수 선언을 말하는 것 같다. 더 찾아보니 함수선언(Function Declarations)은 Function Statement 라고도 하며 말 그대로 함수 문장이란 뜻이다. 이는 곧 실행가능한 코드블럭이 아니며 함수의 정의를 나타내는 문장으로 해석되며 따라서 코드해석에 따른 수행결과가 존재하지 않는다는 것을 의미한다.
다음과 같은 함수형 문장은,
function foo() {}
다음과 같은 의미라고 할 수 있다.
var foo = function foo( ) {};
필자는 책 전체에 걸쳐 함수 표현식(두번째 형식)을 사용했다. 왜냐하면 foo가 함수값(function value)을 가진 변수라는 것을 명확하게 나타내기 때문이다. 자바스크립트라는 언어를 잘 사용하기 위해선 함수도 값(value)이라는 것을 이해하는 것이 중요.
JS에서 함수는 일급 객체이므로 다음과 같은 특징을 지닌다.
함수 문장은 호이스팅의 대상(위로 끌어올려지는)이 된다. 이 말은 함수가 위치한 곳과 관계없이 함수가 정의된 곳의 유효범위 가장 상위로 이동된다는 뜻. (그렇기에 JS에서는 함수 호출이 함수를 정의하는 문장보다 앞에 있건 뒤에 있건 유효범위만 같으면 문제가 없다.) 이러한 특징은 함수를 사용하기 전에 반드시 선언해야 한다는 요구를 경감시키는데, 결국 필자 생각에는 구조를 엉성하게 만들 뿐이다. 또한 이런 특징은 if 문에서 함수 문장 사용을 금하게 된다. 밝혀진 바에 따르면 대부분의 브라우저에서 if문 내에서 함수 문장 사용을 허용하지만, 이렇게 사용된 함수 문장이 어떻게 해석되는지는 브라우저마다 제 각각이다. 이런 점은 잠재적인 문제를 발생시킨다.
IE의 경우
notPreDefined();
var notPreDefined = function notPreDefined() {
alert("ok");
};이 예는 notPreDefined() 부분에 함수가 정의되지 않았다는 오류가 발생해야 정상. 왜냐하면 아래에 있는 함수 정의가 함수 문장으로 정의된 것이 아니기 때문. 하지만 IE에서는 함수 표현식에 함수 이름을 지정만 하면 함수 문장으로 정의한 것처럼 해석하는 것으로 보인다. 그래서 위의 코드를 IE에서 확인하면 오류가 나지 않는다. 하지만 함수 정의 부분을 다음과 같이 하여 함수 이름을 빼면 오류가 발생한다.
var notPreDefined = function() {
alert('ok')
}공식적인 문법은 function이라는 단어로 시작하는 문장을 함수 문장이라고 가정하고 있기 때문에 문장의 첫 부분에 함수 표현식을 사용할 수 없다. 이를 위한 대안은 함수 표현식을 다음의 예처럼 괄호로 묶는 것이다.
;(function() {
var hidden_variable
// 이 함수는 환경에 영향을 미치는 점들이 있지만
// 새로운 전역변수를
// 추가하지는 않음.
})()데이터 타입 랩퍼
new Boolean, new Number, new String 등을 사용하지 마라.
또한, new Object와 new Array 사용도 피하고 {}와 []를 사용하라.
이러한 랩퍼들은 완전히 필요가 없으며 때때로 혼란을 줄 수 있다는게 판명됐다.
new
자바스크립트의 new 연산자는 피연산자의 프로토타입 멤버들을 상속하는 객체를 생성하고 이 객체를 this에 바인딩하면서 피연산자를 호출한다. 이러한 호출은 new의 피연산자(생성자 함수)가 새로 만들어진 객체를 원하는 대로 커스타마이징하여 반환할 수 있는 기회를 제공한다.
만약 new 연산자를 빼먹게 되면 일반적인 함수 호출(function invocation)을 하게 되고 이때 this는 새로운 객체가 아니라 전역객체에 바인딩된다. 이렇게 되면 새로운 속성을 초기화 할 때 전역변수에 접근하게 된다. 아주 안 좋은 결과다. new를 안 써도 컴파일 시나 실행 시에 어떠한 경고도 없다.
new와 함께 사용하기 위해 만든 함수의 이름은 각 단어의 첫 글자를 대문자로 표기하고 그외의 것들에는 이러한 표기법을 사용하지 않는 것이 좋다. (파스칼 표기법). 이렇게 코딩 규칙을 사용함으로써 new를 생략한 실수를 찾을 수 있는 단서를 제공할 수 있다.
물론 new 연산자를 사용하지 않는 것이 더 나은 정책이다.
void
많은 언어에서 void는 아무 값도 없는 데이터 타입이지만, JS에서 void는 피연산자를 취한 후 undefined를 반환하는 연산자다. 이는 유용하지도 않고 혼란스럽다. void 역시 피하라.