자바스크립트

2007. 6. 16. 11:49Programming/JavaScript

출처 : http://monac.egloos.com/1252483


자바스크립트와 첫 인연을 맺은 것은 1996년도인데, 진지하게 사용하는 일이 없기 때문에 계속해서 까먹기만 하는 유명한 언어로 인식되어 있는 중.

자바스크립트를 보면 금방 또 알지만 항상 잊어버리기 때문에 언제든지 새로운 기억을 주입시키기 위한 용도로 작성함. 게다가, 잘 정리된 형태의 글을 보기 힘들고, 내 입맛에 맞는 글을 보기 힘들기 때문에 내 입맛에 맞게 정리한다는 목적 + 다른 분들도 참고할 분은 참고가 되었으면 해서 정리함. 특히, Ajax 프로그래밍과 함께 다시 뜨고 있는 자바스크립트 때문에 괴로운 분들에게 도움이 되길.

자바스크립트는 HTML 페이지에 저장하고

<script type="text/javascript" language="javascript">
  여기에 자바스크립트 코드 추가
</script>

하는 형태면 되지만 불편함이 많으니 "자바스크립트 학습 환경 만들기(http://monac.egloos.com/1222893)"를 참고해서 환경을 만들어서 사용하시면 편함.


1. 경고 보여주기

window.alert( 'Hello, world' );

alert( 'Hello, world' );

두 가지가 다 가능한 형식. 자바스크립트는 언제 어디서나 참조할 수 있는 내장 객체를 갖고 있는 데 그 중에 하나가 window 객체로 우리가 보는 창과 관련된 모든 기능을 제공함. 그 중에 대표적인 것이 경고창 띄우기.


링크를 클릭했을 때, "정말 삭제할래?"등의 대화상자를 보여주고, yes/no 답변을 묻는 confirm() 함수도 마찬가지.

<a href="#" onclick="return confirm( "정말 날 떠날거야?")">click</a>
<a href="#" onclick="return window.confirm( "정말 날 떠날거야?")">click</a>

confirm(), window.confirm() 모두 동일. 그러니까, window 객체에 속한 함수들은 window라는 이름을 생략하고 사용할 수 있음.

사용자로부터 입력을 받는 예로는 prompt( "나이를 알려줘" ) 라든가, window.prompt( "나이를 알려줘" ) 라든가 하는 형태가 가능.


2. 변수

i = 0;
alert( i );

변수를 위해 특별히 선언하지 않고 사용해도 잘 되지만 보통은 var 키워드를 사용해서 변수를 사용하는 것을 선호하고 있고, 대부분의 Ajax 라이브러리들도 var 키워드를 사용해서 변수를 선언해서 사용하고 있음. 명시적으로 변수를 선언해서 사용하는 것이 더 좋다고 생각함.

var i = 0;
i = "hello world";
i = false;

스크립트 언어들은 C/C++/Java 같은 언어들과 달리 데이터 타입이 없는 경우가 대부분이며, 이는 프로그래밍을 간단하게 해준다는 장점이 있음.
그렇다고 해서, 실제로 데이터 타입이 없다거나 구분하지 않는 것은 아니며 내부적으로 데이터 타입을 갖고 있으며, 그에 따라 적절하게 알아서 동작하는 똑똑한 기능을 갖추고 있음. 그래서, 위와 같은 코드를 사용할 경우 알아서 데이터 타입을 변환하고, 알아서 동작함. 내부적으로 갖는 데이터타입을 서브 데이터 타입(sub data type)이라 함. 신경 안써도 됨. 이런 게 있으면 좋다라는 정도의 상식으로만 알고 있으면 되고, 이런 것 모르는 스크립터가 대부분이니 사는 데 지장없음!

첫번째는 정수형이고, 두번째는 문자열 형식이고, 세번째는 불리언(boolean) 형식임.

true/false는 자바스크립트 내부에서 지원하는 키워드. 진리값이니 뭐니 말은 많으나 다 필요없음. 참이면 참이고, 거짓이면 거짓임.

문자열은 "과 ' 모두 사용할 수 있는데, HTML에서 사용할 때는 HTML의 name="value"와 혼동되므로 '를 사용하는 것이 보통임.


3. 배열

var days = new Array( "Mon", "Tue", "Wed" );
days[3] = "Thu";

alert( days );

배열은 Array 클래스를 이용해서 선언함. 클래스 생성은 다른 언어들처럼 new 키워드를 사용함. 배열의 인덱스는 0 부터 세기 시작하는데, 위와 같이 초기값으로 3개의 요소를 주면 0, 1, 2 번째에 할당되고, 3번째에 새로운 요소를 할당하는 것도 자유로움.

배열 자체가 동적이니까 마음대로 편안하게 사용할 수 있음. 즉, 마음대로 꼬장부리면서 사용할 수 있다는 의미.

var days = new Array( "Mon", "Tue", "Wed" );

days[5] = "Thu";

alert( days );

이렇게 쓰면 중간에 비는 것들은 전부 비게되고, 5번째에 삽입이 됨. 꼬장은 그대의 자유.

new Array() 라고 안 쓰고 싶다면

var days = [ "Mon", "Tue" ];
days[2] = "Wed";
alert( days );

와 같이 사용할 수 있음.


4. 제어 구조

var rand = Math.random();
rand = Math.ceil( 6 * rand );

if( rand % 2 == 1 )
{
  alert( 'odd' );
}
else
{
  alert( 'even' );
}

Math 클래스에서는 다양한 수학함수들과 난수 생성을 할 수 있음. Math.cos(), Math.abs(), Math.sqrt() 등이 준비되어 있음.

다른 언어들처럼 if 구조는 축약형을 사용할 수 있음.

var output = (rand % 2 == 1 ) ? 'odd' : 'even';

alert( output );



4.1 swtich

switch ... case 구조도 물론 지원함.

var rand = Math.random();
rand = Math.ceil( rand * 6 );

switch( rand )
{
  case 1:
  case 3:
  case 5:
    alert( 'odd' );
  default:
    alert( 'even' );
}


5. 루프

일반적인 for, while을 모두 지원하며 사용법도 동일함.

var days = ["Mon", "Tue", "Wed", "Sun" ];  // 일주일이 4일 뿐이라서 3일 일하면 노는 세상을 꿈꾸는 무의식의 반영

for( var ctr = 0; ctr < days.length; ++ctr )
{
  alert( days[ctr] );
}

foreach와 비슷한 for .. in 구조는 다음과 같음

var days = [ "Mon", "Tue", "Wed" ];

for( var day in days )
{
  alert( days[ day ] );
}


while 문은 다음과 같음

var days = [ "Mon", "Tue", "Wed" ];

var ctr = 0;

while( ctr < days.length )
{
  alert( days[ ctr ] );
  ++ctr;
}



6. 정규표현식

function html_escape( str )
{
  return str.replace( /&/g, "&amp;" ).replace( /</g, "&lt;" ).replace( />/g, "&gt;" ).replace( /"/g, "&quot;" ).replace( /'/g, "&apos;" );
}

함수는 function 키워드로 선언하면 되고, 데이터 타입이 없으니까 반환 타입 지정 같은 것도 없음.
정규표현식은 어떤 문자열이든 점(.) 찍고 replace() 메서드를 호출하면 됨.

"Hello World".replace( /Hello/, "Cruel");

alert(  "Hello World".replace( /Hello/, "Cruel")   );   // 원래 세상은 잔인햇!!!!

객체지향 언어적인 특성도 갖고 있기 때문에 위에처럼 메서드 연쇄로 쭈욱 호출하면서 처리해도 됨.

위에 코드는 HTML로 쓰인 것들을 안전한 문자열로 변환하기 위해 만든 자바스크립트 함수. 다음 코드로 결과 확인

alert( html_espcae( "<HTML>" ) );



6.1 함수 가변 인자

function make_list()
{
  var ctr = 0;

  for( ctr = 0; ctr < arguments.length; ++ctr )
  {
    alert( arguments[ctr] );
  }
}

make_list( "Mon", "Tue", "Wed" );

함수의 인자는 arguments 내장 객체로 전달되므로 이를 이용해서 처리.



7. 이벤트

자바스크립트는 몇가지 이벤트들을 정의하고 있음. 대표적인 것이 onload 이벤트로 페이지가 로딩될 때 실행하는 처리들을 이곳에서 할 수 있음.

<body onload="alert( 'Hahaha' );" >

이벤트에 직접 자바스크립트를 쓰는 것도 가능하고, 함수 이름을 지정하는 것도 가능함.

function init()
{
  alert( '오빠왔다!!' );
}

window.onload = init;

body 태그에 쓰는 것도 창과 관련된 것이므로 window 객체의 onload 이벤트에 해당 함수 이름을 지정함. init 뒤에 ()를 붙이지 않음. C언어처럼 말하자면 함수의 주소만 지정하는 것이므로.

자바스크립트는 함수 이름을 지정하지 않아도 되니까 위 코드를 다음과 같이 하나로 합치는 것이 가능함.

window.onload = function() {
  alert( '오빠왔대두!!!' );
}

init 함수 정의해서 init만 삭제하고, 합쳐놓은 것과 똑같은 것을 알 수 있음.

인자가 있는 함수를 정의한다면

window.onload = function( a, b ) {
  alert( a + b );
  alert( "오빠 다녀갔어~~!" );
}


8. 클래스

자바스크립트는 객체지향 언어의 특성도 갖고 있음.(다소 부족한 기능이지만)
자바스크립트는 클래스를 위한 키워드가 존재하지 않음. 그냥 클래스도 함수임.

객체지향 언어에서 생성자를 정의한다고 하는데, 사실상 생성자라는 것이 "()" 함수를 정의하는 것이라 할 수 있음.
인자가 있는 생성자라면 "(a, b)"를 정의하는 것이라 할 수 있고.

예를 들어, C++을 보면

size_t length(0);
string arg1( ARGV[1] ), arg2( ARGV[2] );

이런식으로 변수를 선언할 수 있는 것도 생성자의 "()"이 함수이기 때문.

자바스크립트에서 클래스는 함수로 정의함.


function Character( name, gender )
{
  var _name = name;
  var _gender = gender;


  this.get_name = function() { return _name; }
  this.set_name = function(value) { _name = value; }


  this.get_gender = function() { return _gender; }
  this.set_gender = function(value) { _gender = value; }


  this.toString = function() { return _name + ":" + _gender  }
}

var nadia = new Character( "Nadia" );
nadia.set_gender( "F" );

alert( nadia.get_name() );
alert( nadia.get_gender() );

alert( nadia.toString() );

new 키워드로 인스턴스를 생성할 때, 함수의 인자 수를 정확하게 맞추지 않아도 된다는 것.

클래스 정의의 대부분은 이처럼 함수를 직접 선언하는 형태로 구현됨.

이처럼 함수에 이름을 주지 않고, 마음 내키는 대로 함수를 남발하는 것을 익명 함수(Anonymous function or Anonymous method)라 부름. 굳이 기억할 필요없는 함수들은 이름을 주지 않고, 관리를 편하게 하자는 나름대로의 합리적인 이유가 있음.

this 키워드는 현재 인스턴스를 가리키기 위해 사용되는 것으로 다른 객체지향 언어들과 쓰임이 동일함.

자바스크립트에도 루비와 마찬가지로 eval() 이라는 함수가 존재해서 문자열을 코드로 해석하게 하는 기능이 있음.

eval( "alert( 3 + 5 )" );

을 실행하면

alert( 3 + 5 );

가 되어 결과 8이 출력되는 것을 볼 수 있음.

루비의 class_eval(http://monac.egloos.com/1158938)에서 보면 class_eval() 함수를 사용해서 클래스 멤버에 대한 get/set 메서드를 자동으로 생성하는 기능을 구현하는 것을 설명한 적이 있고, 실제 루비의 attr_accessor 키워드가 이렇게 구현되어 있는데, 자바스크립트도 eval() 함수가 있기 때문에 이와 같은 구현을 하는 것이 가능함.

class Person
    attr_accessor :name
end

루비는 이렇게 해주면 name에 대한 getter/setter가 모두 자동으로 만들어지니까 간편하다는 이야기들을 자주하는데, 자바스크립트는 이 보다 더 황당하게 간단함.(한 줄!)
자바스크립트에서 루비처럼 attr_accessor 키워드를 제공하는 것은 아니지만 나중에 소개할 Extjs 같은 Ajax 프레임워크들은 eval을 적극 활용해서 그런 기능을 대부분 자체적으로 만들어서 사용하고 있음.



9. document 객체

document 객체는 웹브라우저에서 문서와 관련된 모든 요소들을 관리함.

document.embeds 배열은 <embed> 태그로 선언된 모든 요소들의 목록을 갖고 있음.

document.forms 배열은 <form> 태그로 선언된 모든 요소들의 목록을 갖고 있음.

document.frames, document.images, document.links 배열이 있음.

<form>
  <input type='text' name='text1' />
</form>

이 경우 text1 텍스트박스에 접근하려면

alert( document.forms[0].elements[ 'text1' ].value );

텍스트 상자의 값을 출력하게 됨. 첫번째 폼이기 때문에 forms[0]로 지정한 것임.

<form name='my_form'> 으로 선언되어 있다면

document.forms[ 'my_form' ].elements[ 'text1' ].value

와 같은 형식으로도 접근 가능함.

<script>
function showme( f )
{
  alert( f.elements[ 'text1' ].value );
}
</script>

<form name='my_form' >
  <input type='text' name='text1' />
  <input type='button' onclick='showme( this.form );' />
</form>

함수 인자에서 this.form으로 폼에 대한 참조를 넘겼음. this.form은 document.forms[ 'my_form' ]과 의미가 같음.

document.forms[ 'my_form' ].elements[ 'text1' ].value 를

f.elements['text1'].value 로 축약한 것임. f는 함수에 전달된 인자.

checkbox, radio 버튼은 checked 속성이 true/false로 지정

select로 표현하는 드랍다운리스트는 selectedIndex로 인덱스를 가져올 수 있음.
option은 selected와 value 멤버로 접근함.


웹페이지에서 컨트롤을 찾는 방법은 documents.forms... 복잡하게 찾아가는 방법도 있지만,  document.getElementById()를 쓰는 방법도 있음.



<script>
function showme( f )
{
  var label = document.getElementById( 'label1' );
  label.innerHTML = html_escape( f.elements['text1'].value );
}
</script>

<form name='my_form' >
  <input type='text' name='text1' />
  <input type='button' onclick='showme( this.form );' />
</form>

<span id='label1'>---</span>


html_escape() 함수는 위에 정의한 것을 사용.


getElementById()는 전체 컨트롤을 전부 탐색하기 때문에 느리다는 것을 명심해야 하고, 화면에 컨트롤이 많다면 성능저하의 주범이 될 수 있으므로 조심해서 사용해야 함. ASP.NET 같은 언어는 Page.FindControl() 메서드로 컨트롤을 탐색하는 데, 이것도 역시 이와 마찬가지.


10. DOM 처리

DOM은 Document Object Model의 약자. 문서 객체 모델이라는 데, 문서의 각 구성 요소 전부를 트리 구조 형태로 표현하는 것.

getElementById() 함수도 바로 이 DOM 트리를 탐색하는 것임. FireFox에서도 Web Developer 플러그인을 설치하면 DOM Inspector를 통해 DOM 트리를 볼 수 있음.

getElementsByTagName( name )

태그 이름으로 해당 요소 검색하는 것. 근데, 웹 페이지에 P 태그가 얼마나 많이 쓰이겠나. 그러니까, 반환 타입은 배열형식임.

var p = document.getElementsByTagName( 'p' )[0];

뒤에 [0]이면 첫번째 p 태그를 가져올 수 있음.

var p_arr = document.getElementsByTagName( 'p' );   // 배열로 가져오기

createElement( name )

DOM 트리에 새로운 요소 만들기.

createAttribute( name )

새로운 속성 만들기... name="value"로 되는 특성에서 name  만들기.

createTextNode( name )
  name을 키로 하고, 텍스트를 값으로 갖는 노드 만들기.

appendChild( node )
  현재 요소를 node에 자식으로 추가하기. 그러니까, 어느날 갑자기 node에겐 딸린 자식이 생긴다는 의미...;;


<script>
function show( f )
{
  var p = document.getElementsByTagName( 'p' )[0];
  var label = document.createElement( 'span' );
  var text = document.createTextNode( f.elements['text1'].value );
 
  label.appendChild( text );
  p.appendChild( label );
}

</script>

<form>
  <input type="text" name="text1" />
  <input type="button" value="show text" onclick="show(this.form);" />
  <p>Entered Text: </p>
</form>

p.innerHTML을 사용하면 내용을 바꾸는 게 되지만 appendChild()를 썼기 때문에 내용이 추가되는 형태가 되었음.

DOM 트리 구조를 보면 이해하기 쉬우므로 DOM Inspector 사용을 권함.