JavaScript 재입문

  • 리비전 슬러그: A_re-introduction_to_JavaScript
  • 리비전 제목: JavaScript 재입문
  • 리비전 아이디: 170456
  • 제작일시:
  • 만든이: Dyhan81
  • 현재 리비전인가요? 아니오
  • 댓글 /* 변수 (Variables) */

리비전 내용

주의: 이 문서는 번역 중 입니다. --Dyhan81 2007년 12월 22일 (토) 19:12 (PST)

들어가며

새삼스래 왠 재입문? JavaScriptthe world's most misunderstood programming language (세계에서 가장 잘못 이해되고있는 프로그래밍 언어)에 소개된 것과 같이 의미있는 비판을 받고 있기 때문입니다. 장난감 정도로 비웃음을 사고 있지만, 그 속기쉬운 이면에는 몇가지 강력한 언어 요소를 내재하고 있습니다. 2005년에는 이 기술에 대한 깊은 이해가 웹 개발자 누구에게 있어서도 중요한 능력이 된다는 것을 보여주는 많은 고급 JavaScript 응용 프로그램들이 나타났습니다.

언어의 역사에서 시작하는 것이 이 이야기를 이해하는데 도움이 됩니다. JavaScript는 1995년 Netscape의 엔지니어 Brendan Eich에 의해 만들어졌고, 이른 1996년에 Netscape 2와 함께 처음 릴리즈 된것입니다. 원래 LiveScript로 불리워지기로 되어있었지만, Sun Microsystem의 Java 언어의 성공에 편승해보려고 두 언어 사이의 공통점이 매우 적음에도 불구하고, 이런 불행이 예견된 마케팅 결정에 따라 이름이 바뀌게 됩니다. 이 사실은 사상 유래가 없는 혼란의 근원이 되어버립니다.

Microsoft는 몇달 후 IE3와 함께 JavaScript와 대부분이 호환되는 JScript로 불리워지는 언어를 발표합니다. Netscape는 1997년에 ECMAScript 표준의 첫번째 판이 되는 JavaScript를 유럽 표준화 단체인 Ecma International에 보냅니다. 표준은 1999년에 ECMAScript edition 3에 따라 큰 규모의 개정을 거친 후, 현재 4번째 판이 제정 준비 중에 있지만 유래없이 아주 안정된 상태로 계속 유지되고 있습니다.

이 안정 상태는 다양한 코드 구현을 하는데 충분한 시간이기 때문에 개발자들에게는 더없이 좋은 소식입니다. 저는 대부분 이 3판에 집중하려고 합니다. 친숙함을 위하여 JavaScript 전반에 걸쳐 여기서 사용된 용어를 준수하겠습니다.

대부분의 프로그래밍 언어와는 달리, JavaScript 언어는 입출력 개념이 없습니다. 호스트 환경 아래에서 스크립트 언어로서 동작하도록 디자인 되어있고, 따라서 외부 세계와 통신하기위해 호스트 환경이 제공하는 메커니즘에 의존합니다. 대부분의 경우 일반적인 호스트 환경은 브라우저이지만 JavaScript 인터프리터는 Adobe Acrobat, Photoshop, Yahoo! 위젯 엔진, 등의 제품에서도 발견할 수 있습니다.

개요

어떤 언어에서라도 기초가 되는 부분인 타입을 살펴보는 것부터 시작해봅시다. JavaScript 프로그램은 값을 다루고 해당 값은 모두 타입을 가지고 있습니다. JavaScript의 타입은 다음과 같습니다:

... 오, 그리고 약간 특별한 타입인 정의되지않음(Undefined) 와 널(Null) 이 있습니다. 또한 객체의 특별한 종류인 배열(Array) 객체. 그리고 자유롭게 사용할 수 있는 날짜(Date) 객체정규식(RegExp) 객체가 있습니다. 그리고 기술적으로 정확히 말해 함수(Function)는 단지 객체의 특별한 타입으로 취급됩니다. 따라서 타입 구조도를 정리해보면 다음과 같이 됩니다:

  • 수 (Number)
  • 문자열 (String)
  • 부울 (Boolean)
  • 객체 (Object)
    • 함수 (Function)
    • 배열 (Array)
    • 날짜 (Date)
    • 정규식 (RegExp)
  • 널 (Null)
  • 정의되지않음 (Undefined)

그리고 또 몇 가지 오류 타입이 내장되어 있습니다. 그렇지만 처음 구조도를 기억하고만 있으면 다른 것들도 아주 쉽게 이해할 수 있을 것입니다.

수 (Numbers)

설계 명세서에 의하면 JavaScript에서 수는 "이중정밀도 64비트 형식 IEEE 754 값"으로 정의됩니다. 이것은 몇가지 흥미로운 결과를 가져옵니다. JavaScript에는 정수와 같은 것이 존재하지 않으므로, C 나 Java 에서 수학 계산을 한 경험이 있다면 산술할 때 약간 조심할 필요가 있습니다. 다음과 같은 경우를 주의해야 합니다:

0.1 + 0.2 = 0.30000000000000004

덧셈, 뺄셈, 계수 (또는 나머지) 연산을 포함하는 표준 산술 연산자가 지원됩니다. 또한 앞에서 언급하는 것을 깜박 잊은 고급 수학 함수와 상수를 다루기 위한 수학(Math)으로 불리워지는 내장 객체가 있습니다:

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

내장 parseInt() 함수를 사용하여 문자열을 정수로 변환할 수 있습니다. 이는 다음과 같이 옵션으로 주어지는 두번째 매개변수를 밑으로 하여 수행할 수 있습니다:

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

밑을 주지 않으면, 다음과 같이 예상치 못한 결과를 얻을 수 있습니다:

> parseInt("010")
8

이 같은 결과는 parseInt 함수가 0으로 시작되는 문자열을 8진수로 취급하기 때문에 발생합니다.

만약 이진수를 정수로 변환하고 싶다면, 밑을 바꾸기만하면 됩니다:

> parseInt("11", 2)
3

문자열이 수가 아닌 경우 NaN ("Not a Number" (수가 아님)을 줄인 약자)로 불리워지는 특별한 값을 돌려줍니다:

> parseInt("hello", 10)
NaN

NaN 는 독성을 가지고 있습니다: 어떤 수학 연산의 입력값으로써 주어지면 그 결과는 역시 NaN가 되기 때문입니다:

> NaN + 5
NaN

내장 isNaN() 함수를 사용해서 NaN 인지 여부를 검사할 수 있습니다:

> isNaN(NaN)
true

JavaScript는 또 특별한 값 Infinity-Infinity를 가지고 있습니다:

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

문자열 (Strings)

JavaScript에서 문자열은 문자 하나하나가 연결되어 만들어진 것입니다. 좀 더 정확히 말하자면, 각각이 16비트로 표현된 유니코드 문자들이 길게 이어져있는 것입니다. 이는 국제화(i18n, internationalization) 하려하는 누구에게라도 환영받을만한 소식입니다.

한 개의 문자를 나타내려면 길이가 1인 문자열을 사용하면 됩니다.

문자열의 길이를 알고싶다면, 해당 문자열의 length 속성(해당 객체가 소유하고 있는 성질을 나타내는 값)에 접근하면 됩니다:

> "hello".length
5

우리의 첫 JavaScript 객체입니다! 문자열도 역시 객체로 취급된다고 언급했던적이 있죠? 다음과 같이 메소드까지 있는 확실한 녀석입니다:

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

이외의 타입들

JavaScript는 의도적으로 값이 없음을 가리키는 '객체' 타입의 객체인 null과 초기화되지 않은 값 — 아직 어떤 값도 주어지않은(할당되지않은) 변수임을 가리키는 '정의되지 않음' 타입의 객체인 undefined로 구분됩니다. 값에 대해서 나중에 언급할 것이지만 JavaScript에서 변수에 값을 주지않고 선언하는 것이 가능합니다. 이럴 경우, 변수의 타입은 undefined이 되는 것입니다.

JavaScript는 truefalse 값 (둘은 모두 키워드로 예약되어있는 값)을 가질 수 있는 부울 타입을 가지고 있습니다. 다음과 같은 규칙에 따라 어떤 임의의 값을 부울값으로 변환할 수 있습니다:

  1. false, 0, 빈 문자열 (""), 수가 아님을 뜻하는 NaN, null, 와 undefined은 모두 false가 됩니다.
  2. 다른 모든 값은 true가 됩니다.

이 변환은 Boolean() 함수를 써서 명시적으로 이 작업을 수행하실 수 있습니다:

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

하지만 반드시 이렇게 할 필요는 거의 없습니다. JavaScript는 이러한 변환 작업을 if 문 (아래를 보세요)과 같이 부울값이 필요한 경우를 만나게되면 자동으로 사용자가 모르는 사이에 처리해버리기 때문입니다. 이러한 이유로 인해 우리는 가끔 부울 타입으로 변환되었을 때, truefalse이 됨을 의미하는 값들을 각각 "참 값"과 "거짓 값"으로 부를 것입니다. 또는 각각 "참으로 취급되다"와 "거짓으로 취급되다"라는 식으로 불릴 수도 있습니다.

부울 연산자는 && (논리적 와, 그리고), || (논리적 또는), 그리고 ! (논리적 부정)이 지원됩니다. 아래에서 다시 언급하겠습니다.

변수 (Variables)

JavaScript에서 새로운 변수는 var 키워드로 선언됩니다:

var a;
var name = "simon";

만약 변수에 아무런 값을 주지 않고 선언하면 해당 변수의 타입은 undefined가 됩니다. should note the absence of block-scoping in JS JavaScript에서는 블록 유효 범위가 따로 없음을 주의해줘야 함

JavaScript에는 블록 유효 범위가 따로 없습니다. 여기에 대한 것은 블록 문장에서 참고바랍니다.

연산자 (Operators)

JavaScript의 산술 연산자로는 +, -, *, /, %(나머지 연산자)가 있습니다. 값은 = 연산자로 할당할 수 있고, +=-=처럼 다른 연산자를 같이사용해서 할당할 수 있습니다. 이렇게 쓰인 연산자는 x = x 연산자 y와 같은 결과를 나타냅니다.

x += 5
x = x + 5

++-- 를 각각 점진적인 증가와 감소에 사용할 수 있습니다. 이들은 또한 전처리 또는 후처리 연산자로 사용될 수 있습니다.

+ 연산자는 문자열 이어붙이기도 합니다:

> "hello" + " world"
hello world

문자열에 어떤 수 (또는 다른 값)를 더하면 일단 모두 문자열로 바뀌게 됩니다. 다음 예를 보시면 무슨 말씀인지 아실 수 있을겁니다:

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

빈 문자열에 어떤 값을 더하는 것은 해당 값을 문자열로 바꾸는 요령입니다.

JavaScript에서 비교<, >, <=>= 를 통해 가능합니다. 이 연산자들은 문자열과 수 양쪽 모두에서 동작합니다. 상동은 약간 직관성이 떨어지는데 이중 등호 (==) 연산자는 서로 다른 타입을 줄 경우 타입 강제 변환을 수행하기 때문에 다음과 같이 때때로 기대하지 않은 결과를 내보내기 때문입니다:

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

타입 강제 변환을 하지 않게 하려면, 삼중 등호 연산자 (===)를 사용해야합니다:

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

이와 비슷하게 !=!== 연산자가 있습니다.

JavaScript는 값을 비트로 취급하는 연산자도 가지고 있습니다. 사용하고 싶을 때 언제라도 사용할 수 있도록 말이죠.

제어 구조

JavaScript는 C 계열의 다른 언어들과 비슷한 제어 구조를 가지고 있습니다. 조건문은 ifelse를 지원하는데, 원하시는대로 얼마든지 중첩 시켜서 사용할 수 있습니다:

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

JavaScript는 while 반복문과 do-while 반복문도 사용할 수 있습니다. 첫번째 것은 단순 반복에 유용하게 사용할 수 있고, 두번째 것은 반복문이 반드시 적어도 한번이상 실행 되도록 하고 싶을 때 사용할 수 있습니다:

while (true) {
  // an infinite loop!
}

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

JavaScript의 for 반복문은 C 와 Java의 그것과 같습니다. 말하자면, 반복문에 필요한 제어 정보를 한줄에 표현할 수 있다는 이야기지요.

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

&&|| 연산자는 첫번째 식을 평가한 결과에 따라서 두번째 식을 평가를 실행하는 단축평가(short-circuit) 논리를 사용합니다. 이는 다음과 같이 객체에 접근하기 전에 null 객체인지, 아닌지를 검사하는데 유용하게 사용될 수 있습니다:

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

또는 기본 값 설정을 위해서 다음과 같이 이 성질을 사용할 수 있습니다:

var name = otherName || "default";

JavaScript는 한줄로 조건문을 쓸 수 있게 해주는 삼중 연산자도 가지고 있습니다:

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

스위치 문은 숫자나 문자열을 기반으로 다중 분기되는 문장을 작성하는데 사용될 수 있습니다:

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

break 문장을 추가하지 않았다면, 다음 단계로 "넘어가서" 실행합니다. 이렇게 되는 것을 기대하는 것은 매우 드문경우 입니다. 실은 디버깅하는데 용이하도록 하기위해 주석으로서 일부러 붙여놓은 넘어가기 이름표 입니다:

switch(a) {
    case 1: // fallthrough
    case 2:
        eatit();
        break;
    default:
        donothing();
}

default 구문은 선택적으로 적용할 수 있습니다. 스위치와 케이스 부분에서 원할경우 둘다 식을 사용할 수 있습니다. === 연산자를 사용해서 두 문장을 비교해보시기 바랍니다.

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

객체 (Objects)

JavaScript 객체는 간단히 이름-값 쌍(name-value pairs)의 모임입니다. 그렇기 때문에, JavaScript의 객체의 모임은 다음과 비슷하다고 할 수 있습니다:

  • Python의 Dictionaries
  • Perl 과 Ruby의 Hashes
  • C 와 C++ 의 Hash tables
  • Java 의 HashMaps
  • PHP의 Associative arrays

이 데이터 구조가 매우 광범위하게 사용된다는 사실은 활용 방도가 다양함을 입증합니다. JavaScript내 모든 것 (코어 타입들은 제외)은 객체로 취급되기 때문에 어떤 JavaScript 프로그램도 기본적으로 해쉬 테이블을 검색하는데 필요한 출중한 성능을 가지고 있습니다. 매우 빠르기 때문에 장점이 됩니다!

값은 객체를 포함하여 아무 JavaScript 값이 될 수 있는 반면, "이름" 부분은 JavaScript 문자열 입니다. 이는 무작위적인 복잡성을 가지는 데이터 구조를 만들 수 있도록 해줍니다.

빈 객체를 생성하는데 두가지 방법이 있습니다:

var obj = new Object();

와:

var obj = {};

이들은 의미적으로 동치입니다. 두번째 방법은 객체의 엄밀한 구문으로 말할 수 있으며 더 편리합니다. 객체 엄밀 구문이 초기 버전에는 없었기 때문에 예전 방법을 사용한 코드를 많이 볼 수 있습니다.

일단 생성되면, 객체의 속성에 다음의 두가지 방법들 중 한가지로 접근할 수 있습니다:

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

그리고...

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

이들은 의미적으로 역시 같습니다. 두번째 방법은 속성의 이름이 실행시간(run-time)에 계산될 수 있는 문자열로 주어집니다. 또한 예약된 단어(키워드)로 되어있는 이름으로 객체의 속성을 설정하거나 얻어낼 수 있습니다:

obj.for = "Simon"; // 구문 오류, for 가 예약된 단어(키워드)이기 때문에
obj["for"] = "Simon"; // 정상 동작

객체 엄밀 구문으로 객체의 전체적인 구조를 초기화 할 수 있습니다:

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

속성에 연속적으로 접근할 수 있습니다:

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

배열 (Arrays)

JavaScript에서 배열은 실재로는 객체의 특별한 타입입니다. (숫자로 나타낸 속성은 자연스럽게 [] 구문만을 사용해서 접근하게 되므로) 일반 객체와 많이 비슷하게 동작하지만, 이 객체는 'length'라는 한가지 마법 속성을 가집니다. 이는 항상 배열에서 가장 큰 인덱스보다 하나 더 큰 값으로 존재합니다.

배열을 생성하는 예전 방법은 다음과 같습니다:

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

한가지 더 편리한 배열 표현 방법은 배열 상수를 사용하는 것입니다:

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

배열 상수 끝에 콤마(",")를 꼬리로 남겨두는 것은 브라우저마다 다르게 처리하므로 그렇게 하지는 마시기 바랍니다.

array.length 는 배열에 들어있는 항목의 수를 반드시 반영하지는 않는다는 점을 주의하시기 바랍니다. 다음과 같은 경우를 고려해보겠습니다:

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

기억해두세요 - 배열의 길이는 최대 인덱스에 하나를 더한 값일 뿐입니다.

존재하지 않는 배열 인덱스를 참조하려고하면 다음과 같이 undefined 을 얻게됩니다:

> typeof(a[90])
undefined

위의 사항들을 감안하면 배열을 반복문으로 처리할 때 다음과 같은 방법으로 처리하실 수 있을 것입니다:

for (var i = 0; i < a.length; i++) {
    // a[i] 로 뭔가를 수행
}

이 코드는 루프를 반복할 때마다 배열의 length 속성을 찾아보게되므로 약간 비 효율적입니다. 개선책은:

for (var i = 0, len = a.length; i < len; i++) {
    // a[i] 로 뭔가를 수행
}

보다 더 좋은 관용적인 코드는:

for (var i = 0, item; item = a[i]; i++) {
    // item 으로 뭔가를 수행
}

여기서 우리는 두개의 변수를 설정합니다. for 루프 중간 부분의 할당문은 참인지 거짓인지 테스트 하는데, 참으로 밝혀지면, 루프를 계속 돕니다. i가 루프를 돌 때마다 하나씩 증가하기 때문에 배열의 항목들은 순차적으로 item 변수에 할당됩니다. "거짓으로 취급되는" 항목 (undefined와 같은 항목)을 발견하면 루프는 멈춥니다.

이 요령은 "거짓으로 취급되는" 값이 포함되지 않은, 예를 들어 객체 또는 DOM 노드의 배열, 배열에 사용되어야만 합니다. 0을 포함하는 수로 표현된 데이터나 빈 문자열을 포함하는 문자열 데이터에 대하여 반복문을 적용할 경우에는 i, j를 사용하는 관용 코드를 대신 사용해야 합니다.

반복문을 사용하는 또다른 방법은 for...in 루프를 사용하는 것입니다. 누군가가 Array.prototype에 새로운 속성을 추가한 경우, 이 루프에 의해 그 속성도 반복된다는 점을 주의하시기 바랍니다:

for (var i in a) {
  // a[i] 으로 뭔가를 수행
}

배열에 어떤 항목을 덧붙이길 원하면 다음과 같이 안전한 방법으로 수행할 수 있는 방법이 있습니다:

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

a.length는 가장 큰 인덱스의 하나더 큰 값이기 때문에 배열 끝의 빈 공간에 할당한다는 점을 확신할 수 있습니다.

배열 객체은 다음과 같이 많은 메소드를 사용할 수 있습니다:

a.toString(), a.toLocaleString(), a.concat(item, ..), a.join(sep),
a.pop(), a.push(item, ..), a.reverse(), a.shift(), a.slice(start, end),
a.sort(cmpfn), a.splice(start, delcount, [item]..), a.unshift([item]..)
  • concat 해당 배열에 지정한 항목들을 추가한 새로운 배열을 돌려줍니다
  • pop 마지막 항목을 제거한 다음 돌려둡니다
  • push 마지막에 하나 이상의 항목을 추가합니다 (ar{{mediawiki.external('ar.length')}}와 같이)
  • slice 배열의 일부분을 돌려줍니다
  • sort 비교에 사용할 함수를 따로 지정할 수 있습니다
  • splice 구역을 삭제하거나 항목을 추가해서 배열을 수정할 수 있게합니다
  • unshift 배열의 시작부분에 항목을 붙일 수 있습니다

함수 (Functions)

Along with objects, functions are the core component in understanding JavaScript. The most basic function couldn't be much simpler:

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

This demonstrates everything there is to know about basic functions. A JavaScript function can take 0 or more named parameters. The function body can contain as many statements as you like, and can declare its own variables which are local to that function. The return statement can be used to return a value at any time, terminating the function. If no return statement is used (or an empty return with no value), JavaScript returns undefined.

The named parameters turn out to be more like guidelines than anything else. You can call a function without passing the parameters it expects, in which case they will be set to undefined.

> add()
NaN // You can't perform addition on undefined

You can also pass in more arguments than the function is expecting:

> add(2, 3, 4)
5 // added the first two; 4 was ignored

That may seem a little silly, but functions have access to an additional variable inside their body called arguments, which is an array-like object holding all of the values passed to the function. Let's re-write the add function to take as many values as we want:

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

That's really not any more useful than writing 2 + 3 + 4 + 5 though. Let's create an averaging function:

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

This is pretty useful, but introduces a new problem. The avg() function takes a comma separated list of arguments - but what if you want to find the average of an array? You could just rewrite the function as follows:

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

But it would be nice to be able to reuse the function that we've already created. Luckily, JavaScript lets you call a function and call it with an arbitrary array of arguments, using the apply() method of any function object.

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

The second argument to apply() is the array to use as arguments; the first will be discussed later on. This emphasizes the fact that functions are objects too.

JavaScript lets you create anonymous functions.

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

This is semantically equivalent to the function avg() form. It's extremely powerful, as it lets you put a full function definition anywhere that you would normally put an expression. This enables all sorts of clever tricks. Here's a way of "hiding" some local variables - like block scope in C:

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

JavaScript allows you to call functions recursively. This is particularly useful for dealing with tree structures, such as you get in the browser DOM.

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;
}

This highlights a potential problem with anonymous functions: how do you call them recursively if they don't have a name? The answer lies with the arguments object, which in addition to acting as a list of arguments also provides a property called arguments.callee. This always refers to the current function, and hence can be used to make recursive calls:

var charsInBody = (function(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 += arguments.callee(child);
    }
    return count;
})(document.body);

Since arguments.callee is the current function, and all functions are objects, you can use arguments.callee to save information across multiple calls to the same function. Here's a function that remembers how many times it has been called:

function counter() {
    if (!arguments.callee.count) {
        arguments.callee.count = 0;
    }
    return arguments.callee.count++;
}

> counter()
0
> counter()
1
> counter()
2

Custom objects

In classic Object Oriented Programming, objects are collections of data and methods that operate on that data. Let's consider a person object with first and last name fields. There are two ways in which their name might be displayed: as "first last" or as "last, first". Using the functions and objects that we've discussed previously, here's one way of doing it:

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

This works, but it's pretty ugly. You end up with dozens of functions in your global namespace. What we really need is a way to attach a function to an object. Since functions are objects, this is easy:

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

There's something here we haven't seen before: the 'this' keyword. Used inside a function, 'this' refers to the current object. What that actually means is specified by the way in which you called that function. If you called it using dot notation or bracket notation on an object, that object becomes 'this'. If dot notation wasn't used for the call, 'this' refers to the global object. This is a frequent cause of mistakes. For example:

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

When we call fullName(), 'this' is bound to the global object. Since there are no global variables called first or last we get undefined for each one.

We can take advantage of the 'this' keyword to improve our makePerson function:

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");

We've introduced another keyword: 'new'. new is strongly related to 'this'. What it does is it creates a brand new empty object, and then calls the function specified, with 'this' set to that new object. Functions that are designed to be called by 'new' are called constructor functions. Common practise is to capitalise these functions as a reminder to call them with new.

Our person objects are getting better, but there are still some ugly edges to them. Every time we create a person object we are creating two brand new function objects within it - wouldn't it be better if this code was shared?

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;
}

That's better: we are creating the method functions only once, and assigning references to them inside the constructor. Can we do any better than that? The answer is yes:

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.prototype is an object shared by all instances of Person. It forms part of a lookup chain (that has a special name, "prototype chain"): any time you attempt to access a property of Person that isn't set, JavaScript will check Person.prototype to see if that property exists there instead. As a result, anything assigned to Person.prototype becomes available to all instances of that constructor via the this object.

This is an incredibly powerful tool. JavaScript lets you modify something's prototype at any time in your program, which means you can add extra methods to existing objects at runtime:

> 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

Interestingly, you can also add things to the prototype of built-in JavaScript objects. Let's add a method to String that returns that string in reverse:

> 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

Our new method even works on string literals!

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

As I mentioned before, the prototype forms part of a chain. The root of that chain is Object.prototype, whose methods include toString() - it is this method that is called when you try to represent an object as a string. This is useful for debugging our Person objects:

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

Remember how avg.apply() had a null first argument? We can revisit that now. The first argument to apply() is the object that should be treated as 'this'. For example, here's a trivial implementation of 'new':

function trivialNew(constructor) {
    var o = {}; // Create an object
    constructor.apply(o, arguments);
    return o;
}

This isn't an exact replica of new as it doesn't set up the prototype chain. apply() is difficult to illustrate - it's not something you use very often, but it's useful to know about.

apply() has a sister function named call, which again lets you set 'this' but takes an expanded argument list as opposed to an array.

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

JavaScript function declarations are allowed inside other functions. We've seen this once before, with an earlier makePerson() function. An important detail of nested functions in JavaScript is that they can access variables in their parent function's scope:

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

This provides a great deal of utility in writing more maintainable code. If a function relies on one or two other functions that are not useful to any other part of your code, you can nest those utility functions inside the function that will be called from elsewhere. This keeps the number of functions that are in the global scope down, which is always a good thing.

This is also a great counter to the lure of global variables. When writing complex code it is often tempting to use global variables to share values between multiple functions - which leads to code that is hard to maintain. Nested functions can share variables in their parent, so you can use that mechanism to couple functions together when it makes sense without polluting your global namespace - 'local globals' if you like. This technique should be used with caution, but it's a useful ability to have.

Closures

This leads us to one of the most powerful abstractions that JavaScript has to offer - but also the most potentially confusing. What does this do?

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

The name of the makeAdder function should give it away: it creates new 'adder' functions, which when called with one argument add it to the argument that they were created with.

What's happening here is pretty much the same as was happening with the inner functions earlier on: a function defined inside another function has access to the outer function's variables. The only difference here is that the outer function has returned, and hence common sense would seem to dictate that its local variables no longer exist. But they do still exist - otherwise the adder functions would be unable to work. What's more, there are two different "copies" of makeAdder's local variables - one in which a is 5 and one in which a is 20. So the result of those function calls is as follows:

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

Here's what's actually happening. Whenever JavaScript executes a function, a 'scope' object is created to hold the local variables created within that function. It is initialised with any variables passed in as function parameters. This is similar to the global object that all global variables and functions live in, but with a couple of important differences: firstly, a brand new scope object is created every time a function starts executing, and secondly, unlike the global object (which in browsers is accessible as window) these scope objects cannot be directly accessed from your JavaScript code. There is no mechanism for iterating over the properties of the current scope object for example.

So when makeAdder is called, a scope object is created with one property: a, which is the argument passed to the makeAdder function. makeAdder then returns a newly created function. Normally JavaScript's garbage collector would clean up the scope object created for makeAdder at this point, but the returned function maintains a reference back to that scope object. As a result, the scope object will not be garbage collected until there are no more references to the function object that makeAdder returned.

Scope objects form a chain called the scope chain, similar to the prototype chain used by JavaScript's object system.

A closure is the combination of a function and the scope object in which it was created.

Closures let you save state - as such, they can often be used in place of objects.

Memory leaks

An unfortunate side effect of closures is that they make it trivially easy to leak memory in Internet Explorer. JavaScript is a garbage collected language - objects are allocated memory upon their creation and that memory is reclaimed by the browser when no references to an object remain. Objects provided by the host environment are handled by that environment.

Browser hosts need to manage a large number of objects representing the HTML page being presented - the objects of the DOM. It is up to the browser to manage the allocation and recovery of these.

Internet Explorer uses its own garbage collection scheme for this, separate from the mechanism used by JavaScript. It is the interaction between the two that can cause memory leaks.

A memory leak in IE occurs any time a circular reference is formed between a JavaScript object and a native object. Consider the following:

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

The circular reference formed above creates a memory leak; IE will not free the memory used by el and o until the browser is completely restarted.

The above case is likely to go unnoticed; memory leaks only become a real concern in long running applications or applications that leak large amounts of memory due to large data structures or leak patterns within loops.

Leaks are rarely this obvious - often the leaked data structure can have many layers of references, obscuring the circular reference.

Closures make it easy to create a memory leak without meaning to. Consider this:

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

The above code sets up the element to turn red when it is clicked. It also creates a memory leak. Why? Because the reference to el is inadvertently caught in the closure created for the anonymous inner function. This creates a circular reference between a JavaScript object (the function) and a native object (el).

There are a number of workarounds for this problem. The simplest is this:

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

This works by breaking the circular reference.

Surprisingly, one trick for breaking circular references introduced by a closure is to add another closure:

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

The inner function is executed straight away, and hides its contents from the closure created with clickHandler.

Another good trick for avoiding closures is breaking circular references during the window.onunload event. Many event libraries will do this for you. Note that doing so disables bfcache in Firefox 1.5, so you should not register an unload listener in Firefox, unless you have other reasons to do so.

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.


{{ wiki.languages( { "en": "en/A_re-introduction_to_JavaScript", "fr": "fr/Une_reintroduction_a_JavaScript", "it": "it/Una_re-introduzione_a_Javascript", "ja": "ja/A_re-introduction_to_JavaScript", "pl": "pl/JavaScript/Na_pocz?tek", "zh-cn": "cn/A_re-introduction_to_JavaScript" } ) }}

리비전 소스

<p>
</p>
<div class="note"><b>주의:</b> 이 문서는 번역 중 입니다. --<a href="User:Dyhan81">Dyhan81</a> 2007년 12월 22일 (토) 19:12 (PST)</div>
<h3 name=".EB.93.A4.EC.96.B4.EA.B0.80.EB.A9.B0"> 들어가며 </h3>
<p>새삼스래 왠 재입문? <a href="ko/JavaScript">JavaScript</a>가 <a class="external" href="http://javascript.crockford.com/javascript.html">the world's most misunderstood programming language (세계에서 가장 잘못 이해되고있는 프로그래밍 언어)</a>에 소개된 것과 같이 의미있는 비판을 받고 있기 때문입니다. 장난감 정도로 비웃음을 사고 있지만, 그 속기쉬운 이면에는 몇가지 강력한 언어 요소를 내재하고 있습니다. 2005년에는 이 기술에 대한 깊은 이해가 웹 개발자 누구에게 있어서도 중요한 능력이 된다는 것을 보여주는 많은 고급 JavaScript 응용 프로그램들이 나타났습니다.
</p><p>언어의 역사에서 시작하는 것이 이 이야기를 이해하는데 도움이 됩니다. JavaScript는 1995년 Netscape의 엔지니어 Brendan Eich에 의해 만들어졌고, 이른 1996년에 Netscape 2와 함께 처음 릴리즈 된것입니다. 원래 LiveScript로 불리워지기로 되어있었지만, Sun Microsystem의 Java 언어의 성공에 편승해보려고 두 언어 사이의 공통점이 매우 적음에도 불구하고, 이런 불행이 예견된 마케팅 결정에 따라 이름이 바뀌게 됩니다. 이 사실은 사상 유래가 없는 혼란의 근원이 되어버립니다.
</p><p>Microsoft는 몇달 후 IE3와 함께 JavaScript와 대부분이 호환되는 JScript로 불리워지는 언어를 발표합니다. Netscape는 1997년에 <a href="ko/ECMAScript">ECMAScript</a> 표준의 첫번째 판이 되는 JavaScript를 유럽 표준화 단체인 <a class="external" href="http://www.ecma-international.org/">Ecma International</a>에 보냅니다. 표준은 1999년에  <a class="external" href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript edition 3</a>에 따라 큰 규모의 개정을 거친 후, 현재 4번째 판이 제정 준비 중에 있지만 유래없이 아주 안정된 상태로 계속 유지되고 있습니다.
</p><p>이 안정 상태는 다양한 코드 구현을 하는데 충분한 시간이기 때문에 개발자들에게는 더없이 좋은 소식입니다. 저는 대부분 이 3판에 집중하려고 합니다. 친숙함을 위하여 JavaScript 전반에 걸쳐 여기서 사용된 용어를 준수하겠습니다.
</p><p>대부분의 프로그래밍 언어와는 달리, JavaScript 언어는 입출력 개념이 없습니다. 호스트 환경 아래에서 스크립트 언어로서 동작하도록 디자인 되어있고, 따라서 외부 세계와 통신하기위해 호스트 환경이 제공하는 메커니즘에 의존합니다. 대부분의 경우 일반적인 호스트 환경은 브라우저이지만 JavaScript 인터프리터는 Adobe Acrobat, Photoshop, Yahoo! 위젯 엔진, 등의 제품에서도 발견할 수 있습니다.
</p>
<h3 name=".EA.B0.9C.EC.9A.94"> 개요 </h3>
<p>어떤 언어에서라도 기초가 되는 부분인 타입을 살펴보는 것부터 시작해봅시다. JavaScript 프로그램은 값을 다루고 해당 값은 모두 타입을 가지고 있습니다. JavaScript의 타입은 다음과 같습니다:
</p>
<ul><li> <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/Number">수 (Number)</a>
</li><li> <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/String">문자열 (String)</a>
</li><li> <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/Boolean">부울 (Boolean)</a>
</li><li> <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/Function">함수 (Function)</a>
</li><li> <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/Object">객체 (Object)</a>
</li></ul>
<p>... 오, 그리고 약간 특별한 타입인 정의되지않음(Undefined) 와 널(Null) 이 있습니다. 또한 객체의 특별한 종류인 <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/Array">배열(Array) 객체</a>. 그리고 자유롭게 사용할 수 있는 <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/Date">날짜(Date) 객체</a> 와 <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/RegExp">정규식(RegExp) 객체</a>가 있습니다. 그리고 기술적으로 정확히 말해 함수(Function)는 단지 객체의 특별한 타입으로 취급됩니다. 따라서 타입 구조도를 정리해보면 다음과 같이 됩니다: 
</p>
<ul><li> 수 (Number)
</li><li> 문자열 (String)
</li><li> 부울 (Boolean)
</li><li> 객체 (Object)
<ul><li> 함수 (Function)
</li><li> 배열 (Array)
</li><li> 날짜 (Date)
</li><li> 정규식 (RegExp)
</li></ul>
</li><li> 널 (Null)
</li><li> 정의되지않음 (Undefined)
</li></ul>
<p>그리고 또 몇 가지 <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/Error">오류</a> 타입이 내장되어 있습니다. 그렇지만 처음 구조도를 기억하고만 있으면 다른 것들도 아주 쉽게 이해할 수 있을 것입니다.
</p>
<h3 name=".EC.88.98_.28Numbers.29"> 수 (Numbers) </h3>
<p>설계 명세서에 의하면 JavaScript에서 수는 "이중정밀도 64비트 형식 IEEE 754 값"으로 정의됩니다. 이것은 몇가지 흥미로운 결과를 가져옵니다. JavaScript에는 정수와 같은 것이 존재하지 않으므로, C 나 Java 에서 수학 계산을 한 경험이 있다면 산술할 때 약간 조심할 필요가 있습니다. 다음과 같은 경우를 주의해야 합니다:
</p>
<pre class="eval">0.1 + 0.2 = 0.30000000000000004
</pre>
<p>덧셈, 뺄셈, 계수 (또는 나머지) 연산을 포함하는 표준 <a href="ko/Core_JavaScript_1.5_Reference/Operators/Arithmetic_Operators">산술 연산자</a>가 지원됩니다. 또한 앞에서 언급하는 것을 깜박 잊은 고급 수학 함수와 상수를 다루기 위한 <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/Math">수학(Math)</a>으로 불리워지는 내장 객체가 있습니다:
</p>
<pre class="eval">Math.sin(3.5);
d = Math.PI * r * r;
</pre>
<p>내장 <code><a href="ko/Core_JavaScript_1.5_Reference/Global_Functions/parseInt">parseInt()</a></code> 함수를 사용하여 문자열을 정수로 변환할 수 있습니다. 이는 다음과 같이 옵션으로 주어지는 두번째 매개변수를 밑으로 하여 수행할 수 있습니다: 
</p>
<pre class="eval">&gt; parseInt("123", 10)
123
&gt; parseInt("010", 10)
10
</pre>
<p>밑을 주지 않으면, 다음과 같이 예상치 못한 결과를 얻을 수 있습니다:
</p>
<pre class="eval">&gt; parseInt("010")
8
</pre>
<p>이 같은 결과는 <code>parseInt</code> 함수가 0으로 시작되는 문자열을 8진수로 취급하기 때문에 발생합니다. 
</p><p>만약 이진수를 정수로 변환하고 싶다면, 밑을 바꾸기만하면 됩니다:
</p>
<pre class="eval">&gt; parseInt("11", 2)
3
</pre>
<p>문자열이 수가 아닌 경우 <code><a href="ko/Core_JavaScript_1.5_Reference/Global_Properties/NaN">NaN</a></code> ("Not a Number" (수가 아님)을 줄인 약자)로 불리워지는 특별한 값을 돌려줍니다:
</p>
<pre class="eval">&gt; parseInt("hello", 10)
NaN
</pre>
<p><code>NaN</code> 는 독성을 가지고 있습니다: 어떤 수학 연산의 입력값으로써 주어지면 그 결과는 역시 <code>NaN</code>가 되기 때문입니다: 
</p>
<pre class="eval">&gt; NaN + 5
NaN
</pre>
<p>내장 <code><a href="ko/Core_JavaScript_1.5_Reference/Global_Functions/isNaN">isNaN()</a></code> 함수를 사용해서 <code>NaN</code> 인지 여부를 검사할 수 있습니다: 
</p>
<pre class="eval">&gt; isNaN(NaN)
true
</pre>
<p>JavaScript는 또 특별한 값 <code><a href="ko/Core_JavaScript_1.5_Reference/Global_Properties/Infinity">Infinity</a></code>와 <code>-Infinity</code>를 가지고 있습니다:
</p>
<pre class="eval">&gt; 1 / 0
Infinity
&gt; -1 / 0
-Infinity
</pre>
<h3 name=".EB.AC.B8.EC.9E.90.EC.97.B4_.28Strings.29"> 문자열 (Strings) </h3>
<p>JavaScript에서 문자열은 문자 하나하나가 연결되어 만들어진 것입니다. 좀 더 정확히 말하자면, 각각이 16비트로 표현된 <a href="ko/Core_JavaScript_1.5_Guide/Unicode">유니코드 문자들</a>이 길게 이어져있는 것입니다. 이는 국제화(i18n, internationalization) 하려하는 누구에게라도 환영받을만한 소식입니다.
</p><p>한 개의 문자를 나타내려면 길이가 1인 문자열을 사용하면 됩니다.
</p><p>문자열의 길이를 알고싶다면, 해당 문자열의 <code><a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/String/length">length</a></code> 속성(해당 객체가 소유하고 있는 성질을 나타내는 값)에 접근하면 됩니다:
</p>
<pre class="eval">&gt; "hello".length
5
</pre>
<p>우리의 첫 JavaScript 객체입니다! 문자열도 역시 객체로 취급된다고 언급했던적이 있죠? 다음과 같이 <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/String#Methods">메소드</a>까지 있는 확실한 녀석입니다:
</p>
<pre class="eval">&gt; "hello".charAt(0)
h
&gt; "hello, world".replace("hello", "goodbye")
goodbye, world
&gt; "hello".toUpperCase()
HELLO
</pre>
<h3 name=".EC.9D.B4.EC.99.B8.EC.9D.98_.ED.83.80.EC.9E.85.EB.93.A4"> 이외의 타입들 </h3>
<p>JavaScript는 의도적으로 값이 없음을 가리키는 '객체' 타입의 객체인 <code>null</code>과 초기화되지 않은 값 — 아직 어떤 값도 주어지않은(할당되지않은) 변수임을 가리키는 '정의되지 않음' 타입의 객체인 <code>undefined</code>로 구분됩니다. 값에 대해서 나중에 언급할 것이지만 JavaScript에서 변수에 값을 주지않고 선언하는 것이 가능합니다. 이럴 경우, 변수의 타입은 <code>undefined</code>이 되는 것입니다.
</p><p>JavaScript는 <code>true</code> 와 <code>false</code> 값 (둘은 모두 키워드로 예약되어있는 값)을 가질 수 있는 부울 타입을 가지고 있습니다. 다음과 같은 규칙에 따라 어떤 임의의 값을 부울값으로 변환할 수 있습니다:
</p>
<ol><li> <code>false</code>, <code>0</code>, 빈 문자열 (<code>""</code>), 수가 아님을 뜻하는 <code>NaN</code>, <code>null</code>, 와 <code>undefined</code>은 모두 <code>false</code>가 됩니다.
</li><li> 다른 모든 값은 <code>true</code>가 됩니다.
</li></ol>
<p>이 변환은 <code>Boolean()</code> 함수를 써서 명시적으로 이 작업을 수행하실 수 있습니다:
</p>
<pre class="eval">&gt; Boolean("")
false
&gt; Boolean(234)
true
</pre>
<p>하지만 반드시 이렇게 할 필요는 거의 없습니다. JavaScript는 이러한 변환 작업을 <code>if</code> 문 (아래를 보세요)과 같이 부울값이 필요한 경우를 만나게되면 자동으로 사용자가 모르는 사이에 처리해버리기 때문입니다. 이러한 이유로 인해 우리는 가끔 부울 타입으로 변환되었을 때, <code>true</code>와 <code>false</code>이 됨을 의미하는 값들을 각각 "참 값"과 "거짓 값"으로 부를 것입니다. 또는 각각 "참으로 취급되다"와 "거짓으로 취급되다"라는 식으로 불릴 수도 있습니다.
</p><p>부울 연산자는 <code>&amp;&amp;</code> (논리적 <i>와, 그리고</i>), <code>||</code> (논리적 <i>또는</i>), 그리고 <code>!</code> (논리적 <i>부정</i>)이 지원됩니다. 아래에서 다시 언급하겠습니다.
</p>
<h3 name=".EB.B3.80.EC.88.98_.28Variables.29"> 변수 (Variables) </h3>
<p>JavaScript에서 새로운 변수는 <code><a href="ko/Core_JavaScript_1.5_Reference/Statements/var">var</a></code> 키워드로 선언됩니다:
</p>
<pre class="eval">var a;
var name = "simon";
</pre>
<p>만약 변수에 아무런 값을 주지 않고 선언하면 해당 변수의 타입은 <code>undefined</code>가 됩니다.
<span class="comment">should note the absence of block-scoping in JS</span>
<span class="comment">JavaScript에서는 블록 유효 범위가 따로 없음을 주의해줘야 함</span>
</p><p>JavaScript에는 블록 유효 범위가 따로 없습니다. 여기에 대한 것은 <a href="ko/Core_JavaScript_1.5_Guide/Block_Statement">블록 문장</a>에서 참고바랍니다.
</p>
<h3 name=".EC.97.B0.EC.82.B0.EC.9E.90_.28Operators.29"> 연산자 (Operators) </h3>
<p>JavaScript의 산술 연산자로는 <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>%</code>(나머지 연산자)가 있습니다. 값은 <code>=</code> 연산자로 할당할 수 있고, <code>+=</code> 와 <code>-=</code>처럼 다른 연산자를 같이사용해서 할당할 수 있습니다. 이렇게 쓰인 연산자는 <code>x = x <i>연산자</i> y</code>와 같은 결과를 나타냅니다.
</p>
<pre class="eval">x += 5
x = x + 5
</pre>
<p><code>++</code> 와 <code>--</code> 를 각각 점진적인 증가와 감소에 사용할 수 있습니다. 이들은 또한 전처리 또는 후처리 연산자로 사용될 수 있습니다.
</p><p><a href="ko/Core_JavaScript_1.5_Reference/Operators/String_Operators"><code>+</code> 연산자</a>는 문자열 이어붙이기도 합니다:
</p>
<pre class="eval">&gt; "hello" + " world"
hello world
</pre>
<p>문자열에 어떤 수 (또는 다른 값)를 더하면 일단 모두 문자열로 바뀌게 됩니다. 다음 예를 보시면 무슨 말씀인지 아실 수 있을겁니다:
</p>
<pre class="eval">&gt; "3" + 4 + 5
345
&gt; 3 + 4 + "5"
75
</pre>
<p>빈 문자열에 어떤 값을 더하는 것은 해당 값을 문자열로 바꾸는 요령입니다.
</p><p>JavaScript에서 <a href="ko/Core_JavaScript_1.5_Reference/Operators/Comparison_Operators">비교</a>는 <code>&lt;</code>, <code>&gt;</code>, <code>&lt;=</code> 와 <code>&gt;=</code> 를 통해 가능합니다. 이 연산자들은 문자열과 수 양쪽 모두에서 동작합니다. 상동은 약간 직관성이 떨어지는데 이중 등호 (<code>==</code>) 연산자는 서로 다른 타입을 줄 경우 타입 강제 변환을 수행하기 때문에 다음과 같이 때때로 기대하지 않은 결과를 내보내기 때문입니다:
</p>
<pre class="eval">&gt; "dog" == "dog"
true
&gt; 1 == true
true
</pre>
<p>타입 강제 변환을 하지 않게 하려면, 삼중 등호 연산자 (<code>===</code>)를 사용해야합니다:
</p>
<pre class="eval">&gt; 1 === true
false
&gt; true === true
true
</pre>
<p>이와 비슷하게 <code>!=</code> 와 <code>!==</code> 연산자가 있습니다.
</p><p>JavaScript는 값을 <a href="ko/Core_JavaScript_1.5_Reference/Operators/Bitwise_Operators">비트로 취급하는 연산자</a>도 가지고 있습니다. 사용하고 싶을 때 언제라도 사용할 수 있도록 말이죠.
</p>
<h3 name=".EC.A0.9C.EC.96.B4_.EA.B5.AC.EC.A1.B0"> 제어 구조 </h3>
<p>JavaScript는 C 계열의 다른 언어들과 비슷한 제어 구조를 가지고 있습니다. 조건문은 <code>if</code> 와 <code>else</code>를 지원하는데, 원하시는대로 얼마든지 중첩 시켜서 사용할 수 있습니다:
</p>
<pre class="eval">var name = "kittens";
if (name == "puppies") {
  name += "!";
} else if (name == "kittens") {
  name += "!!";
} else {
  name = "!" + name;
}
name == "kittens!!"
</pre>
<p>JavaScript는 <code>while</code> 반복문과 <code>do-while</code> 반복문도 사용할 수 있습니다. 첫번째 것은 단순 반복에 유용하게 사용할 수 있고, 두번째 것은 반복문이 반드시 적어도 한번이상 실행 되도록 하고 싶을 때 사용할 수 있습니다:
</p>
<pre class="eval">while (true) {
  // an infinite loop!
}

do {
  var input = get_input();
} while (inputIsNotValid(input))
</pre>
<p>JavaScript의 <code>for</code> 반복문은 C 와 Java의 그것과 같습니다. 말하자면, 반복문에 필요한 제어 정보를 한줄에 표현할 수 있다는 이야기지요.
</p>
<pre class="eval">for (var i = 0; i &lt; 5; i++) {
  // Will execute 5 times
}
</pre>
<p><code>&amp;&amp;</code> 와 <code>||</code> 연산자는 첫번째 식을 평가한 결과에 따라서 두번째 식을 평가를 실행하는 단축평가(short-circuit) 논리를 사용합니다. 이는 다음과 같이 객체에 접근하기 전에 null 객체인지, 아닌지를 검사하는데 유용하게 사용될 수 있습니다:
</p>
<pre class="eval">var name = o &amp;&amp; o.getName();
</pre>
<p>또는 기본 값 설정을 위해서 다음과 같이 이 성질을 사용할 수 있습니다:
</p>
<pre class="eval">var name = otherName || "default";
</pre>
<p>JavaScript는 한줄로 조건문을 쓸 수 있게 해주는 삼중 연산자도 가지고 있습니다:
</p>
<pre class="eval">var allowed = (age &gt; 18) ? "yes" : "no";
</pre>
<p>스위치 문은 숫자나 문자열을 기반으로 다중 분기되는 문장을 작성하는데 사용될 수 있습니다:
</p>
<pre class="eval">switch(action) {
    case 'draw':
        drawit();
        break;
    case 'eat':
        eatit();
        break;
    default:
        donothing();
}
</pre>
<p><code>break</code> 문장을 추가하지 않았다면, 다음 단계로 "넘어가서" 실행합니다. 이렇게 되는 것을 기대하는 것은 매우 드문경우 입니다. 실은 디버깅하는데 용이하도록 하기위해 주석으로서 일부러 붙여놓은 넘어가기 이름표 입니다:
</p>
<pre class="eval">switch(a) {
    case 1: // fallthrough
    case 2:
        eatit();
        break;
    default:
        donothing();
}
</pre>
<p>default 구문은 선택적으로 적용할 수 있습니다. 스위치와 케이스 부분에서 원할경우 둘다 식을 사용할 수 있습니다. <code>===</code> 연산자를 사용해서 두 문장을 비교해보시기 바랍니다.
</p>
<pre class="eval">switch(1 + 3):
    case 2 + 2:
        yay();
        break;
    default:
        neverhappens();
}
</pre>
<h3 name=".EA.B0.9D.EC.B2.B4_.28Objects.29"> 객체 (Objects) </h3>
<p>JavaScript 객체는 간단히 이름-값 쌍(name-value pairs)의 모임입니다. 그렇기 때문에, JavaScript의 객체의 모임은 다음과 비슷하다고 할 수 있습니다:
</p>
<ul><li> Python의 Dictionaries
</li><li> Perl 과 Ruby의 Hashes
</li><li> C 와 C++ 의 Hash tables
</li><li> Java 의 HashMaps
</li><li> PHP의 Associative arrays
</li></ul>
<p>이 데이터 구조가 매우 광범위하게 사용된다는 사실은 활용 방도가 다양함을 입증합니다. JavaScript내 모든 것 (코어 타입들은 제외)은 객체로 취급되기 때문에 어떤 JavaScript 프로그램도 기본적으로 해쉬 테이블을 검색하는데 필요한 출중한 성능을 가지고 있습니다. 매우 빠르기 때문에 장점이 됩니다!
</p><p>값은 객체를 포함하여 아무 JavaScript 값이 될 수 있는 반면, "이름" 부분은 JavaScript 문자열 입니다. 이는 무작위적인 복잡성을 가지는 데이터 구조를 만들 수 있도록 해줍니다.
</p><p>빈 객체를 생성하는데 두가지 방법이 있습니다:
</p>
<pre class="eval">var obj = new Object();
</pre>
<p>와:
</p>
<pre class="eval">var obj = {};
</pre>
<p>이들은 의미적으로 동치입니다. 두번째 방법은 객체의 엄밀한 구문으로 말할 수 있으며 더 편리합니다. 객체 엄밀 구문이 초기 버전에는 없었기 때문에 예전 방법을 사용한 코드를 많이 볼 수 있습니다.
</p><p>일단 생성되면, 객체의 속성에 다음의 두가지 방법들 중 한가지로 접근할 수 있습니다:
</p>
<pre class="eval">obj.name = "Simon"
var name = obj.name;
</pre>
<p>그리고...
</p>
<pre class="eval">obj["name"] = "Simon";
var name = obj["name"];
</pre>
<p>이들은 의미적으로 역시 같습니다. 두번째 방법은 속성의 이름이 실행시간(run-time)에 계산될 수 있는 문자열로 주어집니다. 또한 <a href="ko/Core_JavaScript_1.5_Reference/Reserved_Words">예약된 단어(키워드)</a>로 되어있는 이름으로 객체의 속성을 설정하거나 얻어낼 수 있습니다:
</p>
<pre class="eval">obj.for = "Simon"; // 구문 오류, for 가 예약된 단어(키워드)이기 때문에
obj["for"] = "Simon"; // 정상 동작
</pre>
<p>객체 엄밀 구문으로 객체의 전체적인 구조를 초기화 할 수 있습니다:
</p>
<pre class="eval">var obj = {
    name: "Carrot",
    "for": "Max",
    details: {
        color: "orange",
        size: 12
    }
}
</pre>
<p>속성에 연속적으로 접근할 수 있습니다:
</p>
<pre class="eval">&gt; obj.details.color
orange
&gt; obj["details"]["size"]
12
</pre>
<h3 name=".EB.B0.B0.EC.97.B4_.28Arrays.29"> 배열 (Arrays) </h3>
<p>JavaScript에서 배열은 실재로는 객체의 특별한 타입입니다. (숫자로 나타낸 속성은 자연스럽게 [] 구문만을 사용해서 접근하게 되므로) 일반 객체와 많이 비슷하게 동작하지만, 이 객체는 '<code>length</code>'라는 한가지 마법 속성을 가집니다. 이는 항상 배열에서 가장 큰 인덱스보다 하나 더 큰 값으로 존재합니다.
</p><p>배열을 생성하는 예전 방법은 다음과 같습니다:
</p>
<pre class="eval">&gt; var a = new Array();
&gt; a[0] = "dog";
&gt; a[1] = "cat";
&gt; a[2] = "hen";
&gt; a.length
3
</pre>
<p>한가지 더 편리한 배열 표현 방법은 배열 상수를 사용하는 것입니다:
</p>
<pre class="eval">&gt; var a = ["dog", "cat", "hen"];
&gt; a.length
3
</pre>
<p>배열 상수 끝에 콤마(",")를 꼬리로 남겨두는 것은 브라우저마다 다르게 처리하므로 그렇게 하지는 마시기 바랍니다.
</p><p><code>array.length</code> 는 배열에 들어있는 항목의 수를 반드시 반영하지는 않는다는 점을 주의하시기 바랍니다. 다음과 같은 경우를 고려해보겠습니다:
</p>
<pre class="eval">&gt; var a = ["dog", "cat", "hen"];
&gt; a[100] = "fox";
&gt; a.length
101
</pre>
<p>기억해두세요 - 배열의 길이는 최대 인덱스에 하나를 더한 값일 뿐입니다.
</p><p>존재하지 않는 배열 인덱스를 참조하려고하면 다음과 같이 <code>undefined</code> 을 얻게됩니다:
</p>
<pre class="eval">&gt; typeof(a[90])
undefined
</pre>
<p>위의 사항들을 감안하면 배열을 반복문으로 처리할 때 다음과 같은 방법으로 처리하실 수 있을 것입니다:
</p>
<pre class="eval">for (var i = 0; i &lt; a.length; i++) {
    // a[i] 로 뭔가를 수행
}
</pre>
<p>이 코드는 루프를 반복할 때마다 배열의 length 속성을 찾아보게되므로 약간 비 효율적입니다. 개선책은:
</p>
<pre class="eval">for (var i = 0, len = a.length; i &lt; len; i++) {
    // a[i] 로 뭔가를 수행
}
</pre>
<p>보다 더 좋은 관용적인 코드는:
</p>
<pre class="eval">for (var i = 0, item; item = a[i]; i++) {
    // item 으로 뭔가를 수행
}
</pre>
<p>여기서 우리는 두개의 변수를 설정합니다. <code>for</code> 루프 중간 부분의 할당문은 참인지 거짓인지 테스트 하는데, 참으로 밝혀지면, 루프를 계속 돕니다. <code>i</code>가 루프를 돌 때마다 하나씩 증가하기 때문에 배열의 항목들은 순차적으로 item 변수에 할당됩니다. "거짓으로 취급되는" 항목 (<code>undefined</code>와 같은 항목)을 발견하면 루프는 멈춥니다.
</p><p>이 요령은 "거짓으로 취급되는" 값이 포함되지 않은, 예를 들어 객체 또는 <a href="ko/DOM">DOM</a> 노드의 배열, 배열에 사용되어야만 합니다. 0을 포함하는 수로 표현된 데이터나 빈 문자열을 포함하는 문자열 데이터에 대하여 반복문을 적용할 경우에는 <code>i, j</code>를 사용하는 관용 코드를 대신 사용해야 합니다.
</p><p>반복문을 사용하는 또다른 방법은 <code><a href="ko/Core_JavaScript_1.5_Reference/Statements/for...in">for...in</a></code> 루프를 사용하는 것입니다. 누군가가 <code>Array.prototype</code>에 새로운 속성을 추가한 경우, 이 루프에 의해 그 속성도 반복된다는 점을 주의하시기 바랍니다:
</p>
<pre class="eval">for (var i in a) {
  // a[i] 으로 뭔가를 수행
}
</pre>
<p>배열에 어떤 항목을 덧붙이길 원하면 다음과 같이 안전한 방법으로 수행할 수 있는 방법이 있습니다:
</p>
<pre class="eval">a[a.length] = item;                 // a.push(item); 와 같음
</pre>
<p><code>a.length</code>는 가장 큰 인덱스의 하나더 큰 값이기 때문에 배열 끝의 빈 공간에 할당한다는 점을 확신할 수 있습니다.
</p><p>배열 객체은 다음과 같이 많은 메소드를 사용할 수 있습니다:
</p>
<pre class="eval">a.toString(), a.toLocaleString(), a.concat(item, ..), a.join(sep),
a.pop(), a.push(item, ..), a.reverse(), a.shift(), a.slice(start, end),
a.sort(cmpfn), a.splice(start, delcount, [item]..), a.unshift([item]..)
</pre>
<ul><li> <code>concat</code> 해당 배열에 지정한 항목들을 추가한 새로운 배열을 돌려줍니다
</li><li> <code>pop</code> 마지막 항목을 제거한 다음 돌려둡니다
</li><li> <code>push</code> 마지막에 하나 이상의 항목을 추가합니다 (<code>ar{{mediawiki.external('ar.length')}}</code>와 같이)
</li><li> <code>slice</code> 배열의 일부분을 돌려줍니다
</li><li> <code>sort</code> 비교에 사용할 함수를 따로 지정할 수 있습니다
</li><li> <code>splice</code> 구역을 삭제하거나 항목을 추가해서 배열을 수정할 수 있게합니다
</li><li> <code>unshift</code> 배열의 시작부분에 항목을 붙일 수 있습니다
</li></ul>
<h3 name=".ED.95.A8.EC.88.98_.28Functions.29"> 함수 (Functions) </h3>
<p>Along with objects, functions are the core component in understanding JavaScript. The most basic function couldn't be much simpler:
</p>
<pre class="eval">function add(x, y) {
    var total = x + y;
    return total;
}
</pre>
<p>This demonstrates everything there is to know about basic functions. A JavaScript function can take 0 or more named parameters. The function body can contain as many statements as you like, and can declare its own variables which are local to that function. The <code>return</code> statement can be used to return a value at any time, terminating the function. If no return statement is used (or an empty return with no value), JavaScript returns <code>undefined</code>.
</p><p>The named parameters turn out to be more like guidelines than anything else. You can call a function without passing the parameters it expects, in which case they will be set to <code>undefined</code>.
</p>
<pre class="eval">&gt; add()
NaN // You can't perform addition on undefined
</pre>
<p>You can also pass in more arguments than the function is expecting:
</p>
<pre class="eval">&gt; add(2, 3, 4)
5 // added the first two; 4 was ignored
</pre>
<p>That may seem a little silly, but functions have access to an additional variable inside their body called <a href="ko/Core_JavaScript_1.5_Reference/Functions/arguments"><code>arguments</code></a>, which is an array-like object holding all of the values passed to the function. Let's re-write the add function to take as many values as we want:
</p>
<pre class="eval">function add() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i &lt; j; i++) {
        sum += arguments[i];
    }
    return sum;
}

&gt; add(2, 3, 4, 5)
14
</pre>
<p>That's really not any more useful than writing <code>2 + 3 + 4 + 5</code> though. Let's create an averaging function:
</p>
<pre class="eval">function avg() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i &lt; j; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
}
&gt; avg(2, 3, 4, 5)
3.5
</pre>
<p>This is pretty useful, but introduces a new problem. The <code>avg()</code> function takes a comma separated list of arguments - but what if you want to find the average of an array? You could just rewrite the function as follows:
</p>
<pre class="eval">function avgArray(arr) {
    var sum = 0;
    for (var i = 0, j = arr.length; i &lt; j; i++) {
        sum += arr[i];
    }
    return sum / arr.length;
}
&gt; avgArray([2, 3, 4, 5])
3.5
</pre>
<p>But it would be nice to be able to reuse the function that we've already created. Luckily, JavaScript lets you call a function and call it with an arbitrary array of arguments, using the <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply"><code>apply()</code></a> method of any function object.
</p>
<pre class="eval">&gt; avg.apply(null, [2, 3, 4, 5])
3.5
</pre>
<p>The second argument to <code>apply()</code> is the array to use as arguments; the first will be discussed later on. This emphasizes the fact that functions are objects too.
</p><p>JavaScript lets you create anonymous functions.
</p>
<pre class="eval">var avg = function() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i &lt; j; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
}
</pre>
<p>This is semantically equivalent to the <code>function avg()</code> form. It's extremely powerful, as it lets you put a full function definition anywhere that you would normally put an expression. This enables all sorts of clever tricks. Here's a way of "hiding" some local variables - like block scope in C:
</p>
<pre class="eval">&gt; var a = 1;
&gt; var b = 2;
&gt; (function() {
    var b = 3;
    a += b;
})();
&gt; a
4
&gt; b
2
</pre>
<p>JavaScript allows you to call functions recursively. This is particularly useful for dealing with tree structures, such as you get in the browser <a href="ko/DOM">DOM</a>.
</p>
<pre class="eval">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;
}
</pre>
<p>This highlights a potential problem with anonymous functions: how do you call them recursively if they don't have a name? The answer lies with the <code>arguments</code> object, which in addition to acting as a list of arguments also provides a property called <code>arguments.callee</code>. This always refers to the current function, and hence can be used to make recursive calls:
</p>
<pre class="eval">var charsInBody = (function(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 += arguments.callee(child);
    }
    return count;
})(document.body);
</pre>
<p>Since <code>arguments.callee</code> is the current function, and all functions are objects, you can use <code>arguments.callee</code> to save information across multiple calls to the same function. Here's a function that remembers how many times it has been called:
</p>
<pre class="eval">function counter() {
    if (!arguments.callee.count) {
        arguments.callee.count = 0;
    }
    return arguments.callee.count++;
}

&gt; counter()
0
&gt; counter()
1
&gt; counter()
2
</pre>
<h3 name="Custom_objects"> Custom objects </h3>
<p>In classic Object Oriented Programming, objects are collections of data and methods that operate on that data. Let's consider a person object with first and last name fields. There are two ways in which their name might be displayed: as "first last" or as "last, first". Using the functions and objects that we've discussed previously, here's one way of doing it:
</p>
<pre class="eval">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
}
&gt; s = makePerson("Simon", "Willison");
&gt; personFullName(s)
Simon Willison
&gt; personFullNameReversed(s)
Willison, Simon
</pre>
<p>This works, but it's pretty ugly. You end up with dozens of functions in your global namespace. What we really need is a way to attach a function to an object. Since functions are objects, this is easy:
</p>
<pre class="eval">function makePerson(first, last) {
    return {
        first: first,
        last: last,
        fullName: function() {
            return this.first + ' ' + this.last;
        },
        fullNameReversed: function() {
            return this.last + ', ' + this.first;
        }
    }
}
&gt; s = makePerson("Simon", "Willison")
&gt; s.fullName()
Simon Willison
&gt; s.fullNameReversed()
Willison, Simon
</pre>
<p>There's something here we haven't seen before: the '<code><a href="ko/Core_JavaScript_1.5_Reference/Operators/Special_Operators/this_Operator">this</a></code>' keyword. Used inside a function, '<code>this</code>' refers to the current object. What that actually means is specified by the way in which you called that function. If you called it using <a href="ko/Core_JavaScript_1.5_Reference/Operators/Member_Operators">dot notation or bracket notation</a> on an object, that object becomes '<code>this</code>'. If dot notation wasn't used for the call, '<code>this</code>' refers to the global object. This is a frequent cause of mistakes. For example:
</p>
<pre class="eval">&gt; s = makePerson("Simon", "Willison")
&gt; var fullName = s.fullName;
&gt; fullName()
undefined undefined
</pre>
<p>When we call <code>fullName()</code>, '<code>this</code>' is bound to the global object. Since there are no global variables called <code>first</code> or <code>last</code> we get <code>undefined</code> for each one.
</p><p>We can take advantage of the '<code>this</code>' keyword to improve our <code>makePerson</code> function:
</p>
<pre class="eval">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");
</pre>
<p>We've introduced another keyword: '<code><a href="ko/Core_JavaScript_1.5_Reference/Operators/Special_Operators/new_Operator">new</a></code>'. <code>new</code> is strongly related to '<code>this</code>'. What it does is it creates a brand new empty object, and then calls the function specified, with '<code>this</code>' set to that new object. Functions that are designed to be called by '<code>new</code>' are called constructor functions. Common practise is to capitalise these functions as a reminder to call them with <code>new</code>.
</p><p>Our person objects are getting better, but there are still some ugly edges to them. Every time we create a person object we are creating two brand new function objects within it - wouldn't it be better if this code was shared?
</p>
<pre class="eval">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;
}
</pre>
<p>That's better: we are creating the method functions only once, and assigning references to them inside the constructor. Can we do any better than that? The answer is yes:
</p>
<pre class="eval">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;
}
</pre>
<p><code>Person.prototype</code> is an object shared by all instances of <code>Person</code>. It forms part of a lookup chain (that has a special name, "prototype chain"): any time you attempt to access a property of <code>Person</code> that isn't set, JavaScript will check <code>Person.prototype</code> to see if that property exists there instead. As a result, anything assigned to <code>Person.prototype</code> becomes available to all instances of that constructor via the <code>this</code> object.
</p><p>This is an incredibly powerful tool. JavaScript lets you modify something's prototype at any time in your program, which means you can add extra methods to existing objects at runtime:
</p>
<pre class="eval">&gt; s = new Person("Simon", "Willison");
&gt; s.firstNameCaps();
TypeError on line 1: s.firstNameCaps is not a function
&gt; Person.prototype.firstNameCaps = function() {
    return this.first.toUpperCase()
}
&gt; s.firstNameCaps()
SIMON
</pre>
<p>Interestingly, you can also add things to the prototype of built-in JavaScript objects. Let's add a method to <code>String</code> that returns that string in reverse:
</p>
<pre class="eval">&gt; var s = "Simon";
&gt; s.reversed()
TypeError on line 1: s.reversed is not a function
&gt; String.prototype.reversed = function() {
    var r = "";
    for (var i = this.length - 1; i &gt;= 0; i--) {
        r += this[i];
    }
    return r;
}
&gt; s.reversed()
nomiS
</pre>
<p>Our new method even works on string literals!
</p>
<pre class="eval">&gt; "This can now be reversed".reversed()
desrever eb won nac sihT
</pre>
<p>As I mentioned before, the prototype forms part of a chain. The root of that chain is <code>Object.prototype</code>, whose methods include <code>toString()</code> - it is this method that is called when you try to represent an object as a string. This is useful for debugging our <code>Person</code> objects:
</p>
<pre class="eval">&gt; var s = new Person("Simon", "Willison");
&gt; s
[object Object]
&gt; Person.prototype.toString = function() {
    return '&lt;Person: ' + this.fullName() + '&gt;';
}
&gt; s
&lt;Person: Simon Willison&gt;
</pre>
<p>Remember how <code>avg.apply()</code> had a null first argument? We can revisit that now. The first argument to <code>apply()</code> is the object that should be treated as '<code>this</code>'. For example, here's a trivial implementation of '<code>new</code>':
</p>
<pre class="eval">function trivialNew(constructor) {
    var o = {}; // Create an object
    constructor.apply(o, arguments);
    return o;
}
</pre>
<p>This isn't an exact replica of <code>new</code> as it doesn't set up the prototype chain. <code>apply()</code> is difficult to illustrate - it's not something you use very often, but it's useful to know about.
</p><p><code>apply()</code> has a sister function named <a href="ko/Core_JavaScript_1.5_Reference/Global_Objects/Function/call"><code>call</code></a>, which again lets you set '<code>this</code>' but takes an expanded argument list as opposed to an array.
</p>
<pre class="eval">function lastNameCaps() {
    return this.last.toUpperCase();
}
var s = new Person("Simon", "Willison");
lastNameCaps.call(s);
// Is the same as:
s.lastNameCaps = lastNameCaps;
s.lastNameCaps();
</pre>
<h3 name="Inner_functions"> Inner functions </h3>
<p>JavaScript function declarations are allowed inside other functions. We've seen this once before, with an earlier <code>makePerson()</code> function. An important detail of nested functions in JavaScript is that they can access variables in their parent function's scope:
</p>
<pre class="eval">function betterExampleNeeded() {
    var a = 1;
    function oneMoreThanA() {
        return a + 1;
    }
    return oneMoreThanA();
}
</pre>
<p>This provides a great deal of utility in writing more maintainable code. If a function relies on one or two other functions that are not useful to any other part of your code, you can nest those utility functions inside the function that will be called from elsewhere. This keeps the number of functions that are in the global scope down, which is always a good thing.
</p><p>This is also a great counter to the lure of global variables. When writing complex code it is often tempting to use global variables to share values between multiple functions - which leads to code that is hard to maintain. Nested functions can share variables in their parent, so you can use that mechanism to couple functions together when it makes sense without polluting your global namespace - 'local globals' if you like. This technique should be used with caution, but it's a useful ability to have.
</p>
<h3 name="Closures"> Closures </h3>
<p>This leads us to one of the most powerful abstractions that JavaScript has to offer - but also the most potentially confusing. What does this do?
</p>
<pre class="eval">function makeAdder(a) {
    return function(b) {
        return a + b;
    }
}
x = makeAdder(5);
y = makeAdder(20);
x(6)
?
y(7)
?
</pre>
<p>The name of the <code>makeAdder</code> function should give it away: it creates new 'adder' functions, which when called with one argument add it to the argument that they were created with.
</p><p>What's happening here is pretty much the same as was happening with the inner functions earlier on: a function defined inside another function has access to the outer function's variables. The only difference here is that the outer function has returned, and hence common sense would seem to dictate that its local variables no longer exist. But they <i>do</i> still exist - otherwise the adder functions would be unable to work. What's more, there are two different "copies" of <code>makeAdder</code>'s local variables - one in which <code>a</code> is 5 and one in which <code>a</code> is 20. So the result of those function calls is as follows:
</p>
<pre class="eval">x(6) // returns 11
y(7) // returns 27
</pre>
<p>Here's what's actually happening. Whenever JavaScript executes a function, a 'scope' object is created to hold the local variables created within that function. It is initialised with any variables passed in as function parameters. This is similar to the global object that all global variables and functions live in, but with a couple of important differences: firstly, a brand new scope object is created every time a function starts executing, and secondly, unlike the global object (which in browsers is accessible as window) these scope objects cannot be directly accessed from your JavaScript code. There is no mechanism for iterating over the properties of the current scope object for example.
</p><p>So when <code>makeAdder</code> is called, a scope object is created with one property: <code>a</code>, which is the argument passed to the <code>makeAdder</code> function. <code>makeAdder</code> then returns a newly created function. Normally JavaScript's garbage collector would clean up the scope object created for <code>makeAdder</code> at this point, but the returned function maintains a reference back to that scope object. As a result, the scope object will not be garbage collected until there are no more references to the function object that <code>makeAdder</code> returned.
</p><p>Scope objects form a chain called the scope chain, similar to the prototype chain used by JavaScript's object system.
</p><p>A closure is the combination of a function and the scope object in which it was created.
</p><p>Closures let you save state - as such, they can often be used in place of objects.
</p>
<h3 name="Memory_leaks"> Memory leaks </h3>
<p>An unfortunate side effect of closures is that they make it trivially easy to leak memory in Internet Explorer. JavaScript is a garbage collected language - objects are allocated memory upon their creation and that memory is reclaimed by the browser when no references to an object remain. Objects provided by the host environment are handled by that environment.
</p><p>Browser hosts need to manage a large number of objects representing the HTML page being presented - the objects of the <a href="ko/DOM">DOM</a>. It is up to the browser to manage the allocation and recovery of these.
</p><p>Internet Explorer uses its own garbage collection scheme for this, separate from the mechanism used by JavaScript. It is the interaction between the two that can cause memory leaks.
</p><p>A memory leak in IE occurs any time a circular reference is formed between a JavaScript object and a native object. Consider the following:
</p>
<pre class="eval">function leakMemory() {
    var el = document.getElementById('el');
    var o = { 'el': el };
    el.o = o;
}
</pre>
<p>The circular reference formed above creates a memory leak; IE will not free the memory used by <code>el</code> and <code>o</code> until the browser is completely restarted.
</p><p>The above case is likely to go unnoticed; memory leaks only become a real concern in long running applications or applications that leak large amounts of memory due to large data structures or leak patterns within loops.
</p><p>Leaks are rarely this obvious - often the leaked data structure can have many layers of references, obscuring the circular reference.
</p><p>Closures make it easy to create a memory leak without meaning to. Consider this:
</p>
<pre class="eval">function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
}
</pre>
<p>The above code sets up the element to turn red when it is clicked. It also creates a memory leak. Why? Because the reference to <code>el</code> is inadvertently caught in the closure created for the anonymous inner function. This creates a circular reference between a JavaScript object (the function) and a native object (<code>el</code>).
</p><p>There are a number of workarounds for this problem. The simplest is this:
</p>
<pre class="eval">function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
    el = null;
}
</pre>
<p>This works by breaking the circular reference.
</p><p>Surprisingly, one trick for breaking circular references introduced by a closure is to add another closure:
</p>
<pre class="eval">function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
    }
    (function() {
        var el = document.getElementById('el');
        el.onclick = clickHandler;
    })();
}
</pre>
<p>The inner function is executed straight away, and hides its contents from the closure created with <code>clickHandler</code>.
</p><p>Another good trick for avoiding closures is breaking circular references during the <code>window.onunload</code> event. Many event libraries will do this for you. Note that doing so disables <a href="ko/Using_Firefox_1.5_caching">bfcache in Firefox 1.5</a>, so you should not register an <code>unload</code> listener in Firefox, unless you have other reasons to do so.
</p>
<div class="originaldocinfo">
<h2 name="Original_Document_Information"> Original Document Information </h2>
<ul><li> Author: <a class="external" href="http://simon.incutio.com/">Simon Willison</a>
</li><li> Last Updated Date: March 7, 2006
</li><li> Copyright: © 2006 Simon Willison, contributed under the Creative Commons: Attribute-Sharealike 2.0 license.
</li><li> More information: For more information about this tutorial (and for links to the original talk's slides), see Simon's <a class="external" href="http://simon.incutio.com/archive/2006/03/07/etech">Etech weblog post</a>.
</li></ul>
</div>
<p><br>
</p>
<div class="noinclude">
</div>
{{ wiki.languages( { "en": "en/A_re-introduction_to_JavaScript", "fr": "fr/Une_reintroduction_a_JavaScript", "it": "it/Una_re-introduzione_a_Javascript", "ja": "ja/A_re-introduction_to_JavaScript", "pl": "pl/JavaScript/Na_pocz?tek", "zh-cn": "cn/A_re-introduction_to_JavaScript" } ) }}
Revert to this revision