홈으로 홈으로 | 무료회원가입 | 아이디/비번찾기 | 즐겨찾기
메인홈1대1상담음악방송청취

음악가사 검색
이아인사랑
트로트발전연구소
음악방송 안드로이드앱 제작
온라이브스토어(앱다운로드)
온라이브(방송등록및청취)
Search Video Best 50
금영노래방
TJ노래방
유튜브 동영상 다운로드
POP Player
신나는 고스톱
컴퓨터 오목
작은 음악다방
운.영.자.1대1.상담
FAQ\지원.프로그램
Q&A\고.객.지.원

자동러시 웹플레이어신청

보안∵서버∵쿠키
윈도우∵프레임
이미지∵배경
시간∵달력∵계산
상태바∵타이틀바
음악∵영상
폼∵전송∵테이블
키보드∵마우스
효과∵링크∵메뉴
Script∵php
기타
PHP 강좌
윈도우관련팁
웹관련팁
홈페이지제작팁
기본강좌
레지스트리
윈도우서버
리눅스서버
Android
[번역] 자바스크립트 다시 배우기 (A re - introduction to JavaScript)
3년 전
A re-introduction to JavaScript (JS Tutorial)

Introduction

왜 "다시-배우기" (re-introduction) 라는 표현을 썼는가? 왜냐하면 JavaScript가장 잘못 이해하는 경우가 많은 언어 이기 때문이다. 종종 과소평가 받지만, 자바스크립트의 단순함 뒤에는 매우 강력한 기능이 숨겨져 있다. 2005 년부터 아주 강력한 자바스크립트 어플리케이션이 등장하였으며, 이 어플리케이션들은, 웹 개발자가 자바스크립트를 깊이 이해해야한다는 것을 보여준다.

언어의 역사를 먼저 살펴보자. 자바스크립트는 1995 년, Netscape 의 개발자였던 Brendan Eich 가 만들었고, 1996 년 초에 Netscape 2 와 함께 출시되었다. 원래는 LiveScript 라고 부르려고 하였으나, Java 의 인기에 편승하려는 마케팅 전략으로 이름이 바뀌였다 - 이름으로 인하여 사람들이 헷갈려 하지만 자바와 자바스크립트는 다른 언어이고 공통점이 별로 없다.

마이크로소프트는 몇개월 후 IE 3 에 JScript 라는 언어를 출시하였다. 넷스케이프는 Ecma International 이라는 유럽 표준화 기구에 자바스크립트를 제출하였고, 그 결과가 1997 년의 ECMAScript 표준이다. 이 표준은 1999 년 ECMAScript edition 3 로 대대적인 개편이 되고, 그 이후로 큰 변함이 없었다. 4 번째 버전은, 언어의 복잡성에 대한 정치적 견해 차이로, 폐기되었다. 5 번째 버전은, 4 번째 버전을 기초로 2009 년 겨울에 발표되었다.

이런 안정성은 (역주: 자바스크립트가 오랬동안 변하지 않았다는 것) 개발자에게는 좋은 소식이다. 나는 주로 버전 3 에 대해 다룰 것이다.

대부분의 프로그래밍 언어와는 다르게, 자바스크립트는 입력과 출력에 대한 개념이 없다. 자바스크립트는 host 환경에서 스크립트 언어로 사용되게 설계되었으며, 외부 세계와 소통하는 것은 host 환경이 책임지게 된다. 가장 흔하게 사용되는 host 환경은 브라우저이다. 하지만 자바스크립트 인터프리터는 Adobe Acrobat, Photoshop, Yahoo 의 위젯 엔진, 또는 node.js 같은 서버 환경에서도 실행될 수 있다.

Overview

자바스크립트는 객체지향 언어이고, dynamic 언어이다 (역주: dynamic 이란 다른 언어에서 컴파일시에 할 일을, 실시간에 하는 경우가 많은 언어란 뜻). 자바스크립트에는 타입이 있고, operator (연산자), 핵심 객체들과 함수들이 있다. 문법은 Java 와 C 언어를 차용하여, 이 두 언어의 구조적 특성을 자바스크립트가 가지고 있는 경우도 많다. 가장 큰 차이점은, 자바스크립트에는 클래스가 없다는 것이다; 대신에 클래스 기능은 객체 prototype 에 의해 구현된다. 또 다른 차이점은 함수가 객체라는 것이다; 실행코드를 가지고 있는 함수를 다른 객체들처럼 이리 저리 전달할 수 있다.

모든 언어들의 가장 기본적인 구성요소라고 할 수 있는 "타입" 부터 먼저 살펴보자. 자바스크립트 프로그램은 값을 다루며, 이 값들은 타입을 가진다. 자바스크립트의 타입들은 다음과 같다:

... 아, 그리고 Undefined 와 Null 이 있다. 특별한 객체 (object) 타입인 Arrays 도 있다. Dates Regular Expressions 객체 타입도 있다. 그리고 정확히 말하자면, 함수도 특별한 객체 타입이다. 따라서 타입들을 좀 더 자세히 나열하면 다음과 같다:

  • Number
  • String
  • Boolean
  • Object
    • Function
    • Array
    • Date
    • RegExp
  • Null
  • Undefined

Error 타입도 있긴 하다. 이 문서에서는 편의상, 첫번째 리스트를 이용하여 설명하겠다.

Numbers

자바스크립트의 숫자는, 표준 문서에 따르면 "double-precision 64-bit format IEEE 754 values" 이다. 자바스크립트에는 integer (정수) 라는 것이 없다. 따라서 산술 표현을 다룰 때, 조심해야 한다:

0.1 + 0.2 == 0.30000000000000004

하지만 실제로는, 브라우저들은 integer 값을 32-bit integer 로 처리 하는 경우가 많다. 자세한 사항은 http://www.hunlock.com/blogs/The_Complete_Javascript_Number_Reference">The Complete JavaScript Number Reference 을 참조하기 바란다.

더하기, 빼기, modulus 등등의 수칙 연산 이 제공된다. 좀전에 얘기하길 까먹었는데, Math 라는 내장 객체를 이용하여, 고급 계산을 할 수가 있다:

Math.sin(3.5);
var d = Math.PI * r * r;

parseInt() 를 이용하여, string 을 integer 로 변환할 수 있다. 변환시 사용할 base 를 두번째 인자로 받는다:

> parseInt("123", 10)
123
> parseInt("010", 10)
10

base 인자를 전달하지 않으면, 이전 브라우저들 (2013 년 이전의) 에서 예상치 못한 결과를 얻을 수 있다:

> parseInt("010")
8

parseInt 가 첫 0 을 보고 base 를 8 로 사용했기 때문이다.

2 진수를 10 진수로 바꾸려면:

> parseInt("11", 2)
3

float 숫자도 parseFloat() 를 이용하여 변환할 수 있다. 이 함수는 항상 base 10 을 사용한다.

+ 연산자를 이용하여 값을 숫자로 변환할 수 있다:

> + "42"
42

string 이 숫자로 변형될 수 없을 때에는 NaN (Not a Number) 이라는 특별한 값이 리턴된다.

> parseInt("hello", 10)
NaN

NaN 은 전염성이 강하다: NaN 을 어떤 수식에 입력하면, 수식에 관계없이 결과값이 NaN 이 된다:

> NaN + 5
NaN

isNaN() 을 이용하여 NaN 인지 확인할 수 있다:

> isNaN(NaN)
true

Infinity-Infinity 라는 값들도 있다:

> 1 / 0
Infinity
> -1 / 0
-Infinity

isFinite() 을 이용하여 Infinity, -Infinity NaN 을 테스트할 수 있다:

> isFinite(1/0)
false
> isFinite(-Infinity)
false
> isFinite(NaN)
false

Note: parseInt() parseFloat() 함수는, 문자열의 캐릭터를 하나씩 읽다가 지정한 숫자 형태에 맞지 않는 캐릭터를 만나면, 이전까지 읽은 숫자를 리턴한다. 반면에 "+" 연산자는, 하나의 캐릭터라도 이상이 있으면 NaN 을 리턴한다. 콘솔에서 "10.2abc" 라는 값을, 각각의 함수들에게 넘겨보면, 무슨 얘기인지 쉽게 이해가 될 것이다.

Strings

자바스크립트의 string 은 캐릭터의 나열이다. 좀더 정확히 말하면, 각 캐릭터가 16-bit 숫자로 표현되는 Unicode characters 의 나열이다. 로컬라이징을 계획하고 있는 사람들에게는 희소식이다.

하나의 character 를 표현하려면, 길이가 1 인 string 을 사용한다.

string 의 길이는 length 속성을 이용하여 얻는다:

> "hello".length
5

string 도 객체라는 얘기를 한적이 있는가? string 은 methods 도 가지고 있다:

> "hello".charAt(0)
h
> "hello, world".replace("hello", "goodbye")
goodbye, world
> "hello".toUpperCase()
HELLO

Other types

자바스크립트는 'object' 타입의 객체이면서, 의도적으로 "없는 값" (non-value) 을 나타내는 null 과, 'undefined' 타입의 객체이면서, 초기화 하지 않은 값을 나타내는 undefined 를 구별한다. 변수에 대해 곧 다룰테지만, 자바스크립트에서는 값을 할당하지 않고 변수를 선언할 수 있다. 이 때의 변수의 타입은 undefined 가 된다.

truefalse 값을 가질 수 있는, boolean 타입도 제공한다. 다음 법칙에 따라서, 어떤 값이든지 boolean 값으로 변형될 수 있다.

  1. false, 0, 빈 문자열 (""), NaN, null, undefined 는 모두 false 로 변형된다.
  2. 다른 모든 값은 true 로 변형된다.

Boolean() 함수를 사용하여 명시적으로 변환할 수도 있다:

> Boolean("")
false
> Boolean(234)
true

하지만, 자바스크립트가 자동으로 변환을 하여주기 때문에, 이렇게 하는 경우는 드물다.

&& (논리적 and), || (논리적 or), ! (논리적 not) 등의 boolean 연산자가 제공된다.

Variables

새로운 변수는 var 키워드를 이용하여 선언한다:

var a;
var name = "simon";

선언시에 값을 지정하지 않으면, 변수의 타입은 undefined 가 된다.

Java 등의 다른 언어와 다른 점은, block (역주: { ... }) 이 scope 을 만들지 않는다는 것이다; 함수만이 scope 을 만든다. if 문 안에, var 를 이용하여 변수를 선언하면, 이 변수는 속해있는 함수 전체에서 접근이 가능하다.

Operators

숫자 관련 연산자들은 +, -, *, /, % (나머지 연산자) 등이 있다. 값은 = 로 할당하고, +=-= 도 사용할 수 있다.

x += 5
x = x + 5

++-- 을 이용하여 값을 증가/감소 시킬 수 있다. 앞에 붙일 수도 뒤에 붙일 수도 있다.

+ operator 는 string 을 합칠 때도 사용할 수 있다.

> "hello" + " world"
hello world

string 을 숫자 (또는 다른 값) 에 더하면, 모든 것이 string 으로 먼저 변환된다:

> "3" + 4 + 5
345
> 3 + 4 + "5"
75

이 기능을 활용하면, 빈 string 을 더하여, string 으로 변환할 수 있다.

<,>,<=,>= 등으로 Comparisons (비교) 할 수 있다. 스트링과 숫자 모두 가능하다. 값이 같은 것을 확인하는 것은 약간 설명이 필요하다. == 을 사용할 때, 다른 타입의 값 두개를 주면, 타입을 변환한다:

> "dog" == "dog"
true
> 1 == true
true

타입 변환없이, 값이 같은지를 확인하려면 === 을 사용한다:

> 1 === true
false
> true === true
true

!=!== 도 사용할 수 있다.

자바스크립트는 bitwise operations 도 제공한다.

Control structures

자바스크립트는 C 언어류의 언어들과 유사한 제어문들을 제공한다. 조건문은 ifelse 를 사용한다:

var name = "kittens";
if (name == "puppies") {
  name += "!";
} else if (name == "kittens") {
  name += "!!";
} else {
  name = "!" + name;
}
name == "kittens!!"

whiledo-while 문도 제공한다. do-while 은 loop 이 최소한 한번 실행되기를 원할 때 유용하게 사용할 수 있다:

while (true) {
  // 무한 룹!!
}

var input;
do {
  input = get_input();
} while (inputIsNotValid(input))

자바스크립트의 for 문은 C 나 Java 의 for 문과 동일하다:

for (var i = 0; i < 5; i++) {
  // Will execute 5 times
}

&&|| 연산자는 short-circuit 로직을 사용한다. 즉, 첫번째 연산자에 따라, 두번째 피연산자를 실행할 수도 있고, 그렇지 않을 수도 있다. 객체의 속성을 접근하기 전에 null 객체인지 확인할 때 유용하게 쓸 수 있다:

var name = o && o.getName();

또는 기본 값을 설정할 때 사용할 수 있다:

var name = otherName || "default";

다음과 같은 조건문도 가능하다:

var allowed = (age > 18) ? "yes" : "no";

switch 문을 이용하여 숫자나 문자열에 따라 다른 코드를 실행할 수 있다:

switch(action) {
    case 'draw':
        drawit();
        break;
    case 'eat':
        eatit();
        break;
    default:
        donothing();
}

break 를 추가하지 않으면, 다음 case 문이 실행된다. 보통은 break 를 추가하지만, 정말 다음 case 문을 실행하기를 의도한 것이라면, 주석으로 명시하는 것이 알아보기에 좋다:

switch(a) {
    case 1: // 다음 case 로 넘어간다
    case 2:
        eatit();
        break;
    default:
        donothing();
}

default 문은 선택사항이다. switch 와 case 부분에 표현식을 사용할 수도 있다; 두 값은 === 을 이용하여 비교된다.

switch(1 + 3) {
    case 2 + 2:
        yay();
        break;
    default:
        neverhappens();
}

Objects

자바스크립트의 객체는 단순히 name-value 페어들의 묶음이다. 다음 것들과 비슷하다:

  • Dictionaries in Python
  • Hashes in Perl and Ruby
  • Hash tables in C and C++
  • HashMaps in Java
  • Associative arrays in PHP

이 데이터 구조가 이렇게 많이 사용된다는 것은, 이 데이터 구조로 할 수 있는 일이 많다는 반증이다. 자바스크립트의 모든 것이 객체이기 때문에, 자바스크립트 프로그램은 hash table 을 많이 뒤져 보게 된다. 해쉬 테이블이 빠르니 다행이다!

"name" 부분은 자바스크립트의 string 이고, value 부분은 아무 자바스크립트의 값이 될 수 있다 - 다른 객체가 될 수도 있다. 따라서 복잡한 자료구조를 만들 수 있다.

빈 객체를 만드는 두가지 방법이 있다:

var obj = new Object();

그리고 다음 처럼도 할 수 있다:

var obj = {};

두개의 기능은 같다; 두번째 것은 object literal 문법이라고 하고, 좀 더 편리하다. JSON 도 이 문법을 사용한다. 가능하면 이 문법을 사용하는 것이 좋다.

생성 후에는, 객체의 속성을 다음과 같은 두가지 방법으로 접근할 수 있다:

obj.name = "Simon";
var name = obj.name;

그리고 다음처럼...

obj["name"] = "Simon";
var name = obj["name"];

역시나 같은 뜻이다. 두번째 방법은 속성의 이름을 string 으로 준다는 장점이 있다. 즉 실시간으로 string 의 값을 변경할 수 있는 것이다. 하지만 이 방법을 쓰면 자바스크립트 엔진과, minfier 의 최적화가 조금 덜 되는 단점도 있다. string 방법을 사용하면, 예약어 를 속성이름으로 사용할 수 있다:

obj.for = "Simon"; // Syntax error, because 'for' is a reserved word
obj["for"] = "Simon"; // works fine

Object literal 문법은 object 전체를 초기화할 때 사용할 수 있다:

var obj = {
    name: "Carrot",
    "for": "Max",
    details: {
        color: "orange",
        size: 12
    }
}

속성값은 chain (연결) 하여 사용할 수 있다:

> obj.details.color
orange
> obj["details"]["size"]
12

Arrays

자바스크립트에서 array 는 사실 특별한 타입의 object 이다. 일반 object 처럼 동작하지만 (숫자 속성은 [] 문법으로만 접근할 수 있다), length 라는 특별한 속성이 있다. 이 값은 항상 array 의 가장 큰 index + 1 이다.

예전에는 array 를 다음처럼 만들었다:

> var a = new Array();
> a[0] = "dog";
> a[1] = "cat";
> a[2] = "hen";
> a.length
3

다음처럼 array literal 을 이용하면 편리하다:

> var a = ["dog", "cat", "hen"];
> a.length
3

array literal 마지막에 , 을 추가하면 어떤 브라우저에서는 동작하지 않을 수도 있다. , 을 끝에 추가하지 말아라.

array.length 는 배열안의 아이템의 갯수가 아닐 수도 있다. 다음 예를 보라:

> var a = ["dog", "cat", "hen"];
> a[100] = "fox";
> a.length
101

array 의 length 는 가장 큰 index + 1 이다.

존재하지 않는 index 를 접근하면, undefined 를 얻는다:

> typeof a[90]
undefined

다음처럼 array 를 iterate (하나씩 방문) 할 수 있다:

for (var i = 0; i < a.length; i++) {
    // Do something with a[i]
}

length 속성을 매번 찾지 않고, 다음처럼 하면 조금 더 효율적이다:

for (var i = 0, len = a.length; i < len; i++) {
    // Do something with a[i]
}

다음 처럼 할 수도 있다: (역주: 읽기가 어려워서, 그리 좋아보이진 않는다)

for (var i = 0, item; item = a[i++];) {
    // Do something with item
}

두개의 변수를 선언하고, for 의 가운데 부분에서 값을 할당하고, true 인지 확인한다 - true 이면 룹을 실행한다. i 가 매번 증가되기 때문에, array 의 item 을 차례로 얻어온다. (undefined) 같은 "falsy" (역주: false 로 변형되는) item 을 만나면 loop 을 멈춘다.

이 방법은 array 가 "falsy" 값을 가지지 않는다는 것을 알고 있을때에만 사용하라. 숫자 데이터들이라면 0 을 가질 수 있고, string 데이터들이라면 "" 을 가질 수 있으니, 이 방법을 사용하면 안된다. 이 때에는 i, len 방법을 사용하라.

for...in 을 사용하여 반복문을 만들 수도 있다. Array.prototype 에 새로운 속성을 추가하면, 이 반복문에서 이 속성들도 iterate 하게 될 것이다:

for (var i in a) {
  // Do something with a[i]
}

아이템을 array 에 추가하려면, 다음과 같이 하는 것이 안전하다:

a[a.length] = item;                 //  a.push(item); 와 같다

a.length 가 가장 큰 index + 1 이기 때문에, array 의 끝에 새 공간에 값을 할당하는 것이 된다.

Array 에는 다음과 같은 method 들이 있다:

Method name Description
a.toString()  
a.toLocaleString()  
a.concat(item[, itemN]) Returns a new array with the items added on to it.
a.join(sep)  
a.pop() Removes and returns the last item.
a.push(item[, itemN]) Push adds one or more items to the end.
a.reverse()  
a.shift()  
a.slice(start, end) Returns a sub-array.
a.sort([cmpfn]) Takes an optional comparison function.
a.splice(start, delcount[, itemN]) Lets you modify an array by deleting a section and replacing it with more items.
a.unshift([item]) Prepends items to the start of the array.

Functions

자바스크립트를 이해하려면, 함수를 이해해야 한다. 간단한 함수는 다음처럼 생겼다:

function add(x, y) {
    var total = x + y;
    return total;
}

자바스크립트 함수는 0 개 이상의 이름있는 인자를 갖는다. 함수는 자신의 변수를 선언할 수 있다. return 을 이용하여 어느 때나 함수를 종료하고, 값을 리턴할 수 있다. return 문을 사용하지 않으면 (또는 return 을 값 없이 사용하면), 자바스크립트는 undefined 를 리턴한다.

인자는 생략이 가능하다. 생략한 경우 인자는 undefined 값을 갖는다.

> add()
NaN // undefined 를 + 하였다.

함수가 예상하는 것보다 더 많은 인자를 전달할 수도 있다:

> add(2, 3, 4)
5 // 처음 두개를 합한다. 4 는 무시된다.

arguments 라는 변수는, 함수에 전달된 모든 인자들을 array 형태로 가지고 있다. add 함수가 임의의 인자 갯수를 받도록 변경하여 보자:

function add() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i < j; i++) {
        sum += arguments[i];
    }
    return sum;
}

> add(2, 3, 4, 5)
14

평균을 구하는 함수를 작성하여 보자:

function avg() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i < j; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
}
> avg(2, 3, 4, 5)
3.5

avg() 함수는 , 로 구분된 인자들을 받는다 - array 의 평균값을 구하려면 어떻게 할까? 다음처럼 함수를 만들 수 있다:

function avgArray(arr) {
    var sum = 0;
    for (var i = 0, j = arr.length; i < j; i++) {
        sum += arr[i];
    }
    return sum / arr.length;
}
> avgArray([2, 3, 4, 5])
3.5

하지만, 우리가 이미 만들어 놓은 함수를 이용할 수 있으면 더 좋을 것이다. 자바스크립트에서는, apply() 라는 함수 객체의 method 를 이용하여, array 를 인자로 하여, 함수를 호출할 수 있다:

> avg.apply(null, [2, 3, 4, 5])
3.5

apply() 의 두번째 인자가, 함수에게 전달할 인자들의 배열이다. 첫번째 인자는 조금 있다 설명할 것이다. 위 문장에서, 함수가 객체라는 것을 다시 한번 볼 수 있다.

자바스크립트에서는 이름 없는 함수를 만들 수 있다.

var avg = function() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i < j; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
}

function avg() 형태와 뜻은 같다. 표현식을 사용할 자리에 함수의 정의를 적을 수 있다. 이를 이용한 유용한 기술들이 많이 있다. 다음은 local 변수를 "숨기는" 방법을 보여준다 - 마치 C 의 block scope 같이 말이다:

> var a = 1;
> var b = 2;
> (function() {
    var b = 3;
    a += b;
})();
> a
4
> b
2

함수를 재귀호출할 수 있다. 브라우저의 DOM 같은 tree 구조를 다룰 때 유용하다.

function countChars(elm) {
    if (elm.nodeType == 3) { // TEXT_NODE
        return elm.nodeValue.length;
    }
    var count = 0;
    for (var i = 0, child; child = elm.childNodes[i]; i++) {
        count += countChars(child);
    }
    return count;
}

이름 없는 함수를 재귀적으로 호출할 수 있을까? 이름이 없는데 말이다. "이름 있는, 이름없는 함수" (named anonymous function) 을 쓰면 된다.

var charsInBody = (function counter(elm) {
    if (elm.nodeType == 3) { // TEXT_NODE
        return elm.nodeValue.length;
    }
    var count = 0;
    for (var i = 0, child; child = elm.childNodes[i]; i++) {
        count += counter(child);
    }
    return count;
})(document.body);

이름 없는 함수에 주어진 이름은 이 함수의 scope 안에서만 접근 가능하다.

Custom objects

전통적인 객체지향 프로그래밍에서는, 객체는, 데이터와 이 데이터를 사용하는 함수의 묶음이다. 자바스크립트는 prototype 기반의 언어로, C++ 이나 Java 에 있는 클래스 문장이 없다. 대신에, 자바스크립트는 함수를 클래스로 사용한다. first name 과 last name 을 가지는 person 객체가 있다고 하자. "first last" 또는 "last, first" 의 형태로 이름을 보여주는 방법이 있다고 하자. 함수와 객체를 이용하여 다음처럼 할 수 있다:

function makePerson(first, last) {
    return {
        first: first,
        last: last
    }
}
function personFullName(person) {
    return person.first + ' ' + person.last;
}
function personFullNameReversed(person) {
    return person.last + ', ' + person.first;
}
> s = makePerson("Simon", "Willison");
> personFullName(s)
Simon Willison
> personFullNameReversed(s)
Willison, Simon

동작은 한다, 하지만 지저분하다. 이 방법을 사용하면, global namespace 에 엄청나게 많은 함수들을 갖게 될 것이다. 우리는 함수를 객체에 붙일 수 있는 방법이 필요하다. 함수도 객체라는 사실을 이용하여 다음처럼 할 수 있다:

function makePerson(first, last) {
    return {
        first: first,
        last: last,
        fullName: function() {
            return this.first + ' ' + this.last;
        },
        fullNameReversed: function() {
            return this.last + ', ' + this.first;
        }
    }
}
> s = makePerson("Simon", "Willison")
> s.fullName()
Simon Willison
> s.fullNameReversed()
Willison, Simon

this[33] 라는 새로운 keyword 가 등장하였다. 함수안에서 this 는 현재 객체를 나타낸다. 함수를 어떻게 호출하였는지에 따라 this 의 의미가 달라지게 된다. 객체에 dot notation or bracket notation (anObject.foo, anObject["foo"]) 을 이용하여 함수를 호출하였다면, 이 객체가 this 가 된다. 그렇지 않다면 this 는 global 객체를 가리키게 된다. 자주 범하는 실수중의 하나이다:

> s = makePerson("Simon", "Willison")
> var fullName = s.fullName;
> fullName()
undefined undefined

fullName() 을 호출할 때, this 는 global 객체가 된다. 하지만 이 global 객체에 firstlast 변수가 없기 때문에, undefined 를 얻게 된다.

this 를 이용하여 makePerson 함수를 개선할 수 있다:

function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = function() {
        return this.first + ' ' + this.last;
    }
    this.fullNameReversed = function() {
        return this.last + ', ' + this.first;
    }
}
var s = new Person("Simon", "Willison");

new 라는 새로운 keyword 를 사용하였다. newthis 와 깊은 연관이 있다. new 는 새로운 빈 객체를 만들고, 함수를 호출하며, 이 함수에서 this 는 생성된 객체가 된다. new 를 이용하여 호출할 함수들을 constructor 함수라고 부른다. 관례적으로 이 함수들의 이름은 대문자로 시작하게 한다.

Person 객체는 많이 개선되었지만, 여전히 지저분한 구석이 있다. person 객체를 만들때마다 함수 객체들을 새로 만들고 있는 것이다 - 이 코드들을 공유하는 것이 더 좋지 않겠는가?

function personFullName() {
    return this.first + ' ' + this.last;
}
function personFullNameReversed() {
    return this.last + ', ' + this.first;
}
function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = personFullName;
    this.fullNameReversed = personFullNameReversed;
}

함수를 한번만 생성하고, constructor 에서 이 함수 객체를 참조하였다. 더 개선할 수 있는가? 그렇다:

function Person(first, last) {
    this.first = first;
    this.last = last;
}
Person.prototype.fullName = function() {
    return this.first + ' ' + this.last;
}
Person.prototype.fullNameReversed = function() {
    return this.last + ', ' + this.first;
}

Person.prototypePerson 의 모든 객체가 공유하는 객체이다. "prototype chain" 이라는 것을 만든다: Person 의 속성을 접근할 때, Person 이 이 속성을 가지고 있지 않으면, 자바스크립트는 Person.prototype 에 이 속성이 존재하는지 확인한다. 즉 Person.prototype 에 설정한 것들은, Person 의 모든 객체들에서 접근할 수 있다.

이것은 아주 강력하다. 자바스크립트에서는 어떤 것의 prototype 을 아무때나 변경할 수 있다. 즉 존재하는 객체에 method 를 추가할 수도 있다:

> s = new Person("Simon", "Willison");
> s.firstNameCaps();
TypeError on line 1: s.firstNameCaps is not a function
> Person.prototype.firstNameCaps = function() {
    return this.first.toUpperCase()
}
> s.firstNameCaps()
SIMON

또한, 자바스크립트가 기본 제공하는 객체들의 prototype 에도 추가가 가능하다. String 에, string 을 거꾸로 리턴하는 함수를 추가하여 보자:

> var s = "Simon";
> s.reversed()
TypeError on line 1: s.reversed is not a function
> String.prototype.reversed = function() {
    var r = "";
    for (var i = this.length - 1; i >= 0; i--) {
        r += this[i];
    }
    return r;
}
> s.reversed()
nomiS

string literal 에도 이 함수를 사용할 수 있다!

> "This can now be reversed".reversed()
desrever eb won nac sihT

잠시 언급한 것처럼, prototype 은 chain 형태이다. 이 chain 의 root (최상위) 는 Object.prototype 이고 이 prototype 은 toString() 을 가지고 있다 - 객체를 문자열로 나타낼때 이 함수가 사용된다. Person 객체를 디버깅할 때 유용하게 사용할 수 있다:

> var s = new Person("Simon", "Willison");
> s
[object Object]
> Person.prototype.toString = function() {
    return '<Person: ' + this.fullName() + '>';
}
> s
<Person: Simon Willison>

avg.apply() 의 첫번째 인자로 null 을 전달한 것을 기억하는가? 이제 그 첫번째 인자를 설명할 수 있다. apply() 의 첫번째 인자는 this 가 될 객체이다. 예를들어 new 를 다음처럼 구현할 수 있다:

function trivialNew(constructor) {
    var o = {}; // 객체를 생성한다
    constructor.apply(o, arguments);
    return o;
}

prototype chain 을 설정하지 않기 때문에, new 랑 완전히 동일한 기능을 하는 것은 아니다.

call 이라는 함수는 apply() 와 같은데, 인자를 배열대신, , 구분된 인자 목록으로 받는다는 것이 다르다.

function lastNameCaps() {
    return this.last.toUpperCase();
}
var s = new Person("Simon", "Willison");
lastNameCaps.call(s);
// Is the same as:
s.lastNameCaps = lastNameCaps;
s.lastNameCaps();

Inner functions

함수 내에서 다른 함수를 선언할 수 있다. makePerson() 함수에서 이미 한번 봤었다. 중요한 것은, 부모 함수의 scope 에 있는 변수들을 안쪽 함수에서 접근할 수 있다는 것이다.

function betterExampleNeeded() {
    var a = 1;
    function oneMoreThanA() {
        return a + 1;
    }
    return oneMoreThanA();
}

이 기능은 코드를 관리하기 편하게 해준다. 즉 어떤 함수가 다른 함수들을 사용하는데, 이 함수들을 다른 곳에서는 사용하지 않는다면, 이 함수들을 함수의 내부에 정의할 수 있다. global 에 존재하는 함수의 갯수를 줄이는 것은 언제나 좋은 일이다.

또한, 안쪽의 함수가, 바깥 함수의 변수를 공유할 수 있기 때문에, global 변수를 선언하지 않고, 함수안의 변수를 공유할 수도 있다. 이 방법을 이용할 때에는 주의가 필요하나, 유용한 기능임에는 틀림없다.

Closures

자바스크립트가 제공하는 가장 강력하고도 헷갈리는 개념중 하나를 설명할 차례이다. 다음 코드의 결과는 무엇인가?

function makeAdder(a) {
    return function(b) {
        return a + b;
    }
}
x = makeAdder(5);
y = makeAdder(20);
x(6)
?
y(7)
?

makeAdder 라는 이름에 힌트가 있다: 즉 새로운 add 함수를 만든다. 이 함수는, 인자 하나를 받아서, 함수 생성시 받은 인자와 합한다.

좀전에 본 inner function 에서 본 것과 같은 원리이다: 함수안에 정의된 함수는, 바깥 함수의 변수를 접근할 수 있다. 차이점은, 바깥 함수가 return 하였다는 것이다. 상식적으로는 return 한 함수의 local 변수는 더이상 존재하지 않게 된다. 하지만 그들은 여전히 존재한다 - 그렇지 않다면, adder 함수는 동작하지 않을 것이다. 더욱이, makeAdder 의 변수들의 두가지 "복사본" 이 존재한다 - 한 곳에서는 a 가 5 이고, 다른 곳에서 a 는 20 이다. 따라서 위 함수의 결과는 다음과 같다:

x(6) // returns 11
y(7) // returns 27

자바스크립트가 함수를 실행할 때, 함수 내의 local 변수를 저장하기 위해 scope 객체가 생성된다. scope 객체는, 전달된 함수 인자와 함께 초기화된다. 함수가 실행될 때마다 새로운 scope 객체가 생성되며, 자바스크립트 코드에서 이 scope 객체를 참조할 수 있는 방법은 없다. 즉 현재 scope 객체의 속성들을 iterate 할 수 있는 방법은 없다.

makeAdder 가 호출되면, makeAdder 를 호출할 때 전달한 a 라는 속성을 갖는 scope 객체가 생성된다. 그리고 makeAdder 는 새로 생성한 함수를 리턴한다. 보통, 함수가 리턴할 때, 자바스크립트의 garbage collector 가 makeAdder 호출로 인해 만들어진 scope 객체를 지울테지만, 이 리턴된 함수가 scope 객체에 대한 참조를 가지고 있기 때문에, scope 객체가 사라지지 않게 된다.

Scope 객체들은 scope chain 이라 불리는 chain 을 형성한다. 마치 prototype chain 처럼 말이다.

closure 는 함수와, 함수가 생성된 scope 객체의 조합이다.

closure 를 이용하여 상태를 저장할 수 있다 - 객체 대신에 사용되곤 한다.

Memory leaks

클로저를 사용하면 Internet Explorer 에서 메모리 누수를 발생하기가 매우 쉽다. 자바스크립트는 garbage collected 언어이다 - 객체 생성시 메모리가 할당되고, 객체에 대한 참조가 모두 없어질 때 메모리가 해제된다. host 환경에서 제공하는 객체들은 그 환경에 의해 처리된다.

브라우저 host 는 HTML 페이지를 표현하는 객체들, 즉 DOM 의 객체들, 을 관리한다. 브라우저가 이들 객체를 위한 메모리를 할당하고 해제한다.

Internet Explorer 는 이를 위해 자바스크립트가 제공하는 garbage collection 과는 별도의 garbage collection 을 사용한다. 이 두 garbage collection 의 상호작용이 메모리 누수를 발생시킨다.

IE 에서 자바스크립트 객체와 native 객체 사이에 순환 참조가 만들어지면 메모리 누수가 발생한다. (역주: 오래된 ie 에 대한 얘기로, 크게 신경쓰지 않아도 된다..) 다음 예를 보라:

function leakMemory() {
    var el = document.getElementById('el');
    var o = { 'el': el };
    el.o = o;
}

IE 는 브라우저가 완전히 재시작 될 때까지, elo 의 메모리를 해제하지 않는다.

위 경우는 아마도 그냥 무시될 것이다; 메모리 누수는, 큰 자료구조에서 많은 양의 메모리 누수를 발생시키거나, 반복문에서 메모리 누수를 발생시키는 경우에, 문제가 된다.

이렇게 명확히 보이지 않는 메모리 누수도 많이 있다 - 여러 겹에 걸쳐 참조를 하는 경우에, 순환 참조가 있음을 알기가 쉽지 않다.

클로져를 이용할 때에도 메모리 누수를 만들기 쉽다:

function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
}

위 코드는 요소가 클릭되었을 때 빨간색으로 변하게 한다. 메모리 누수가 생겼다. 이름없는 내부 함수로 인해 만들어진 closure 에 el 이 포함되기 때문이다. 자바스크립트 객체 (내부 함수) 에서 el 을 참조하고, native 객체 el 이 내부함수를 참조하는, 순한 참조가 만들어졌다.

해결책이 몇가지 있다. 가장 간단한 방법은 el 변수를 사용하지 않는 것이다:

function addHandler(){
    document.getElementById('el').onclick = function(){
        this.style.backgroundColor = 'red';
    }
}

놀랍게도, closure 로 인해 생긴 순환 참조를 끊는 하나의 방법으로 새로운 closure 를 추가하는 방법이 있다:

function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
    };
    (function() {
        var el = document.getElementById('el');
        el.onclick = clickHandler;
    })();
}

안쪽 함수는 즉시 실행되며, clickHandler 로 인해 생성된 closure 로 부터 자신의 내용 (역주: el) 을 숨긴다.

또 다른 방법은 window.onunload event 에서 순환참조를 끊는 것이다. 많은 event 라이브러리들이 이렇게 한다. bfcache in firefox 1.5 기능을 끄기 때문에, 다른 이유가 있지 않는한, firefox 에서는 unload 리스너를 등록하지 말아라.

Original Document Information

  • Author: Simon Willison
  • Last Updated Date: March 7, 2006
  • Copyright: © 2006 Simon Willison, contributed under the Creative Commons: Attribute-Sharealike 2.0 license.
  • More information: For more information about this tutorial (and for links to the original talk's slides), see Simon's Etech weblog post.
추천추천 : 248 추천 목록
번호 제목
3,012
 텍스트 줄바꿈, 글자자르기 CSS
3,011
 jQuery Mobile에서 유용한 코드 10가지.
3,010
 [PHP] dirname()함수와 $_SERVER 관련 상수들
3,009
 [PHP] 파일 크기, 사이즈 불러오는 함수, filesize()
3,008
 [jQuery] jQuery Quick API
3,007
 [ transition ] 링크 hover 색상 변화 속도 조절
3,006
 PHP 5.3.0 에서 사라진 함수들 대체
3,005
 어떤 파일들이 include 나 require 되었는지 확인하는 함수(get_included_files)
3,004
 PHP 날짜 형식 및 계산하기(날짜 더하고 빼기)
3,003
 jQuery Mobile에서 유용한 코드 10가지.
3,002
 값이 배열 안에 존재하는지 확인하는 in_array함수
3,001
 사용자가 웹브라우저에서 뒤로가기를 했을때 감지하는 방법
3,000
 [jQuery]버튼 활성화, 비활성화
2,999
 jQuery show() / hide() / toggle() 사용법
2,998
 jquery 여러가지 이벤트
2,997
 border-radius 속성
2,996
 네이버 오픈API 음성합성 API 사용하는 PHP 샘플코드
2,995
 UTF8 한글 자르기..
2,994
 iconv 에러 발생시 계속 처리하기 옵션
2,993
 [PHP] 현재 페이지의 도메인 , URL 정보 알아내기.
2,992
 [PHP] 막강 기능 배열..
2,991
 [CSS] - Input clear `X ` 버튼 제거 ( IE, Chrome, Firefox )
2,990
 [Mobile] - 모바일웹 Href 태그속성들
2,989
 [JqueryMobile] - 현재화면의 가로세로 사이즈 구하기
2,988
 [JqueryMobile] - 화면의 가로, 세로 사이즈 구하는 방법
2,987
 jquery로 가로 넓이(width), 세로 높이(height) 자동 조절
2,986
 iframe 높이 jquery로 자동조절하기
2,985
 jQuery 오른쪽 영역의 높이를 왼쪽 영역의 높이와 동일하게 하기
2,984
 jquery에서 테이블 짝수, 홀수 번째 TR 배경색 변경하기
2,983
 jquery에서 테이블에 마우스 오버시 해당 행의 배경색상 변경하기
2,982
 jquery 스크립트내 특정값 확인하기 (디버깅)
2,981
 jquery cookie (jquery.cookie.js)
2,980
 jquery div 기본 넓이, 높이 계산 및 padding, border 포함 하기
2,979
 jquery 다른버전 추가 사용시 충돌 방지 (카페24 스마트디자인 기본내장 jquery 1.4.4 버전과 충돌시 해결방법)
2,978
 특정 페이지 종료시 확인 경고창 출력
목록
추천음악방송
인기트로트메들리모음
추천 트로트
감성 트로트
밤무대 애창곡
전자올겐 경음악
작은 음악다방
종합성인가요방
카바레 음악
트롯디스코팡팡
관광 메들리
트롯카페
가요감상실
추억의옛노래
스페셜가요광장
BillBoard Free
Dance&Remix
Pandora TV
추천가요모음
경음악.전자올겐
스페셜음악여행
WOLRD POPs
K-POP\BillBoard
JP\CN
POP TOP BEST
K.R.노래방

추천가요\인기
F뮤직 인기\발라드
F뮤직 애창\트로트
트로트성인가요
인기가요
프리미엄 POP
경음악\기타
프리미엄 최신가요
프리미엄 성인가요
가요1번지(종합)
뮤직하우스
동요\연변\반주\기타
앗싸! 뽕짝
가요 포장마차
가요축제\트롯1번지
댄스\메들리\리믹스
카페\명상\경음악\기타
추천가요\POP\기타
최신가요\인기가요
뮤직트로트 부산광역시부산진구 가야동 ㅣ 개인정보취급방침
Copyright (C) musictrot All rights reserved.