본문 바로가기

Development/Coding

자바스크립트 코딩할 때 실수 하기 쉬운 몇가지

http://www.ifadey.com/2011/05/javascript-mistakes-you-must-avoid/


자바스크립트 코딩할 때 실수 하기 쉬운 몇가지 (발역)


- 비교연산자

가장 많이 사용하는 비교연산자 == 입니다. 이 비교 연산자는 식의 타입에 관계 없이 결과의 값을 비교하는 것입니다.
아래 식에서 1 과 true 는 같은 결과를 가집니다.

if( 1 == true ) {
    //this code will run
}


몇가지 예를 더 보시죠.

1 == "1"        //true
"true" == true  //false
1 == true       //true
"0" == 0        //true
"" == 0         //true
" " == 0        //true
"Str" == false  //false
"Str" == true   //false


어떤 것은 예상했던 것보다 아리송한 결과를 내기도 합니다. 확실히 기억해둘 것은 자바스크립트에서 == 비교 연산의 모든 항은 비교하기 전에 숫자 타입의 데이터로 변환이 된 후 처리된다는 것입니다.

첫번째 예 ( 1 == "1" ) 에서 두번째 "1" 은 숫자로 변환이 된 후 1 == 1 과 같은 결과를 가지게 됩니다.

두번째 예 ( "true" == true ) 는 거짓이 됩니다. "true" 는 숫자로 변경되지 않습니다. 엄밀히 말하면 NaN (Not a Number) 로 변환되지요. 따라서 결과는 거짓이 됩니다.

파이어버그 같은 툴로 Number() 함수로 어떤 값이 숫자형으로 변경되는 과정을 체크할 수 있습니다.




그렇다면 === 연산자는 어떻게 작동하는지 궁금할 것 입니다. 이 연산자는 데이터 타입과 함께 값을 비교합니다. 따라서 데이터 타입이 다르다면 false 를 리턴합니다.

아래 예제를 참고하세요.

4 === 4         //true
"2" === 2       //false
1 === true       //false
"true" === true //false



- 참조형 변수에 Null 을 대입하여 마무리하기

객체나 배열을 담고 있는 변수는 객체나 배열의 포인터를 담고 있습니다. 즉 실제 값을 담고 있지 않습니다.
이런 참조형 변수를 모두 사용하고 난 후에는 null 로 초기화시켜야 자바스크립트 엔진이 추후에 GC(garbage collect) 할 수 있습니다.

var arr = [1, 2, 3];
 
//perform some processing on arr
 
//assign null to arr when its use is finished
arr = null;


위 변수가 글로벌 영역에 있다면 반드시 null 로 초기화하여 메모리가 낭비되지 않도록 해야합니다. 로컬 영역의 변수들은 대부분 자동적으로 GC 됩니다.


- 참조형 변수 초기화

참조형 변수를 한번에 초기화시키지 말아주세요. 예제를 살펴봅시다.

var arr1 = [1, 2, 3]
  , arr2 = ['a', 'b', 'c'];
 
//reset both arrays
arr1 = arr2 = [];
 
//add a single item in arr2 and arr1
arr2.push( 32 );
arr1.push( 10 );
 
//print both arrays and you will see same result
//OUTPUT: 10, 32
alert( arr1.join() );
alert( arr2.join() );


arr1, arr2 를 선언함과 동시에 값을 생성하였습니다. 그 후 한번에 arr1, arr2 를 초기화하였습니다. 문제는 위 두 변수가 같은 곳은 참고하고 있게 되었다는 것입니다. 결국 두 값은 동일한 변수가 되어 버렸습니다.

서로 다른 값을 push() 했지만 결과는 한곳에 두번 push() 한 것과 같습니다.


- var 를 잊지마세요

어느 곳에서든 var 를 사용하지 않으면 전역변수로 취급됩니다.
심지어 유일하게 변수 영역을 지정할 수 있는 함수 내부에서도 var 를 사용하여 변수를 선언하지 않으면 전역변수로 취급됩니다.

function createVar() {
    var myVar = 'local';
};
 
alert( myVar ); //output: undefined


myVar 는 함수 내부에 선언된 로컬 변수 입니다. alert 의 myVar 는 선언되지 않은 변수로 판단됩니다.

function createVar() {
    myVar = 'local';
};
 
alert( myVar ); //output: local


함수 내부에 선언된 myVar 는 전역변수로 처리됩니다.


- 가능하면 이벤트를 위임하세요

요즘은 이벤트 핸들러를 적용하는 경우가 많습니다. 아래의 코드처럼 링크에 클릭 이벤트를 달죠.

document.getElementById('myLink').addEventListener( 'click', function() {
   //you code goes here...
}, false );

그런데 아래의 테이블에 있는 모든 <td> 에 이벤트를 달게 된다면 어떻게 해야할까요.

<table id="myTable">
   <tbody>
      <tr>
         <td>1, 1</td>
     <td>1, 2</td>
      </tr>
 
      <tr>
         <td>2, 1</td>
     <td>2, 2</td>
      </tr>
   </tbody>
</table>


델리게이트 패턴을 적용해 처리하는게 좋겠죠. 작전은 myTable 에 클릭 이벤트 핸들러를 붙인 후 콜백 함수에 e 이벤트 핸들을 넘겨줍니다.
즉, e 변수에 이벤트를 위임하는 것이죠. 이벤트가 td 일 경우에만 다음 처리를 진행하도록 로직을 짜면 모든 td 에 이벤트 핸들러를 추가하지 않아도 같은 기능을 구현하게 되고 코드도 깔끔해지게 되겠죠.

document.getElementById( 'myTable' ).addEventListener( 'click', function( e ) {
      if( e.target && e.target.nodeName == 'TD' ) {
         console.log( e.target.innerHTML );
 
         //to access id
         //console.log( e.target.id );
 
         //to access className
         //console.log( e.target.className );
      }
   }, false );



- innerText 대 innerHTML

이건 헷갈릴 것도 없이 innerText 은 텍스트를 innerHTML HTML 를 참조합니다.



위 그림을 보면 innerHTML 은 <p> 태그가 포함됩니다.

아래 그림을 보면 innerText 는 <p> 태그가 포함되지 않습니다.





- 노드 추가하기

아래 코드는 보통 Ajax 호출을 통해 서버로 데이터를 받아 DOM 을 삽입하는 스타일의 예제입니다.

window.onload = function() {
//ul element - <ul id="list"></ul>
var list = document.getElementById( 'list' );
 
var item = null;
 
//suppose this json is returned from ajax call
var ajaxResponse = [
    { 'name' : 'Haiku' },
    { 'name' : 'Linux' },
    { 'name' : 'OS X' },
    { 'name' : 'Windows' }
];
 
//add all names in ajaxReponse to documentFragment
for( var i in ajaxResponse ) {
    item = document.createElement( 'li' );
    item.appendChild( document.createTextNode( ajaxResponse[ i ].name ) );
    list.appendChild( item );
}
} //end onload
 
/*
..:: OUTPUT ::..
<ul id="list">
<li>Haiku</li>
<li>Linux</li>
<li>OS X</li>
<li>Windows</li>
</ul>
*/


여기에서 루프 안의 리스트를 추가할 때 문제가 발생할 수 있습니다. DOM 이 즉시 갱신되기 때문이죠. 아시다시피 DOM 조작에는 많은 시간이 걸립니다.

“DocumentFragment is light weight version of document and it has no visual representation on the web page.”

라고 합니다. 가능하면 이런 작업은 아래의 코드처럼 처리하는 것이 좋습니다. DOM 을 즉시 업데이트 하기 보다 documentFragment 를 사용하여 업데이트 하고 DOM 조작은 마지막에 몰아서 처리합니다.

window.onload = function() {
    //create DocumentFragment
    var documentFragment = document.createDocumentFragment();
 
    var list = document.getElementById( 'list' ); //<ul id="list"></ul>
    var item = null;
 
    //suppose this json is returned from ajax call
    var ajaxResponse = [
    { 'name' : 'Haiku' },
    { 'name' : 'Linux' },
    { 'name' : 'OS X' },
    { 'name' : 'Windows' }
    ];
 
    //add all names in ajaxReponse to documentFragment
    for( var i in ajaxResponse ) {
    item = document.createElement( 'li' );
    item.appendChild( document.createTextNode( ajaxResponse[ i ].name ) );
    documentFragment.appendChild( item );
    }
 
    //append all items in documentFragment to list
    list.appendChild( documentFragment );
}


John Resig 이 DocumentFragment 에 대해 포스트한 글을 살펴보세요.


- innerHTML 을 사용하여 DOM 조작하기

추가연산자 += 를 사용하여 innerHTML 에 DOM 을 추가하지 마세요.

+= 연산자는 즉시 DOM 을 업데이트 하기 때문입니다. 아래 예제는 적절하지 않은 코드입니다.

var container = document.getElementById( 'container' );
 
for( var i = 1; i <= 10; ++i ) {
    container.innerHTML += 'Item ' + i + '<br />';
}


항상 임시변수에 DOM 을 담아두고 마지막에 업데이트하는 습관을 가지도록 합니다.


var container = document.getElementById( 'container' )
  , str = '';
 
for( var i = 1; i <= 10; ++i ) {
    str += 'Item ' + i + '<br />';
}
 
container.innerHTML += str;