Blog

참조 타입

May 5, 2014

참조 타입

목차

이 포스팅은 "프론트엔드 개발자를 위한 자바스크립트(2013 인사이트, 한선용 옮김)"에서 발췌 요약한 것이며, 인사이트와 저작권에 대한 부분을 의논하여 사전 허락을 받은 것입니다. 자세한 내용은 페이스북 자바스크립트 제대로 하기 스터디 그룹해당 포스트를 참조하시기 바랍니다.

5장. 참조 타입

참조 값(객체)이란 특정 '참조 타입'의 인스턴스이다. ECMAScript에서 참조 타입은 데이터와 기능을 그룹으로 묶는 구조이며, 객체가 가져야 할 프로퍼티와 메서드를 정의한다는 점 때문에 '객체 정의'라 불리기도 한다.

객체를 생성할 때는 new 연산자 뒤에 객체를 생성하는 함수인 '생성자'를 쓴다.

var person = new Object(); // Object()는 생성자이며 네이티브 참조 타입

5.1 Object 타입

Object의 인스턴스를 명시적으로 생성하는 두 가지 방법. 첫 번째는 new 연산자와 Object() 생성자를 함께 쓴다:

var person = new Object();
person.name = "Nolboo";
person.age = 19;

다른 방법은 여러 가지 프로퍼티를 쉽게 정의할 수 있는 '객체 리터럴' 표기법:

var person = {
    name : "Nolboo",
    age : 19
};

왼쪽 중괄호({ 기호)는 할당 연산자(=) 다음에 값이 나올 것으로 예상되는 '표현식 컨텍스트'에 있으므로 표현식의 시작으로 간주하며, 객체 리터럴의 시작이다.

var person = {}; // new Object()와 같이 기본 프로퍼티와 메서드만 가진 객체

객체 리터럴 표기법은 프로퍼티를 여러 개 쓸 때 가독성을 확보하는 용도로만 쓰길 권한다.

객체 리터럴 표기법을 사용해 객체를 생성할 때는 Object 생성자를 호출하지 않는다. 파이어폭스 2까지는 호출했지만 3부터는 바뀌었다.

옵션 매개변수를 많이 쓸 때 유용하다. 이름 붙은 매개변수가 더 편리하지만, 옵션 매개변수의 숫자가 많아지면 관리하기 어렵다. 가장 좋은 방법은 필수적인 매개변수에는 이름을 붙이고 나머지 옵션 매개변수는 객체 리터럴로 받는 방법이다.

객체 프로퍼티는 보통 '점 표기법'을 써서 접근하지만 '대괄호 표기법'을 쓸 수도 있다. 대괄호 표기법은 변수를 써서 프로퍼티에 접근할 수 있으며, 프로퍼티 이름에 문법 에러 문자가 있거나 키워드 및 예약어에 해당하는 프로퍼티 이름에도 접근할 수 있다.

5.2 Array 타입

데이터의 순서 있는 목록이며, 배열 슬롯에 어떤 타입의 데이터라도 넣을 수 있으며, 데이터를 추가하면 자동으로 커진다.

배열을 만드는 두 가지 방법. 첫 번째는 Array 생성자를 쓴다:

var colors = new Array)();

숫자 하나만 넘기면 숫자 길이만큼의 배열이 만들어지나, 숫자가 아닌 매개변수를 넘기면 해당 데이터 하나만 가진 배열이 생성된다.

var colors = new Array(3);          // 데이터가 3개 있는 배열을 만든다
var names = new Array("Nolboo");    // 문자열 "Nolboo"만 있는 배열을 만든다

Array 생성자를 쓸 때는 new 연산자를 생략할 수 있다.

두 번째는 대괄호 안에 데이터를 쉼표로 구분하는 '배열 리터럴' 표기법이다:

var colors = ["red", "blue", "green"];  // 문자열이 3개 있는 배열을 만든다
var names = [];                         // 빈 배열을 만든다

length 프로퍼티는 배열에 저장된 데이터의 개수이며 항상 0 이상의 값을 반환한다. 흥미로운 것은 읽기 전용이 아니라서 값을 바꾸면 배열 길이가 그것에 맞게 바뀌면서 데이터를 제거하거나 빈 슬롯을 추가한다.

배열에는 최대 4,294,967,295개의 데이터를 담을 수 있다. 그 이상의 데이터를 담으려 하면 예외가 발생하여 오래 실행되는 스크립트 에러가 발생할 수 있다.

배열 감지

전역 스코프가 하나뿐인 단순한 웹 페이지에서는 instanceof 연산자를 쓴다. 웹 페이지에 프레임이 여러 개 있을 경우의 문제를 우회하기 위해 Array.isArray() 메서드를 사용하여 실행 컨텍스트와 무관하게 주어진 값이 배열인지를 알기 위한 것이다.

변환 메서드

toLocaleString(), toString(), valueOf() 메서드가 있다. toString()과 valueOf()는 쉼표로 분리된 문자열의 형태로 같은 값을 반환한다.

toLocaleString()을 배열에서 호출하면 각 값에서 toLocaleString()을 호출해서 문자열 값을 얻는다는 점이 나머지 두 메서드와 다른 점이다.

join() 메서드로 문자열 구분자를 지정할 수 있다. 매개변수를 쓰지 않거나 undefined를 쓰면 구분자로 쉼표를 사용한다.

배열에서 join(), toLocaleString(), toString(), valueOf() 메서드를 호출했을 때 null이나 undefined인 데이터는 빈 문자열로 표시된다.

스택 메서드

push()와 pop() 메서드는 배열의 기본 메서드이면서 배열을 스택처럼 동작하게 한다. 스택은 'LIFO: last-in-first-out' 구조이며, 마지막에 추가된 데이터가 제일 먼저 삭제된다. 스택에서 데이터 '삽입(push)'과 '제거(pop)'는 스택의 맨 위 단 한 지점에서만 발생한다.

push() 메서드의 매개변수 숫자는 제한이 없으며 받은 매개변수를 그대로 배열에 추가한 후 늘어난 배열 길이를 반환한다. pop() 메서드는 반대로 배열의 마지막 데이터를 제거하고 length 프로퍼티를 줄여서 반환한다.

큐 메서드

큐는 데이터 입출력을 'FIFO: first-in-first-out'로 제한하는 데이터 구조이다. 목록 마지막에 데이터를 추가하며 목록 맨 앞에서 데이터를 꺼낸다. shift() 메서드는 배열의 첫 번째 데이터를 꺼내서 반환하며 배열 길이는 1만큼 줄어든다.

unshift() 메서드는 shift()와 반대로 동작하며, 매개변수로 넘겨받은 데이터를 배열의 처음에 추가하며 늘어난 배열 길이를 반환한다.

정렬 메서드

배열 순서를 직접 조작하는 메서드는 reverse()와 sort() 두 가지이다. reverse() 메서드는 단순히 배열에 저장된 데이터 순서를 뒤집는다. sort() 메서드는 데이터를 정순, 즉 가장 작은 값이 첫 번째에 오고 가장 큰 값이 마지막에 오도록 정렬한다. 이를 위해 이면에서 String() 함수를 호출해 데이터를 문자열로 변환한 후에 이를 비교하여 순서를 판단한다. 숫자만으로 이루어진 배열에서 똑같이 동작하는 단점이 있다. 이럴 때는 '비교함수'를 넘겨서 순서를 조절한다.

sort() 메서드의 매개변수로 쓸 수 있는 비교 함수 예제:

function compare(value1, value2) {
    if (value1 < value2) {
        return -1;
    } else if (value1 > value2) {
        return 1;
    } else {
        return 0;
    }
}

reverse()와 sort()는 모두 자신을 호출한 배열에 대한 참조를 반환하므로, array.sort(compare).reverse() 와 같이 체인 형태로 쓸 수 있다.

숫자 타임이나 Date 객체처럼 숫자형 값을 반환하는 객체에서의 비교함수는 두 값을 빼기만 하면 된다.

조작 메서드

concat() 메서드는 현재 배열을 복사한 후 메서드의 매개변수를 새 배열 마지막에 추가하여 반환한다.

slice() 메서드는 매개변수를 하나만 넘기면 해당 인덱스에서 끝까지 모든 데이터를 가져오고, 매개변수를 두 개 넘기면 첫 번째 매개변수에 해당하는 인덱스부터 두 번째 매개변수에 해당하는 인덱스의 바로 앞까지 가져온다. 원래 배열은 전혀 건드리지 않는다.

매개변수를 음수로 넘기면 배열 길이 해당 값을 더한 숫자를 대신 사용한다. 예로, 길이가 5인 배열에서 slice(-2, -1)를 호출한 결과는 slice(3, 4)를 호출한 결과와 같다. 두 번째 매개변수가 첫 번째 매개변수보다 작으면 빈 배열을 반환한다.

splice() 메서드의 주요 목적은 배열 중간에 데이터를 삽입하는 것이며, 세 가지 방법으로 사용된다.
* 삭제 – 첫 번째 매개변수(인덱스)부터 두 번째 매개변수(개수)만큼 삭제한다.
* 삽입 – 삽입할 위치, 0(아무것도 삭제하지 않는다), 삽입할 데이터 순서로 3개 이상의 매개변수를 넘긴다.
* 대체 – 삽입과 삭제를 조합한다. splice(2, 1, "red", "green")은 인덱스 2의 데이터를 삭제한 다음 문자열 "red"와 "green"을 인덱스 2에 삽입한다.

splice() 메서드는 항상 원래 배열에서 삭제한 데이터의 배열을 반환한다. 삭제한 것이 없다면 빈 배열을 반환한다.

위치 메서드

indexOf()와 lastIndexOf() 메서드에서 첫 번째 매개변수는 검색할 데이터이고, 두 번째 매개변수는 검색을 시작할 인덱스이다. indexOf() 메서드는 배열의 처음(인덱스 0)에서 검색을 시작하여 마지막까지 검색하고, lastIndexOf() 메서드는 배열의 마지막에서 검색을 시작하여 처음까지 검색한다. 두 메서드는 찾아낸 데이터의 인덱스를 반환하며, 찾지 못하면 -1을 반환한다. === 연산자로 비교해서 일치하는(타입까지 일치하는) 데이터를 검색한다.

var numbers = [1,2,3,4,5,4,3,2,1];

alert(numbers.indexOf(4));
alert(numbers.lastIndexOf(4));

alert(numbers.indexOf(4, 4));
alert(numbers.lastIndexOf(4, 4));

var person = { name: "Nolboo" };
var people = [{ name: "Nolboo" }];
var morePeople = [person];

alert(people.indexOf(person));
alert(morePeople.indexOf(person));

세번째 결과부터 잘 이해가 안됨;;

반복 메서드

반복 메서드는 배열의 각 데이터에서 실행할 함수와 함수를 실행할 스코프 객체를 매개변수로 받는다. 콜백함수는 모두 데이터, 데이터의 인덱스, 배열 자체의 세 가지 매개변수를 받는다.

  • every() – 반환값이 전부 true이면 true를 반환한다.
  • filter() – 반환값이 true인 데이터를 새 배열에 저장하여 반환한다.
  • forEach() – 반환값이 없으며, 해당 배열에 for 문을 실행한 것과 같다.
  • map() – 결과를 새 배열에 저장하여 반환한다.
  • some() – 반환값 중 하나라도 true이면 true를 반환한다.

감소 메서드

reduce()와 reduceRight()는 배열을 순회하며 콜백함수를 실행하고 값을 하나 만들어 반환한다. reduce() 메서는 배열의 첫 번째 데이터에서 시작하여 마지막까지, reduceRight()는 배열의 마지막 데이터에서 시작하여 첫 번째까지 진행한다.

첫 번째 매개변수는 각 데이터에서 실행할 콜백 함수이고, 두 번째 매개변수는 감소 작을 시작할 초기값이다. 콜백함수가 넘겨받는 매개변수는 이전 값, 현재 값, 현재 값의 인덱스, 현재 배열 네 가지이다. 콜백함수가 반환하는 값은 자동으로 다음 데이터에서 실행하는 콜백 함수의 첫 번째 매개변수가 된다.

5.3 Date 타입

Date 타입은 1970년1월1일 자정부터의 밀리초 숫자로 저장하며, 285,616년 전후의 날짜까지를 정확히 표현할 수 있다.

날짜 객체를 생성할 때는 new 연산자 다음에 Date 생성자를 쓰며, 매개변수를 넘기지 않으면 현재 날짜와 시간이 저장된다.

var now = new Date();

밀리초 매개변수의 메서드는 Date.parse()와 Date.UTC()가 있다.

Date.parse() 메서드는 날짜 문자열을 받고 밀리초로 변환을 시도한다.

  • 월/일/년(6/13/2004)
  • 월이름 일, 년(January 12, 2004)
  • 요일 월이름 일 년 시:분:초 타임존(Tue May 25 2004 00:00:00 GMT-0700)
  • ISO 8601 확장형식 YYYY-MM-DDTHH:mm:ss.sssZ(2004-05-25T00:00:00) 이 형식은 ECMAScript 5 호환 브라우저에서만 지원된다.

올바른 날짜 형식이 아닐 때는 NaN을 반환한다.

Date 생성자는 (Date.parse() 메서드 없이) 문자열을 넘겨받으면 이면에서 Date.parse()를 호출한다.

Date.UTC()의 매개변수는 해당 시각의 년, 월 인덱스(0이 1월), 일(1~31), 시(0~23), 분, 초, 밀리초이다. 필수는 년, 월 인덱스 2개이며, 일을 생략하면 1일로 간주하며, 다른 매개변수는 모두 0으로 간주한다.

Date 생성자에 Date.UTC() 형식의 매개변수를 넘기면 이면에서 Date.UTC()를 호출하지만 날짜와 시간을 GMT가 아닌 지역 시간을 사용한다.

ECMAScript 5에서는 현재 시각을 밀리초로 반환하는 now() 메서드가 추가되었다. 코드의 실행시간을 측정하는 프로파일링 작업을 쉽게 할 수 있다.

상속된 메서드

Date 타입 역시 toLocaleString(), toString(), valueOf() 메서드를 오버라이드하나, 조금 다른 값을 반환하며 브라우저마다 반환 형식이 상당한 차이가 있어 디버그 목적으로나 쓸만하지 사용자에게 표시하기에는 적당하지 않다.

valueOf() 메서드가 반환하는 값에는 문자열이 전혀 들어있지 않은 밀리초 표현을 쓴다. 이를 통해 날짜 순서를 쉽게 비교할 수 있다.

날짜 표시 메서드

toDateString(), toTimeString(), toLocaleDateString(), toLocaleTimeString(), toUTCString 등이 있으나 반환하는 문자열이 브라우저에 따라 크게 달라 사용자 인터페이스에 일관된 날짜 형식을 쓰고 싶다면 그대로 사용할 수는 없다.

날짜/시간 부속 메서드

날짜의 특정 부분을 가져오거나 설정하는 데 쓰인다. 표 참조

5.4 RegExp 타입

var expression = /pattern/flags;

패턴 부분에는 정규 표현식을 나타내는 식을 쓰며, 문자 클래스, 수량자, 그룹, 룩어헤드, 역참조 등이 포함된다. 플래그를 통해 어떻게 동작할지 나타내며, 세 가지가 있다.

  • g – 전역모드. 지정하면 문자열에서 패턴을 찾는 즉시 종료하지 않고 문자열 전체에서 동작한다.
  • i – 대소문자 비구분 모드.
  • m – 여러 줄 모드. 테스트의 줄 끝에 도달해도 멈추지 않고 계속 패턴을 찾는다.

다음 '메타문자'를 패턴에 쓸 때는 반드시 역슬래시로 이스케이프해야 한다.

( [ { \ ^ $ | } ] ) ? * + .

정규 표현식 리터럴이나 RegExp 생성자를 사용하여 정규 표현식을 생성할 수 있다. RegExp 생성자의 매개변수는 "패턴이 될 문자열"과 옵션인 "플래그를 나타내는 문자열"이며, RegExp 생서자에 정규 표현식 리터럴을 넘기면 안 된다. RegExp 생성자에 메타 문자를 쓸 때는 반드시 이중으로 이스케이프해야 한다.

ECMAScript 3판에서 리터럴로 생성한 정규 표현식은 항상 같은 RegExp 인스턴스를 공유하지만 RegExp 생성자는 호출할 때마다 새로운 인스턴스를 생성한다. ECMAScript 5에서는 리터럴로 정규 표현식을 생성할 때도 RegExp 생성자를 호출하게끔 명확히 정했다.

정규 표현식 인스턴스 프로퍼티

RegExp 인스턴스에는 다음 프로퍼티가 있으며 각 프로퍼티는 패턴에 대한 정보를 포함한다.

  • global – g 플래그 설정 여부 불리언 값.
  • ignoreCase – i 플래그 설정 여부 불리언 값.
  • lastIndex – 패턴 매칭의 시작 위치를 나타내는 정수 값이며, 항상 0에서 시작한다.
  • multiline – m 플래그 설정 여부 불리언 값.
  • source – 정규 표현식을 생성한 문자열. 항상 리터럴 형식으로 반환하되 여닫는 /문자는 포함되지 않는다. 생성자를 통해 생성되었더라도 리터럴 형식으로 반환한다.

정규 표현식 인스턴스 메서드

가장 많이 쓰는 메서드는 그룹을 캡처할 의도로 만들어진 exec() 메서드이며, 패턴을 테스트할 문자열을 매개변수로 받고 패턴에 일치하는 문자열 배열을 반환한다. 일치하는 부분을 찾을 수 없을 때는 null을 반환한다. 반환하는 배열은 Array의 인스턴스인 동시에 일치 위치를 나타내는 index 프로퍼티, exec() 메서드에 넘긴 문자열인 input 프로퍼티가 추가된다. exec() 메서드가 반환하는 첫 번째 데이터는 패턴에 일치하는 부분 전체이며, 다른 데이터는 표현식에서 캡처한 부분이다. 캡처 그룹이 없다면 반환하는 배열에는 데이터가 단 하나만 존재한다.

exec() 메서드는 g 플래그가 설정되어 있더라도 한 번에 한 가지 매치에 관한 정보만 반환한다. g 플래그가 설정되어 있지 않으면 exec()를 같은 문자열에 여러 번 호출하더라도 첫 번째 매치에 대한 정보만 반환한다. 패턴에 g 플래그가 설정되어 있으면 exec()를 호출할 때마다 문자열 안쪽으로 더 깊숙이 들어가며 검색한다.

test() 메서드는 문자열이 패턴에 일치할 때는 true를 반환하고 그렇지 않으면 false를 반환한다. if문에서 자주 사용한다.

객체 프로토타입에서 상속한 메서드 toLocaleString()과 toString()은 정규 표현식을 어떻게 생성했는지에 관계없이 리터럴 표현을 반환한다. valueOf() 메서드는 정규 표현식 자체를 반환한다.

RegExp 생성자 프로퍼티

정식 이름과 짧은 이름이 있는데 오페라는 짧은 이름을 지원하지 않는다. 다음 표는 RegExp 생성자의 프로퍼티이다.

정식이음 짧은 이름 설명
input $_ 마지막으로 테스트한 텍스트
lastMatch $& 마지막으로 일치한 문자열
lastParen $+ 마지막으로 일치한 캡처 그룹
leftContext $' input 텍스트에서 lastMatch 앞에 있는 문자열
multiline $* 모든 정규 표현식이 다중 행 모드를 사용해야 하는지 표현하는 불리언 값
RightContext $' input 텍스트에서 lastMatch 다음에 오는 문자열

짧은 이름은 반드시 대괄호 표기법을 써야한다.

정규 표현식 생성자에는 캡처 그룹을 아홉 개까지 지정할 수 있는 프로퍼티도 있으며, RegExp.$1 형식으로 쓰면 첫 번째 캡처 그룹을 나타내고, RegExp.$9은 아홉 번째 캡처 그룹을 나타낸다. exec()나 test()를 호출할 때마다 자동으로 값이 설정된다.

패턴의 한계

ECMAScript의 정규 표현식은 완전히 개발된 상태이지만 펄 같은 언어에서 가능한 고급 정규 표현식 기능에는 미치지 못한다. 다음 기능은 ECMAScript 정규 표현식에서 지원하지 않는 기능이다. 자세한 정보는 Regular-Expressions.info – Regex Tutorial, Examples and Reference – Regexp Patterns를 참고한다.

  • 테스트의 처음과 마지막에 일치하는 \A와 \Z
  • 룩비하인드
  • 병합 클래스
  • 최소 그룹(atomic group)
  • 유니코드 지원(한 번에 한 문자를 찾는 기능은 지원한다.)
  • 이름 붙은 캡처 그룹
  • s(한 줄 모드), x(공백 무시 모드) 플래그
  • 조건문
  • 정규 표현식 주석

5.5 Function 타입

모든 함수는 function 타입의 인스턴스이며 프로퍼티와 메서드가 있으며, 함수 이름은 단순히 함수 객체를 가리키는 포인터이다.

보통 함수 선언 문법(함구 표현식)을 통해 정의한다.

fuction sum (num1, num2) {
    return num1 + num2;
}

함수 선언은 다음 함수 표현식과 거의 정확히 일치한다.

var sum = function (num1, num2) {
    return num1 + num2;
};

이 코드는 변수 sum을 정의하면서 함수로 초기화하였다. 함수 표현식에서 function 키워드 다음에 함수 이름이 없는 점에 주목한다. 변수 sum으로 함수를 참조하므로 함수 이름은 필요치 않다. 또한 표현식 마지막에 다른 변수 초기화 문장과 마찬가지로 세미콜론을 쓴 점도 눈여겨 본다.

함수를 정의하는 마지막 방법은 Function 생성자를 이용하는 방법이다. Function 생성자에 넘기는 매개변수 숫자에는 제한이 없다. 매개변수 중 마지막은 함수 바디로 간주하며 그 이전에 있는 매개변수는 함수의 매개변수로 전달된다.

var sum = new Function("num1", "num2", "return num1 + num2") // 권장하지 않음.

오버로딩 없음(다시 설명합니다)

함수 이름이 단순한 포인터임을 이해하면 ECMAScript에서 함수 오버로딩이 불가능한 이유도 이해할 수 있다.

함수 선언 vs 함수 표현식

자바스크립트 엔진이 실행 컨텍스트에 데이터를 불러올 때 중요한 차이가 하나 있다. 함수 선언은 어떤 코드도 실행하기 전에 이미 모든 실행 컨텍스트에 접근하고 실행할 수 있지만, 함수 표현식은 코드 실행이 해당 줄까지 진행하기 전에는 사용할 수 없다.

alert(sum(10,10));
function sum(num1, num2) {
    return num1 + num2;
}

이 코드는 아무 에러 없이 실행되는데, 코드를 실행하기 전에 '함수 선언 호이스팅'을 통해 함수 선언을 읽고 실행 컨텍스트에 추가하기 때문이다. 자바스크립트 엔진은 코드를 평가할 때 제일 먼저 함수 선언을 찾은 다음 이들을 맨 위로 올린다. 즉 함수 선언이 소스코드에서 해당 함수를 실행하는 부분보다 뒤에 있어도 자바스크립트 엔진이 함수 선언을 '끌어올린다(hoist)'.

alert(sum(10,10));
var sum = function (num1, num2) {
    return num1 + num2;
};

위의 코드는 에러를 내는데, 함수가 함수 선언이 아니라 초기화 문장의 일부이기 때문이다. 코드 첫 줄에서 '예기치 못한 식별자(unexpected identifier)'에러를 낸다.
이런 차이를 제외하면 함수를 이름으로 호출할 때 두 문법 사이에 다른 차이는 없다.

함수 표현식을 쓰면서도 함수 선언처럼 보이게, 즉 var sum = function sum() {}처럼 쓸 수 있다.

값처럼 쓰는 함수

ECMAScript에서 함수 이름은 단지 변수일 뿐이므로 함수도 다른 값이 올 수 있는 곳이라면 어디든 올 수 있으며, 함수를 다른 함수에 매개변수로 넘기거나, 함수가 실행 결과로 다른 함수를 반환하는 일이 가능하다.

함수의 내부 구조

함수 내부에는 arguments, this라는 특별한 객체가 있다. arguments 객체는 배열과 비슷한 객체이며 함수에 전달된 매개변수를 모두 포함한다.

this는 함수가 실행 중인 컨텍스트 객체에 대한 참조이다. 함수를 웹 페이지의 전역 스코프에서 호출했다면 this 객체는 window를 가리킨다.

caller 프로퍼티에는 해당 함수를 호출한 함수에 대한 참조를 저장하며 전역 스코프에서 호출했다면 null이 저장된다.

함수의 프로퍼티와 메서드

모든 함수에 공통인 프로퍼티는 length와 prototype이다. length 프로퍼티는 함수가 넘겨받을 것으로 예상하는 이름 붙은 매개변수의 숫자이다.

prototype 프로퍼티는 모든 참조 타입의 인스턴스 메서드가 존재하는 곳이다. 즉 toString()이나 valueOf() 같은 메서드는 prototype에 존재하며 객체 인스턴스는 이에 접근한다. prototype 프로퍼티는 개발자 자신만의 참조 타입과 상속을 정의할 때 매우 중요하며, 열거할 수 없는 프로퍼티이므로 for-in 문에 나타나지 않는다.

apply()와 call() 메서드는 소유자인 함수를 호출하면서 this를 넘기는데, 결과적으로 함수 내부에서 this 객체의 값을 바꾸는 것이나 마찬가지이다. apply() 메서드는 매개변수로 소유자 함수에 넘길 this와 매개변수 배열을 받는다. 두 번째 매개변수는 Array의 인스턴스일 수도 있고 arguments 객체일 수도 있다. call 메서드는 매개변수를 전달하는 방식이 다르다. this가 첫 번째 매개변수인 것은 같지만, 반드시 매개변수를 각각 나열해야 한다. arguments 객체를 그대로 전달해도 되거나 데이터가 배열 형태로 준비되어 있다면 apply()가 나을 것이고, 그렇지 않다면 call90이 나을 것이다.

ECMAScript 5판에서 정의된 bind()는 새 함수 인스턴스를 만드는데 그 this는 bind()에 전달된 값이다.

5.6 원시 래퍼 타입

Boolean, Number, String은 원시 값을 편리하게 조작하기 위해 디자인된 참조타입이다. 원시 값을 읽을 때마다 이에 해당하는 래퍼 타입이 이면에서 생성되므로 메서드를 사용할 수 있다.

참조 타입과 원시 래퍼 타입의 주요 차이는 그 생명 주기이다. new 연산자를 사용해 참조 타입의 인스턴스를 만들면 스코프를 벗어날 때까지 메모리에 존재하지만, 자동으로 생성된 원시 래퍼 타입은 코드의 해당 행을 벗어나는 즉시 파괴된다. 따라서 원시 래퍼 타입에는 런타임에 프로퍼티나 메서드를 추가할 수 없다.

Object 생성자에 문자열을 넘기면 String의 인스턴스가 생성되며 숫자를 넘기면 Number의 인스턴스가, 불리언을 넘기면 Boolean의 인스턴스가 생성된다. 염두에 둘 점은 new를 사용해 원시 래퍼 생성자를 호출한 것과 같은 이름의 형 변환 함수를 호출한 결과는 다르다는 것이다.

불리언 타입

원시 불리언 값과 Boolean 객체를 반드시 구분할 수 있어야 하며 Boolean 객체는 절대 쓰지 마라.

Number 타입

toFixed() 메서드는 지정한 만큼의 정확도로 소수점을 찍은 문자열을 반환하며, 매개변수로 지정한 자리 수보다 크다면 반올림한다.

일반적으로 소수점 이하 20자리까지 지원한다.

toExponential() 메서드는 숫자를 지수 표기법 문자열로 반환한다.

toPrecision() 메서드는 숫자에 따라 지수 표기법이나 소수점 표기법으로 선택하여 반환한다. 숫자 표현에 사용할 총 자리 수를 매개변수로 받는다(지수 부분은 받지 않는다)

일반적으로 1부터 21 사이의 자리 수를 넘길 수 있다.

String 타입

문자열을 나타내는 객체이며 String 생성자를 쓴다.

var stingObject = new String("hello world");
글자 메서드

charAt()와 charCodeAt()는 원하는 문자의 인덱스를 매개변수로 받는다. charAt()은 한 글자의 문자열로 반환하고, charCodeAt()는 해당하는 문자의 문자코드를 반환한다.

문자열 조작 메서드

concat() 메서드는 문자열을 병합한다. + 연산자가 더 자주 쓰이며 더 빨리 동작한다.

slice(), substr(), substring() 메서드는 문자열 일부로 새 문자열을 만든다. 어디서부터 가져올 것인가를 첫 번째 매개변수로, 어디까지(직전)가 두 번째이다. substr()의 두 번째 매개변수는 문자 몇 개를 가져올지이다. 세 가지 모두 두 번째 매개변수를 생략하면 열 전체 길이가 기본 값이다.

var = stringValue = "hello world";
alert(stringValue.slice(3));        // "lo world"
alert(stringValue.substring(3));    // "lo world"
alert(stringValue.substr(3));       // "lo world"
alert(stringValue.slice(3, 7));     // "lo w"
alert(stringValue.substring(3, 7)); // "lo w"
alert(stringValue.substr(3, 7));    // "lo worl"

slice()는 음수 매개변수를 받으면 전체 문자열 길이에 해당 매개변수를 더한 값을 사용한다. substr()은 첫 번째 매개변수에 음수를 받으면 전체 문자열 길이에 해당 매개변수를 더한 값을 사용한다. 두 번째 매개변수가 음수이면 0을 사용한다. substring()은 음수 매개변수를 모두 0으로 바꾼다.

문자열 위치 메서드

indexOf()와 lastIndex()는 문자열의 위치를 찾으면 위치를 반환하며 찾지 못했을 때는 -1을 반환한다. indexOf()는 문자열 처음에서, lastIndexOf()는 문자열 마지막에서 검색을 시작한다. 두 번째 매개변수는 옵션이며, 검색을 시작할 인덱스이다.

trim() 메서드

문자열을 복사한 후 앞뒤 공백을 모두 제거한 결과를 반환한다.

대소문자 메서드

toLowerCase(), toLocaleLowerCase(), toUpperCase(), toLocaleUpperCase() 네 가지. 일부 언어에서는 대소문자 변환에 따른 특별한 규칙이 필요하며 이 때문에 지역 메서드가 필요하다. 일반적으로 작성한 코드가 어느 지역에서 실행될지 모른다면 지역 메서드를 쓰는 편이 안전한다.

문자열 패턴 매칭 메서드

String 타입에서 문자열 내에서 패턴 매칭하는 메서드 중 가장 많이 쓰이는 것은 match()이다. RegExp 객체의 exec() 메서드와 같은 결과를 반환한다. 매개변수로 정규 표현식 리터럴이나 RegExp 객체를 하나 받는다.

search() 메서드는 패턴에 일치하는 첫 번째 문자열 인덱스를 반환하며 찾지 못했을 때는 -1을 반환한다. 항상 문자열의 처음에서 검색을 시작한다.

replace()는 문자열 일부를 바꾼다. 첫 번째 매개변수를 RegExp 객체 또는 문자열(단, 이 문자열을은 정규표현식으로 변환되지 않는다)이고, 두 번째 매개변수는 문자열 또는 문자열을 반환하는 함수이다. 일치하는 문자열 전체를 바꾸려면 정규 표현식에 g 플래그를 써서 넘긴다.

두 번째 매개변수에 문자열을 쓸 대 정규 표현식에서 얻은 정보를 다양한 방법으로 응용하는 특수문자가 있다.

특수문자 대응 문자열
$$ $
$& 패턴에 일치하는 문자열 전체. RegExp.lastMatch와 같다.
$' 일치하는 문자열 앞의 텍스트. RegExp.rightContext와 같다.
$' 일치하는 문자열 다음의 텍스트. RegExp.leftContext와 같다.
$n 'n'번째 캡처 그룹이며 'n'은 0부터 9사이.
$nn 'nn'번째 캡처 그룹이며 'nn'은 00부터 99사이.

replace()의 두 번째 매개변수에는 함수도 쓸 수 있다. 일치하는 것이 하나뿐일 때 콜백함수는 자동으로 매개변수를 세 개 받는다. 일치하는 문자열, 해당 문자열의 위치, 전체 문자열. 일치하는 것이 여럿일 때는 이들 모두가 매개변수로 전달되며 그다음에는 매치한 위치, 마지막으로 원래 문자열이 전달된다. 콜백함수는 반드시 문자열을 반환해야 하며 이 문자열로 일치한 문자열을 대체합니다.

htmlEscape() 함수는 HTML에서 사용할 수 있도록 특수문자를 이스케이프한다. HTML에서 그대로 쓸 수 없는 <, >, &, " 문자를 이스케이프하는 가장 쉬운 방법은 정규 표현식으로 해당 문자를 검색한 다음에 HTML 엔티티를 반환하는 함수를 쓰는 것이다.

split() 메서드는 텍스트를 구분자를 기준으로 분리해서 배열에 담아 반환한다. 구분자에는 문자열과 RegExp 객체를 쓸 수 있는데 문자열을 정규 표현식으로 간주하지는 않는다. 두 번째 매개변수(옵션)는 반환받을 배열의 크기를 지정하는 숫자이다.

split()과 정규 표현식의 캡처 그룹의 브라우저 간 비호환성 문제는 스티브 레비슨의 자바스크립트 split 버그가 마침내 수정되었다을 읽어본다.

localeCompare() 메서드

문자열 두 개를 비교한 후 -1, 0, 양수(대부분 1)를 반환한다. 메서드를 호출한 텍스트가:
* 매개변수로 넘긴 문자열보다 알파벳 순서상 뒤에 있다면 : -1
* 매개변수 문자열과 일치하면 : 0
* 매개변수로 넘긴 문자열보다 알파벳 순서상 앞에 있다면 : 양수(대부분 1이지만 브라우저에 따라 값이 다룰 수 있어 추상화한 함수를 만드는 편이 좋다)

fromCharCode() 메서드

String 생성자에서 문자 코드를 받아 이를 문자열로 변환하는 메서드.

HTML 메서드

자바스크립트에서 동적으로 HTML 형식을 생성하기 위한 메서드이나 시맨틱 마크업을 해치므로 거의 사용하지 않는다.

5.7 내장된 싱글톤 객체

Global, Math. 싱글톤(singleton)이란 인스턴스를 한 개만 만들도록 하는 객체. Object, Array, String 등은 인스턴스를 여럿 만들어 사용하지만 Math 객체는 인스턴스를 만들지 않고 메서드의 네임스페이스 용도로만 사용한다.

Global 객체

Global 객체는 명시적으로 접근할 수 없다는 점에서 ECMAScript에서 가장 독특한 객체이다. ECMA-262는 Global 객체를 소유자가 없는 모든 프로퍼티와 메서드를 담는 객체로 정의한다. 사실 전역 변수나 전역 함수라는 것은 존재하지 않는다. 전역에서 정의한 변수와 함수는 모두 Global 객체의 프로퍼티가 된다.

URI 인코딩 메서드

브라우저에 전달한 URI를 인코드하는 메서드. 특별한 UTF-8 인코딩을 써서 URI에 사용할 수 없는 문자를 브라우저가 인식하고 이해할 수 있는 문자로 바꾼다.

encodeURI() 메서드는 전체 URI를, encodeURIComponent()는 URI 일부분만 조작한다. encodeURI()는 :이나 /, ?, #처럼 URI 일부분으로 쓰이는 특수문자는 인코드하지 않지만 encodeURIComponent()는 비표준 문자를 모두 인코드하며, URI 마지막에 추가할 문자열에만 사용할 수 있다.

일반적으로 베이스 URI 뒤에 추가할 쿼리스트링을 인코드하는 경우가 많으므로 encodeURIComponent()를 자주 쓰게 된다.

decodeURI()와 decodeURIComponent()는 반대.

위 네 가지 URI 메서드는 ECMA-232 3판에서 폐기한 escape(), unescape() 메서드를 대체한다. 배포할 코드에 ASCII 문자만 변환하는 escape()나 enscape()를 쓰지 마라.

eval() 메서드

ECMAScript에서 가장 강력한 메서드이다. ECMAScript 인터프리터 자체인 듯 동작하며 매개변수로 문자열 하나 받는데 이 문자열은 실행할 수 있는 ECMAScript 코드여야 한다.

다음 두 개의 문장은 같다.

eval("alert('hi')");
alert("hi");

인터프리터가 eval()을 만나면 매개변수를 실제 ECMAScript 문자으로 해석하여 eval()이 있던 위치에 삽입한다. eval()이 실행하는 코드는 eval()을 호출한 실행 컨텍스트의 일부분으로 간주되며 해당 컨텍스트와 같은 스코프 체인을 가진다.

eval() 내부에서 정의한 변수나 함수는 끌어올림의 대상이 아니다. 코드 파싱 단계에서는 문자열이다. 변수나 함수로 정의되는 시점은 eval()이 실행되는 시점이다.

스트릭트 모드에서는 eval() 내부에서 정의한 변수나 함수에 접근할 수 없으며, eval에 값을 할당할 수 없다.

문자열을 코드로 변환하는 기능은 대단히 강력하지만 대단히 위험하기도 하다. eval()을 쓸 때는 항상 주의해야 하며, 사용자가 입력한 데이터에 eval()을 쓸 때는 특히 더 조심해야 한다. 악의적인 사용자가 당신의 사이트나 애플리케이션 보안을 해치는 값을 입력할 수 있기 때문이다.(코드 주입)

Global 객체의 프로퍼티

undefined, NaN, Infinity, Object, Array, Function, Boolean, String, Number, Date, RegExp, Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError 등이 있으며, ECMAScript 5판에서는 undefined, NaN, Infinity에 값을 할당할 수 없음을 명확히 했다. 이들에 값을 할당하려 하면 스트릭트 모드가 아닐 때에도 에러가 난다.

Widnow 객체

ECMA-262에서는 Global 객체에 직접 접근할 수 없도록 했지만, 웹 브라우저에서는 window 객체를 통해 Global 객체에 접근할 수 있다. 전역 스코프에서 선연한 변수와 함수는 모두 window 객체의 프로퍼티가 된다.

var color = "red";

function sayColor(){
    alert(window.color);
}

window.sayColor();  // "red"

Math 객체

수학 공식과 각종 상수를 Math 객체에 저장한다. 필요한 계산이 Math 객체에 구현되어 있다면 직접 만들기보다 빠르다.

Math 객체의 프로퍼티
프로퍼티 설명
Math.E 자연로그의 밑수인 e의 값
Math.LN10 10의 자연로그
Math.LN2 2의 자연로그
Math.LOG2E e의 밑수가 2인 로그(base 2)
Math.LOG10E e의 밑수가 10인 로그(base 10)
Math.PI 파이값
Math.SQRT1_2 1/2의 제곱근
Math.SQRT2 2의 제곱근
min()과 max() 메서드

min()과 max() 메서드는 한 그룹의 숫자 중에서 가장 작은 숫자와 가장 큰 숫자를 찾는다. 매개변수 숫자에 제한이 없다.

배열 데이터 중에서 최대값이나 최소값을 찾을 때는 apply() 메서드를 쓸 수 있다.

var values = [1, 2, 3, 3, 4, 5, 6, 7, 8];
var max = Math.max.apply(Math, values);

이 테크닉의 요점은 apply()의 첫 번째 매개변수로 Math 객체를 넘겨서 this가 정확히 설정되도록 하는 것이다. 이렇게 하면 두 번째 매개변수로 배열을 넣었을 때 올바르게 동작한다.

반올림 메서드

Math.ceil()은 올림, Math.floor()는 내림, Math.round()는 반올림이다.

random() 메서드

0과 1 사이의 난수를 반환하되 0이나 1을 반환하지는 않는다.

number = Math.floor(Math.random() * total_number_of_choices + first_possible_number)

경우의 수를 세는 것보다 범위만 제공하여 그 사이의 난수를 반환하는 함수를 만드는 편이 편리하다.

function selecForm(lowerValue, upperValue) {
    var choices = upperValue - lowerValue + 1;
    return Math.floor(Math.random() * choices + lowerValue);
}

var num = selectForm(2,10);
alert(num); // 2와 10사이의 난수. 2와 10을 포함 

var colors = ["red", "green", "blue", "yellow", "black", "purple", "brown"];
var color = colors[selectFrom(0, colors.length-1)];
기타 메서드
메서드 반환값
Math.abs(num) num 절대값
Math.exp(num) Math.E의 num제곱
Math.log(num) num의 자연로그
Math.pow(num, power) num의 power제곱
Math.sqrt(num) num의 제곱근
Math.acos(x) x의 아크코사인
Math.asin(x) x의 아크사인
Math.atan(x) x의 아크탄젠트
Math.atan2(y, x) y/x의 아크탄젠트

관련 글들