2014-10-19 <Java Script for Web Developer> 1~6장 요약본
1장. 자바스크립트란 무엇인가
1995년 처음 등장할 때 주요 목적은 서버 언어에서 담당하던 입력 유효성을 검사하기 위해서였다.
자바스크립트는 완전히 익히려면 자바스크립트의 성격과 역사, 한계에 대해 이해해야 한다.
1.1 간추린 역사
넷스케이프의 브랜든 아이흐가 처음엔 Mocha, 나중에 LiveScript라고 불린 스크립트 언어를 개발했는데 넷스케이프 내비게이터 2에서 브라우저와 서버(서버쪽은 LiveWire) 모두에서 사용하려는 것이었으며, 출시하기 직전 자바의 인기에 편승하기 위해 자바스크립트로 이름을 바꾸었다.
IE 3에선 저작권 문제를 피하기 위해 JScript라고 이름지었다. 이는 자바스크립트가 하나의 언어로 본격적으로 개발되기 시작한 의미 있는 날이다.
1997년 자바스크립트 1.1이 ECMA에 제안되었다. ECMA는 TC39 위원회에서 “문법과 의미를 표준화하여 일반적인 목적에 쓸 수 있고 플랫폼을 가리지 않으며 제조사에 중립인 스크립트 언어”를 만들기로 했으며, 1998년 ISO와 IEC에서도 ECMAScript를 표준(ISO/IEC-16262)으로 받아들였다.
1.2 자바스크립트 구현
자바스크립트 구현은 다음 세 가지로 나뉜다.
- 코어 (ECMAScript)
- 문서 객체 모델 (DOM:Document Object Model)
- 브라우저 객체 모델 (BOM:Browser Object Model)
1.2.1 ECMAScript
ECMAScript ‘판’
ECMA-262 3판이야말로 표준에 대한 첫 번째 진짜 업데이트이며, 진정한 프로그래밍 언어로 간주하는 이유다. ECMAScript 3.1이 ECMA-262 5판이라는 이름으로 2009년 12월 3일 공식 발표되었다.
웹 브라우저의 ECMAScript 지원
넷스케이프 내비게이터 4.06의 자바스크립트 1.3 버전이 ECMA-262 초판과 완전히 호환되었으며, 2008년부터 주요 웹 브라우저 모두 ECMA-262 3판을 준수하였다.
IE 8은 5판을 구현한 최초의 브라우저였으며 IE 9은 5판을 완전히 지원한다. 곧이어 파이어폭스 4도.
1.2.2 문서 객체 모델(DOM)
DOM은 XML을 HTML에서 사용할 수 있도록 확장한 API이며, 전체 페이지를 노드의 계층 구조로 변환한다. HTML(XML) 페이지의 부분을 데이터를 포함하는 다양한 타입의 트리 노드로 만들고 이를 통해 문서의 콘텐츠와 구조를 자유롭게 수정할 수 있다.
DOM이 필요한 이유
브라우저별로 다른 DHTML을 지원하면서 개발자들은 새로고침 없이 콘텐츠의 모양을 바꿀 수 있게 되었으나 브라우저 별로 HTML 페이지를 만들어야 하는 재앙이 발생했다. W3C에서 DOM 관련 작업을 시작했고 레벨 1,2,3가 차례대로 나왔다. 레벨 0는 W3C 작업 이전에 IE 4.0과 넷스케이프 내비게이터 4.0이 지원했던 DHTML을 말한다.
1.2.3 브라우저 객체 모델(BOM)
브라우저 창에 접근하고 조작할 수 있게 하는 인터페이스이며, 브라우저에 표시된 페이지와는 별개의 컨텍스트에서 브라우저와 상호작용할 수 있다.
표준이 없어 자주 문제를 일으켰으나, HTML5는 BOM을 공식 명세의 일부로 표준화하고 있어 개선되고 있다.
1.3 자바스크립트 버전
넷스케이프 소스코드가 모질라 프로젝트로 오픈소스로 공개될 때 자바스크립트 버전은 1.3이었다.
1.4 요약
자바스크립트는 웹페이지와 상호작용하도록 디자인된 스크립트 언어이며, 핵심 기능을 담당하는 ECMAScript(ECMA-262에서 정의), 웹 페이지 콘텐츠를 조작하는 DOM, 브라우저와 상호작용하는 BOM으로 구현된다.
주요 웹 브라우저들의 ECMAScript 3에 대한 지원은 일반적으로 좋은 편이며 ECMAScript 5에 대한 지원은 나아지고 있지만, DOM 지원은 편차가 심하다. BOM은 최근에야 HTML5에서 표준화되고 있으므로 일부 외에는 각기 다르다.
요약자 후기
- 요약치곤 좀 긴 것 같다. 요약도 실력인 것 같은데..
- 다른 두 분의 요약글 링크를 걸며 빠져나가본다^^
관련 글들
2장. HTML 속의 자바스크립트
2.1 <script> 요소
자바스크립트를 HTML 페이지에 삽입하는 일차적인 방법이며, async, charset, defer, language, src, type의 여섯가지 속성이 있다. 현재 language는 페기되었고, charset 값은 무시된다. type 속성은 아직 습관적으로, 또는 브라우저 호환성을 위해 “text/javascript” 라 표기하곤 하나, 기본값이라서 생략 가능하다.
<script> 요소 안의 인라인 자바 스크립트 코드는 위에서부터 차례로 해석되며 코드 전체를 해석하기 전에는 페이지의 나머지 콘텐츠를 불러오지도 않고 표시하지도 않는다.
인라인 자바스크립트 코드에서 문자열 “”는“/” 문자를 \로 이스케이프해야 한다.
자바스크립트를 외부 파일에서 불러오려면 파일의 URL을 속성값으로 src 속성을 사용한다. 외부 파일의 코드를 내려받고 해석하는 동안 페이지 처리가 멈춘다.
<script type="text/javascript" src="example.js"></script>
중요: <script>와 </script> 태그 사이에 스크립트 코드가 있고 src 속성도 사용했다면 브라우저는 스크립트 파일을 내려받아 실행하며 인라인 코드는 무시합니다.
신뢰할 수 있는 외부 도메인의 코드를 마치 페이지의 원래 일부였던 것처럼 불러와서 해석할 수 있다.
2.1.1 태그 위치
전통적으로 <script> 요소는 외부 파일에 참조를 한곳에 관리하기 위해 페이지의 <head> 요소 안에 쓰는 것이 일반적이었다. 그러나, 최신 웹 앱에선 페이지 렌더링 시간의 지연을 막기 위해 <body> 요소 안에, 페이지 콘텐츠 마지막에 -</body> 태그 바로 앞에 - 쓴다.
2.1.2 스크립트 처리 지연(defer 속성)
defer 속성을 설정하면 브라우저는 즉시 코드를 내려받지만 실행은 페이지 전체를 파싱한 후에 한다. 그러나, 지연시킨 스크립트가 항상 순서대로 실행되지는 않으므로 <script> 요소를 하나만 쓰는 것이 최선이다. 그러나, 여러 브라우저 지원 문제 때문에 스크립트는 페이지 맨 마지막에 놓는 것이 최상이다.
2.1.3 비동기 스크립트(async 속성)
스크립트를 모두 내려받을 때까지 기다릴 필요없이 페이지 렌더링을 시작해도 좋으며, 앞선 스크립트 파일을 기다리지 않고 위의 스크립트 파일을 받아도 좋다고 명시하는 것이다. 스크립트가 순서대로 실행된다는 보장이 없어 여러 스크립트 파일 사이에 의존성이 있으면 안되며, DOM을 조작하는 스크립트는 비동기적으로 불러오지 않는 것이 좋다.
2.1.4 XHTML에서 바뀐 점
XHTML에서는 < 기호 다음 공백문자가 있으면 문법 에러가 발생하는데, <를 HTML 엔티티 <로 변경하거나, CDATA 섹션으로 감싸줄 수 있다. 브라우저가 CDATA 섹션을 지원하지 않으면 CDATA 마크업 앞에 자바스크립트 주석 기호를 써야 한다.
2.2 인라인 코드와 외부 파일(src 속성)
외부 파일을 쓸 때 이점.
- 관리가 쉽다.
- 캐싱으로 페이지 불러오는 시간이 짧아진다.
- 외부 파일 불러오는 문법이 HTML과 XHTML 모두 똑같으므로, 위의 편법을 쓰지 않아도 된다.
2.3 문서 모드
IE 5.5는 쿽스와 표준의 두 가지 문서모드 개념을 처음 도입했다. 두 모드의 주요 차이는 콘텐츠 렌더링과 관련된 것이지만 자바스크립트에도 영향이 있다. 다른 브라우저 들도 이를 도입함에 따라 '거의 표준 모드'라는 엄격하지 않은 표준 모드가 등장했다. 이미지 주변의 공백과 테이블 이미지 등에서 차이가 크다.
쿼크 모드는 브라우저 사이의 일관성을 전혀 기대할 수 없으므로 독타입을 이용하여 표준 모드로 사용한다. 표준 모드와 '거의 표준' 모드는 차이를 느낄 수 없기 때문에 일반적으로 '표준 모드'라는 용어는 쿽스 모드를 제외한 둘 모두를 말한다.
2.4 <noscript> 요소
<noscript>는 브라우저가 자바스크립트를 지원하지 않거나 지원이 꺼져 있을 때 대체 콘텐츠를 제공하며, <script> 요소를 제외한 모든 HTML 요소를 사용할 수 있다. 브라우저에서 스크립트를 사용할 수 있을 때는 <noscript> 요소의 콘텐츠는 결코 표시되지 않습니다.
3장. 언어의 기초
3.1 문법
대소문자 구분
변수, 함수 이름, 연산자 모두 대소문자를 구분한다.
식별자(Identifiers)
'식별자'란 변수, 함수, 프로퍼티, 함수 매개변수의 이름이다. 식별자는 한 개 이상의 문자로 표기하며, 첫 번째 문자는 반드시 글자나 밑줄(_), 달러기호($) 중 하나이다.
ECMAScript는 관습적으로 카멜 케이스로 쓴다. 카멜 케이스란 첫번째글자는 소문자로 쓰고 단어가 바뀔 때는 바뀐 단어의 첫 글자를 대문자로 쓰는 표기법이다. 반드시 써야하는 것은 아니지만 ECMAScript 내장 함수와 객체가 카멜 케이스로 표기되어 있어서 따르길 권한다.
- 키워드와 예약어 및 true, false, null은 식별자로 사용할 수 없다.
주석(comments)
ECMAScript는 한 줄 주석과 블록 주석 모두 C언어 스타일로 표기한다.
// 한줄 주석
/*
-
블록 주석은 /*로 시작하고,
-
그 반대 */로 끝난다. */
두번 째와 세 번째 줄에있는 *는 가독성을 위해 추가한 것이며 기업 애플리케이션에서 선호한다.
스트릭트 모드(Strict Mode)
ECMAScript 5에서 도입하였는데, 안전하지 않는 동작에서는 에러를 반환하도록 한다.
전체 스크립트에 스트릭트 모드를 적용하려면 다음 문장을 스크립트 최상단에 추가한다.
"use strict";
함수 단 하나만 적용하려면 다음 선언(pragma)을 함수 본문 맨 앞에 추가한다.
function doSomething(){
"use strict";
// 함수 본문
}
IE 10+, 파이어폭스 4+, 사파리 5.1+, 오페자 12+, 크롬에서 지원한다.
문장(Statements)
ECMAScript에서 각 문장은 세미콜론으로 종료한다. 꼭 써야하는 것은 아니지만 여분의 공백 제거, 압축 시 문법 에러, 성능 향상 등의 이유로 세미콜론으로 종료해라.
C 언어 스타일 문법을 써서 여러 문장을 코드 블록으로 합칠 수 있다. {로 시작하여 }로 끝난다. 실행한는 문장이 하나뿐이더라도 에러의 원인이 되기도 하므로 코드블록을 써라.
3.2 키워드와 예약어
ECMAScript의 키워드와 예약어는 식별자 이름이나 프로퍼티 이름으로 사용하지 마라.
3.3 변수
ECMAScript는 느슨한 변수타입을 사용하므로, 변수에 어떠한 타입의 데이터라도 저장할 수 있다. 모든 변수는 단순히 값에 대한 이름 붙은 플레이스홀더일 뿐이다.
변수를 정의 할 때에는 var 연산자 다음에 변수 이름을 쓴다. var는 키워드이며, 변수 이름은 식별자이다. 변수를 초기화하지 않으면 특별한 값 undefined 가 할당되며, 다음과 같이 변수 선언과 동시에 값을 할당할 수 있다.
var message = "h1";
var 연산자는 변수를 로컬 스코프에서 정의한다. 함수 안에서 var 키워드를 써서 변수를 정의하면 해당 변수는 함수를 종료하는 즉시 파괴된다.
var 연산자를 생략하면 변수를 전역으로 정의할 수 있으며, 함수를 호출하는 즉시 변수가 정의되며, 함수 외부에서도 이 변수에 접근할 수 있다. 그러나 이런 패턴은 피해라.
변수를 여러 개 선언하려면 쉼표로 구분하여 같은 문장에서 선언하고 초기화할 수 있다. 변수 사이에 줄바꿈과 들여쓰기로 읽기 쉽게 한다:
var message = "h1",
found = false,
age = 29;
3.4 데이터 타입
ECMAScript에는 Undefined, Null, Boolean, 숫자, 문자열의 다섯가지 기본적인 데이터 타입(원시 primitive 데이터 타입)이 있으며, 이름-값 쌍의 순서없는 목록 즉, 객체라는 복잡한 데이터 타입도 있다. 이 여섯 가지의 데이터 타입은 동적이어서 한 가지 데이터 타입이 여러 특성을 가질 수 있다.
typeof 연산자(Operator)
typeof 연산자를 통해 데이터 타입을 알 수 있으며, 값(변수)에 typeof 연산자를 적용하면 다음 문자열 중 하나를 반환한다.
- 정의되지 않은 변수 : "undefined"
- 불리언 : "boolean"
- 문자열 : "string"
- 숫자 : "number"
- 함수를 제외한 객체 또는 null : "object"
- 함수 : "function"
undefined 타입
기본적으로 초기화하지 않는 변수에는 항상 undefined 값이 할당된다. 그러나, 변수를 항상 초기화하는 습관을 갖으면 typeof에서 undefined를 반환했을 때 해당 변수가 초기화되지 않은 것인지, 정의되지 않은 것인지 바로 알 수 있다.
Null 타입
Null 타입의 값은 특별한 값인 null이다. null은 빈 객체를 가리키는 포인터이므로 null에 typeof를 호출하면 object를 반환하며, 객체를 사용해야 하지만 해당 객체를 이용할 수 없을 때 null을 사용하여 해당 변수가 객체를 자리키는지 명시적으로 확인할 수 있다.
불리언 타입
불리언 타입은 ECMAScript 에서 가장 많이 쓰이는 데이터 타입 중 하나이며 true와 false 두 가지 리터럴 값만 가진다. 숫자 타입과 달라, ture는 1이 아니며, false는 0이 아니다.
Boolean() 함수는 어떤 타입의 데이터에서도 호출할 수 있고 항상 불리언 값을 반환한다.
데이터 타입 |
true 값 |
false 값 |
불리언 |
ture |
false |
문자열 |
비어 있지 않은 문자열 모두 |
" "(빈 문자열) |
숫자 |
0이 아닌 모든 숫자, 무한대 포함 |
0, NaN |
객체 |
모든 객체 |
null |
Undefined |
해당없음 |
undefined |
if 문 같은 제어문은 타입을 자동으로 불리언으로 바꾸므로 위 표의 변환 규칙을 반드시 이해해야한다.
숫자 타입
기본적인 숫자 리터럴 형식은 10진법이다. 정수는 8진수 리터럴, 16진수 리터럴도 가능하지만 실제 계산시에는 항상 10진수로 변환하여 계산한다.
부동소수점 숫자
부등소수점 숫자를 표현하려면 반드시 소수점이 있어야 하며, 소수점 뒤에 숫자를 쓴다. .1도 가능하지만 권장하지 않는다.
부동소수점 숫자는 정수보다 메모리를 두 배로 소모하므로 소수점 뒤에 숫자가 없다면 정수로 변환된다.
대단히 크거나 작은 부동소수점 숫자를 표현할 때는 'e- 표기법(지수 표기법)'을 쓴다. 기본적으로 소수점 뒤에 0이 6개 이상 있는 모든 부동소수점 숫자를 지수 표기법으로 변환한다.
숫자 범위
메모리 제한 때문에 모든 숫자를 표현할 수 없다. ECMAScript로 표현할 수 있는 최소값은 브라우저마다 다르지만 보통 5e-324이다. 최대값은 보통 1.7976931348623157e+308이다. 이 범위를 벗어나면 자동으로 (-)Infinity로 변환된다.
NaN(Not a Number)
숫자를 반환할 것으로 의도한 조작이 실패했을 때 반환하는 특별한 값이며, 에러를 반환하는 것은 아니다.
NaN이 포함된 모든 조작은 항상 NaN을 반환하며, 심지어 NaN끼리도 서로 일치하지 않는다. isNaN() 함수는 해당 값이 '숫자가 아닌 값'인지 검사한다. 문자열 "10"이나 불리언 값은 숫자도 바꿀 수 있다.
숫자 변환
숫자가 아닌 값을 바꾸는 함수는 어떤 데이터 타입에도 쓸 수 있는 Number(), 문자열을 숫자로 바꾸는 parseInt()와 parseFlaot() 함수 세 가지이다.
Number()함수로 문자열을 숫자로 바꿀 때는 여러 규칙을 기억해야 한다.
정수 형태의 문자열을 숫자로 바꿀 때 보통 parseInt()을 사용하며, var num = parseInt("10", 10)의 형태로 사용한다.
parseFlaot() 함수는 두 번째 소수점과 같은 잘못된 부동소수점 숫자를 만날 때까지 파싱을 계속하여, 그 이후 문자열은 무시한다. 리딩제로는 항상 무시한다. 또한, 항상 10진수 기준으로 파싱하기 때문에 16진수는 항상 0을 반환한다.
문자열 타입
문자열 데이터 타입은 16비트 유니코드 문자의 연속이다. 문자열은 큰따옴표나 작은따옴표로 감싸서 표현한다. PHP에서는 큰따옴표와 작은따옴표를 다르게 해석하지만 ECMAScript는 완전히 똑같게 해석한다. 그러나 짝은 맞아야 한다.
문자 리터럴
문자열의 성질
ECMAScript에서 문자열은 불변(immutable)이다. 즉, 문자열이 만들어지면 그 값을 바꿀 수 없다. 변수에 저장된 문자열을 바꾸려면 기존의 문자열을 파괴하고 다음과 같이 해당 변수에 새 문자열을 채워야 한다.
var lang = "Java";
lang = lang + "Script";
문자열로 변환
값을 문자열로 바꾸는 방법은 거의 모든 값에 존재하는 toString() 메서드를 사용하는 것과 호출할 값이 null이나 undefined일 가능성이 있을 경우 String() 함수를 사용하는 두 가지 방법이 있다.
객체 타입
객체는 데이터와 기능의 집합이다. 객체는 new 연산자 다음에 새로 만들 객체 타입의 이름을 써서 만든다.
var o = new Object();
Object 타입은 이 타입에서 파생하는 모든 객체의 원형이기 때문에 Object 타입의 인스턴스는 Object 타입의 프로퍼티와 메서드를 전부 상속한다.
3.5 연산자(OPERATORS)
단항 연산자(Unary Operators)
단 하나의 값에만 적용되는 연산자이며, 가장 단순한 연산자이다.
증감 연산자
C 언어에서 차용한 것이며, 피연산자의 앞에 쓸 수도 있고 뒤에 쓸 수도 있다. ++를 피연산자 앞에 쓰면 피연산자에 1을 더하며, --를 쓰면 피연산자에서 1을 뺀다.
증감 연산자를 변수 앞에 쓰면 변수의 값을 바꾼 다음 문장을 계산한다. 변수 뒤에 쓰면 문장을 계산한 다음에 적용되는 중요한 차이가 있다.
단항 플러스와 단항 마이너스
비트 연산자(Bitwise Operators)
가이드에서 언급한 대로 건너뜀
불리언 연산자
불리언 연산자에는 NOT과 AND, OR 세가지가 있다.
논리 NOT
논리 NOT 연산자는 느낌표!로 표시하며 모든 값에 적용할 수 있다. 피연사자를 불리언 값으로 변환한 다음 그 결과를 부정하며, 다음과 같이 동작한다.
피연산자 |
반환값 |
객체 |
false |
빈 문자열 |
true |
비어있지 않은 문자열 |
false |
숫자 0 |
true |
0이 아닌 숫자(Infnity 포함) |
false |
null |
true |
NaN |
true |
undefined |
true |
논리 NOT 연산자를 연달아 두 개 쓰면 Boolean() 함수를 쓴 것과 같은 효과가 있다.
논리 AND
앰퍼샌드 2개&&로 나타내며 값 두 개에 적용한다. 피연산자가 불리언 값이 아니어도 사용할 수 있으며, 그 동작은 약간 복잡하다;;
논리 OR
파이프 두개||로 표현한다. 피연산자가 불리언 값이 아니라도 사용할 수 있으며, 그 동작은 약간 복잡하다;;
다음과 같이 그 행동을 이용해서 변수에 null이나 undefined가 저장되지 않게 할 수 있다:
var myObject = preferredObject || backupObject;
곱셈 관련 연산자(Multiplicative Operators)
곱셈, 나눗셈, 나머지의 세가지 곱셈 관련 연산자가 있다.
곱셈
아스테리스크*를 써서 나타내며 두 숫자를 곱하는 데 쓰인다.
나눗셈
슬래시/ 기호로 표현하며, 첫 번째 피연산자를 두 번째 피연산자로 나눈다.
나머지(Modulus)
퍼센트 기호%로 나타내며 나눗셈을 적용하고 나머지를 반환한다.
덧셈 관련 연산자
덧셈
피연산자 중 하나가 문자열이라면 다음 규칙을 따르며, 아래 예제와 같은 흔한 실수를 피해야 한다.
- 피연산자가 모두 문자열이라면 두 번째 문자열을 첫 번째 문자열에 합한다.
- 피연사자 중 하나가 문자열이라면 다른 피연산자를 문자열로 변환하고 두 문자열을 합친다.
var num1 = 5;
var num2 = 10;
var message = "The sum of 5 and 10 is " + num1 + num2;
alert(message); // "The sum of 5 + 10 is 510"
이를 피하기 위해서는 (num1 + num2)와 같이 괄호로 감싸 괄호 안을 먼저 계산한 후에 그 결과를 문자열에 합쳐야 한다.
뺄셈
관계 연산자(Relational Operators)
관계 연산자에는 미만<과 초과>, 이하<=와 이상>= 연산자가 있다. 두 값을 비교하여 불리언 값을 반환한다.
피연산자 중 하나가 숫자라면 다른 피연산자를 숫자로 변환한 후 비교하며, 하나가 불리언이라면 숫자로 바꾼 후 비교한다.
피연산자가 모두 문자열이면 서로 대응하는 문자의 문자 코드를 비교하는데, 알파벳의 대문자 전체가 소문자의 문자 코드보다 작기 때문에 대소문자를 전부 통일한 다음 비교하는 등의 주의가 필요하다.
NaN과 비교는 항상 false를 반환한다.
동일 연산자(Equality Operators)
ECMAScript에서는 연산자를 두 벌로 분리해서 '동일' '비동일' 연산자는 비교 전에 타입을 변환하며, '일치' '불일치' 연산자는 타입 변환 없이 비교하는 것으로 정했다.
동일, 비동일(Equal and Not Equal)
동일 연산자는 ==로 표시하며 동일하면 true를 반환한다. 비동일 연산자는 !=로 나타내며 동일하지 않으면 true를 반환한다. 피연산자를 비교하기 전에 변환하며, 이를 종종 '타입 강제(type coercion)'라 부른다.
- null과 undefined는 동일하며, 이 둘은 다른 값으로 변환하여 평가하지 않는다.
-
피연산자 중 하나가 NaN이라면 동일 연산자는 false를, 비동일 연산자는 true를 반환한다. 피연산자가 모두 NaN이라도 동일 연산자는 항상 false를 반환한다. NaN은 NaN과 같지 않다.
- 피연산자가 모두 객체라면 객체끼리 비교하는 것이 아니라 객체에 대한 참조를 비교한다.
일치, 불일치(Identically Equal and Not Identically Equal)
일치 연산자는 ===로 나타내며 피연산자의 타입을 변환하지 않아도 같을때에만 true를 반환한다. 불일치 연산자는 !==로 나타내며 피연산자의 타입을 변환하지 않는 상태에서 일치하지 않을 때에만 true를 반환한다.
null과 undefined는 비슷한 값이므로 null == undefined는 true를, null === undefined는 false를 반환한다.
동일/비동일 연산자보다는 일치/불일치 연산자를 써라!
3항 연산자(Conditional Operator)
variable = boolean_expression ? true_value : false_value;
boolean_expression이 true면 변수 variable에 true_value를 저장하며, false면 false_value가 저장된다.
var max = (num1 > num2) ? num1 : num2;
위 예제에서는 max에 num1과 num2 중 큰 값이 저장된다.
할당 연산자(Assignment Operators)
단순한 할당은 =로 나타내며 단순히 값을 변수에 할당한다. 다음과 같이 복합 할당을 적용하여 줄여 쓸 수 있으나, 성능이 좋아지는 것은 아니다.
num = num + 10;
num += 10; // 복합 할당으로 위와 같다.
쉼표 연산자
쉼표(,) 를 사용해 여러문장을 한 문장으로 합칠 때 쓴다.
3.6 문장(STATEMENTS)
ECMA-262에는 몇가지 문장이 정의되어 있는데 이를 '제어문: flow-control statements'이라 부르기도 한다. ECMAScript 대부분이 문장을 통해 정의되며 일반적으로 키워드와 연결되어 있다.
if 문
if (condition) statement1 else statement2
대부분은 언어에서 가장 많이 쓰이는 제어문이며, 조건에 해당하는 것이 불리언 값이 아니면 Boolean() 함수를 호출하여 불리언 값으로 바꾼다. 실행 코드는 한 줄의 코드라도 코드 블록으로 사용하라. if문을 연이어 쓸 수도 있다.
do-while 문
do {
statement
} while (expression);
do-while 문은 평가전 루프이며, 이는 루프의 종료 조건을 평가하기 전에 루프 본문을 실행한다는 뜻이다. 루프 본문을 적어도 한번은 실행한 후 빠져나가야 하는 상황에서 주로 사용한다.
while 문
while(expression) statement
while문은 평가 후 루프이며, 이는 루프 본문을 실행하기 전에 종료 조건을 평가한다는 뜻이다.
for 문
for (initialization; expression; post-loop-expression) statement
for 문은 평가후 루프인데, 루프에 들어가기 전에 변수를 초기화(initialization)할 수 있으며 루프 후 코드(post-loop-expression)도 지정할 수 있다. 조건 표현식이 true일 때만 실행하므로, while 문과 같이 루프 본문의 코드를 실행하지 않을 때도 있다. 루프 본문을 실행하면 루프 후 코드 역시 실행한다.
while 루프로 실행할 수 없는 일은 for 루프로도 불가능하다. for 루프는 단순히 루프 제어와 관련된 코드를 한곳에 모았을 뿐이다.
for 루프에서 반복을 결정하는 변수(i:iteration)를 반드시 for 루프의 변수 초기화 부분에서 var 키워드로 초기화해야 하는 건 아니다. for 루프 밖에서 초기화해도 된다. 또한, ECMAScript에는 블록 레벨 변수가 존재하지 않아 루프 안에서 변수를 선언하더라도 밖에서 이 변수에 접근할 수 있다.
옵션인 초기화, 조건 표현식, 루프 후 코드를 모두 생략하여 무한루프를 생성할 수도 있으며, 조건 표현식 하나만 명시하면 while 루프처럼 동작한다.
for-in 문
for (property in expression) statement
for-in 문은 엄격한 반복문이다. for-in 문은 객체의 프로퍼티를 나열하는데 사용한다.
문장 레이블
label: statement
문장에 레이블을 붙였다가 나중에 사용가능하며, 일반적으로 중첩된 루프에서 사용한다.
break 문과 continue 문
break 문과 continue 문을 통해 루프 내부의 코드 실행을 세밀히 조절할 수 있다. break 문은 즉시 루프에서 빠져나가 루프 다음 문장을 실행하는 반면, continue 문은 즉시 루프를 빠져나가지만 루프 실행은 지속된다.
break 문과 continue 문을 문자 레이블과 함께 사용하면 코드의 특정 위치로 이동할 수 있다.
루프는 전체적인 성능에 영향을 미치며, 개발자의 의도를 파악하기 어렵게 만들어 디버그나 유지 보수에도 문제가 생길 수 있으니 continue 문은 사용하지 마라.
with 문
with (expression) statement;
with 문은 코드의 스코프를 특정 객체에 고정한다. 원래 의도는 특정 객체를 코드에서 매우 자주 참조할 때 편리하자는 것이었다.
with 문 내부의 변수는 우선 지역 변수로 간주한다. 지역 변수에서 찾을 수 없다면 with 문과 함께 쓴 객체의 프로퍼티 중에서 같은 이름으로 검색하여, 해당 이름을 프로퍼티로 평가한다.
스트릭트 모드에서는 with 문을 금지하며 문법 에러로 간주한다. with 문은 성능에 악영향이 있으며 디버그도 힘들어, 배포할 최종 코드에 with 문을 쓰지 마라.
switch 문
switch (expression) {
case value: statement
break;
case value: statement
break;
case value: statement
break;
case value: statement
break;
default: statement
}
if 문과 관련이 깊으며 C언어 기반의 문법과 매우 비슷하다. switch 문의 각 case는 '표현식이 value와 일치하면 statement를 실행하라'는 의미이다. break 키워드를 코드 실행을 멈추고 switch 문을 빠져나가라는 의미이며, 쓰지 않으면 다음 case를 계속 평가한다. default 키워드는 case 중 value로 평가되는 것이 없을 때 실행할 문장이다. 즉, else 문과 같은 구실을 한다.
case 문마다 break 문을 넣어서 다음 case까지 진행하지 않게 하라. 만약 다음 case 문까지 진행하기 해야 한다면, 주석(/* falls through */ 등)을 달아서 의도적으로 break 문을 생략했으며 실수가 아님을 분명히 한다.
ECMAScript의 switch 문은 모든 데이터 타입에서 동작하는 고유한 특징이 있어 문자열과 객체에서도 사용할 수 있으며, 값이 상수일 필요가 없으며 변수나 표현식도 쓸 수 있다.
switch 문은 일치 연산자(===)로 값을 비교하므로 타입 변환이 일어나지 않는다.
3.7 함수
함수는 문장을 캡슐화하여, 언제 어디서든 실행할 수 있게 하므로 모든 언어의 핵심이다. function 키워드로 정의하며 그 뒤에 매개변수와 함수 본문을 순서대로 쓴다.
function functionName(arg0, arg1, ... , argN) {
statements
}
함수는 꼭 값을 반환하지 않아도 된다. 모든 함수는 return 문 뒤에 반환값을 써서 값을 반환할 수 있다. return 문 뒤의 코드는 결코 실행되지 않는다.
return 문 뒤에 반환값을 쓰지 않아도 되며, 이 때 함수는 return 문을 만나는 즉시 실행을 멈추고 undefined 값을 반환한다.
함수는 항상 값을 반환하거나 항상 반환하지 않게 만들어라. 즉, 반환에 일관성이 있어야 한다. 그렇지 않으면 디버그하기 어려울 수 있다.
스트릭트 모드에서의 함수 제한
- 함수 이름과 매개변수 이름에 eval이나 arguments는 쓸 수 없다.
- 서로 다른 매개변수에 결코 같은 이름을 쓸 수 없다.
매개변수의 이해
ECMAScript 함수는 매개변수 숫자를 따지지 않으며 데이터 타입도 체크하지 않는다.
함수는 arguments라는 객체를 하나 갖는데, 이 객체를 통해 매개변수의 값에 접근할 수 있다. arguments 객체는 각 매개변수를 대괄호 표기법, 첫 번째 매개변수는 arguments[0], 두 번째는 arguments[1] 형태로 접근하며 매개변수 개수를 length 프로퍼티를 통해 알 수 있다는 면에서 배열처럼 동작하기는 하지만 Array의 인스턴스는 아니다.
ECMAScript의 매개변수는 모두 값으로 넘겨야 한다. 매개변수를 참조 형식으로 전달할 수는 없다.
오버로딩 없음
ECMAScript에서는 같은 이름으로 함수를 여러 번 정의하면 마지막 함수가 해당 이름을 소유한다.
함수에 넘긴 매개변수의 타입과 숫자를 체크해서 그에 맞게 반응하는 방법으로 오버로딩을 흉내낼 수 있다.
4장. 변수와 스코프, 메모리
자바스크립트 변수는 느슨한 타입을 취하여, 변수의 이름만 그대로이고, 값과 데이타 타입은 스크립트 실행 중에도 바뀔 수 있다.
4.1 원시 값과 참조 값
ECMAScript의 변수는 원시 값과 참조 값(Primitive And Reference Values) 두가지 타입의 데이터를 저장할 수 있다.
원시 값은 단순한 데이터이며, Undefined, Null, 불리언, 숫자, 문자열이다. 변수에 저장된 실제 값을 조작하기 때문에 '값으로' 접근한다고 한다. 원시 값은 고정된 크기를 가지며 스택 메모리에 저장된다.
참조 값은 여러 값으로 구성되는 객체이자 메모리에 저장된 객체이며, 힙 메모리에 저장된다. 객체를 조작할 때는 객체 자체가 아닌 해당 객에 대한 '참조'를 조작하기 때문에 '참조로 접근한다'고 한다.
동적 프로퍼티
참조 값을 다룰 때는 언제든 프로퍼티와 메서드를 추가하거나 바꾸고 삭제할 수 있으며, 객체를 파괴하거나 프로퍼티를 제거하기 전까지는 접근할 수 있다. 원시 값에는 프로퍼티가 없으며, 참조 값만이 동적으로 프로퍼티를 추가할 수 있다.
값 복사
원시 값은 다른 변수로 복사할 때 저장된 값을 새로 생성한 다음 새로운 변수에 복사하여 두 변수를 완전히 분리한다. 참조 값을 변수에서 다른 변수로 복사하면, 힙(heap: 브라우저가 쓰는 메모리)에 저장된 객체를 가리키는 포인터 값을 복사하므로 두 변수는 정확히 같은 객체를 가리킨다. 따라서 한쪽을 조작하면 다른 쪽에도 반영된다.
매개변수 전달(Argument Passing)
ECMAScript의 함수 매개변수는 모두 값으로 전달된다. 원시 값이던 참조 값이던 함수 매개변수는 지역 변수에 복사되어 실행되며 함수 내에서만 동작하고 외부에 반영되지 않는다. 객체의 경우에도 외부 객체에 프로퍼티만 추가될 뿐이며, 생성된 지역 객체는 함수가 실행을 마치는 즉시 파괴된다. 함수 매개변수는 지역 변수와 다를 것이 없다고 생각하라.
타입 판별
typeof 연산자는 변수가 문자열이나 숫자, 불리언, undefined라면 정확한 타입을 알 수 있다. 값이 객체이거나 null이면 "object"를 반환한다.
instanceof 연산자는 변수가 주어진 참조 타입의 인스턴트일 때 true를 반환하기 때문에, 어떤 타입의 객체인지 판별할 때 도움이 된다. 원시 값은 항상 false를 반환다.
요약: typeof 연산자는 값의 원시 타입을 판별하며 instanceof 연산자는 값의 참조 타입을 판별한다.
4.2 실행 컨텍스트와 스코프
실행 컨텍스트(execution context:EC)는 짧게 '컨텍스트'라고 부르며, 비할 바 없이 중요한 개념이다. 변수나 함수의 실행 컨텍스트는 다른 데이터에 접근할 수 있는지, 어떻게 행동하는지를 규정한다. 각 실행 컨텍스트에는 변수 객체(variable object:VO)가 연결되어 있으며 해당 컨텍스트에서 정의된 모든 변수와 함수는 이 객체에 존재한다. 이 객체를 코드에서 접근할 수는 없지만 이면에서 데이터를 다룰 때 이 객체를 이용한다.
가장 바깥쪽에 존재하는 실행 컨텍스트는 전역 컨텍스트이다. 웹 브라우저에서는 이 컨텍스트를 windows라고 부르며, 전역 변수와 함수는 모두 window 객체의 프로퍼티 및 메서드로 생성도니다.실행 컨텍스트는 포함된 코드가 모두 실행될 때 파괴되는데, 이때 컨텍스트 내부에서 정의된 변수와 함수도 함께 파괴된다. 전역 컨텍스트는 애플리케이션이 종료될 때, 예를 들어 웹 페이지에서 나가거나 브라우저를 닫을 때까지 유지된다.
함수를 호출하면 자체의 실행 컨택스트가 생성된다. 코드 실행이 함수로 들어갈 때마다 함수의 컨텍스트가 컨텍스트 스택에 쌓인다. 함수 실행이 끝나면 해당 컨텍스트를 꺼내고 컨트롤을 이전 컨텍스트에 반환한다.
컨텍스트에서 코드를 실행하면 변수 객체에 '스코프 체인(scope chain)'이 만들어진다. 스코프 체인의 목적은 실행 컨텍스트가 접근할 수 잇는 모든 변수와 함수에 순서를 정의하는 것이다.스코프 체인의 앞쪽은 항상 코드가 실행되는 컨텍스트의 변수 객체이다. 컨텍스트가 함수라면 '활성화 객체(activation object)'를 변수 객체로 사용한다. 활성화 객체는 항상 arguments 변수 단 하나로 시작한다(이 변수는 전역 컨텍스트에는 존재하지 않는다). 변수 객체의 다음 순서는 부모 컨텍스트이며 그 다음에는 부모의 부모 컨텍스트이다. 이런 식으로 스코프 체인의 마지막인 전역 컨텍스트에 도달할 때까지 계속한다. 식별자를 찾을 때는 스코프 체인 순서를 따라가며 검색하며, 찾을 수 없다면 일반적으로 에러가 발생한다.
내부 컨텍스트는 스코프 체인을 통해 외부 컨텍스트 전체에 접근할 수 있지만 외부 컨텍스트는 내부 컨텍스트에 대해 전혀 알 수 없다.
스코프 체인 확장
Scope Chain Augmentation 실행 컨텍스트에는 전역 컨텍스트와 함수 컨텍스트 두 가지 타입만 있지만(eval()은 세 번째 타입이 있다) 스코프 체인을 확장할 수 있는 방법도 있다.
try-catch 문의 catch 블록과 with 문은 모두 스코프 체인 앞 부분에 임시로 변수 객체를 만들며 코드 실행이 끝나면 사라진다. with 문에서는 해당 객체가 스코프 체인에 추가되며 catch 문에서는 에러 객체를 선언하는 변수 객체가 생성된다.
자바스크립트에는 블록 레벨 스코프가 없다
No Block-Level Scopes 블록 레벨 스코프를 지원하는 언어에서는 for문의 초기화 부분에서 선언한 변수가 오직 for문의 컨텍스트 안에서만 존재한다. 그러나 자바스크립트에서는 for문에서 선언한 i변수가 루프 실행이 끝난 후에도 존재한다.
변수 선언
var를 사용해 선언한 변수는 자동으로 가까운 컨텍스트에 추가된다. 함수 내부에서는 함수의 로컬 컨텍스트가 가장 가까운 컨텍스트이며 with 문 내부에서는 함수 컨텍스트가 가장 가까운 컨텍스트이다. 변수를 선언하지 않은 채(함수 내부에서 var 키워드가 생략된 채) 초기화하면 해당 변수는 자동으로 전역 컨텍스트에 추가된다.(외부에서 접근할 수 있게 된다)
항상 변수를 선언한 다음 초기화하라. 스트릭트 모드에서는 변수를 선언하지 않고 초기화하려 하면 에러를 낸다.
식별자 검색
컨텍스트 안에서 식별자를 참조하려면 검색부터 해야 하고, 스코프 체인을 따라 검색을 계속하여 전역 컨텍스트의 변수 객체에 도달할 때까지 계속한다. 식별자를 찾지 못하면 정의되지 않은 것으로 판단한다.
이러한 검색 과정 때문에 식별자가 로컬 컨텍스트에 정의되어 있으면 부모 컨텍스트에 같은 이름의 식별자가 있다 해도 참조할 수 없다.
4.3 가비지 콜렉션
자바스크립트는 실행 환경에서 코드 실행 중에 메모리를 관리한다는 의미에서 가비지 콜렉션(garbage collection) 언어이다. 필요한 메모리를 자동으로 할당하고 더 이상 사용하지 않는 메모리는 자동으로 회수하므로 직접 메모리를 관리하지 않아도 된다.
더 이상 사용하지 않는 변수를 식별하는 기준은 브라우저마다 다르지만 보통 구 가지 방법을 채용한다.
표시하고 지우기(Mark-and-Sweep)
가장 널리 쓰인다.
참조 카운팅(Reference Counting)
널리 쓰이지는 않지만, 이 방식의 주안점은 각 값이 얼마나 참조되었는지 추적한다는 것이다.
참조 카운팅 방법은 넷스케이프 내비게이터 3.0에서 처음 사용되었으나 즉시 순환 참조라는 심각한 문제가 뱔견되었다. 버전 4.0에서 참조 카운팅 방식을 버렸다.
IE 8과 그 이전 버전에서는 BOM과 DOM의 객체들이 참조 카운 방식을 이용하는 C++의 COM 객체로 구현되었다. 이 경우 순환 참조 문제를 해결하려면 해당 객체들에 null을 할당하여 연결을 끊어야 한다. IE 9에서는 BOM과 DOM 객체를 진정한 자바스크립트 객체로 만들어 이 상황을 다소 완화했다.
성능
가비지 컬렉터는 주기적으로 실행되며 메모리 내에 할당된 변수가 많다면 상당한 비용이 드는 작업이므로 실행 타이밍이 중요하다. IE는 가비지 컬렉터를 할당 숫자 기준으로 너무 자주 실행하여 성능 문제를 일으키는 것으로 악명이 높다.
메모리 관리
일반적으로는 가비지 컬렉션 환경에서는 개발자가 메모리 관리를 신경쓰지 않아도 되지만, 웹 브라우저에서 사용할 수 있는 메모리는 일반적인 테스크톱 애플리케이션의 가용 메모리에 비해 매우 적다.
페이지 성능을 올리기 위해 메모리 사용을 최적화하는 가장 좋은 방법은 코드 실행에 필요한 데이터만 유지하는 것이다. 필요없어진 데이터에는 null을 할당하여 참조를 제거(dereference)하는 편이 좋다. 수동으로 참조 제거해야 할 대상은 주로 전역 변수 및 전역 객체의 프로퍼티이다. 지역 변수는 컨텍스트를 빠져나가는 순간 자동으로 참조가 제거된다.
값의 컨텍스트를 없애기 위해서 변수에서 참조를 제거하면 다음 가비지 콜렉션을 실행할 때 해당 메모리가 회수된다.
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()를 호출할 때마다 자동으로 값이 설정된다.
패턴의 한계
- 테스트의 처음과 마지막에 일치하는 \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 객체를 쓸 수 있는데 문자열을 정규 표현식으로 간주하지는 않는다. 두 번째 매개변수(옵션)는 반환받을 배열의 크기를 지정하는 숫자이다.
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의 아크탄젠트 |
6장. 객체 지향 프로그래밍
객체지향 언어는 일반적으로 클래스를 통해 같은 프로퍼티와 메서드를 가지는 객체를 여러 개 만든다는 특징이 있지만, ECMAScript에는 클래스라는 개념이 없다.
ECMA-262의 객체 정의는 "프로퍼티의 순서 없는 컬렉션이며 각 프로퍼티는 원시값이나 객체, 함수를 포함한다"이며, 특별한 순서가 없는 값의 배열이란 의미이다. ECMAScript 객체는 해시 테이블이며, 이름-값 쌍의 그룹이며 각 값은 데이터나 함수가 될 수 있다.
모든 객체는 참조 타입을 바탕으로 생성된다.
6.1 객체에 대한 이해
객체를 만드는 가장 단순한 방법은 Object의 인스턴스를 만들고 프로퍼티와 메서드를 추가하는 것이다.
var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function() {
alert(this.name);
};
초기에는 위의 패턴을 자주 사용했으나 몇 년이 지나면서 객체 리터럴 패턴을 더 많이 쓴다:
var person = {
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName: function() {
alert(this.name);
}
};
프로퍼티 타입
ECMA-262 5판에서는 프로퍼티의 특징을 내부적으로만 유효한 속성에 따라 설명하며, 속성이 자바스크립트 엔진 내부에서 구현하므로 자바스크립트에서 직접적으로 접근할 수 없다. [[]]로 감싸서 내부 속성임을 나타낸다.
데이터 프로퍼티
- [[Configurable]] - 삭제/변경/변환. 기본값은 true.
- [[Enumerable]] - for-in 루프에서 반환. 기본값은 true.
- [[Writable]] - 값 변경. 기본값은 true.
- [[Value]] - 실제 데이터 값. 기본값은 undefined
Object.defineProperty() 메서드를 써서 속성 값을 바꿀 수 있다. Object.defineProperty()를 여러 번 호출할 수 있지만, 일단 Configurable을 false로 지정하면 제한이 생긴다. 프로퍼티 값을 따로 명시하지 않는다면 기본 값은 false이다.
접근자 프로퍼티
- [[Configurable]]
- [[Enumerable]]
- [[Get]] - 프로퍼티를 읽을 때 호출할 함수. 기본 값은 undefined.
- [[Set]] - 프로퍼티를 바꿀 때 호출할 함수. 기본 값은 undefined.
접근자 포로퍼티는 일반적으로 프로퍼티의 값을 바꿨을 때 해당 프로퍼티만 바뀌는 게 아니라 부수적인 절차가 필요한 경우에 사용한다.
getter 함수만 지정하면 해당 프로퍼티는 읽기 전용이 되고, 수정 시도는 모두 무시되거나 스트릭트 모드에선 에러가 발생한다. setter만 지정된 프로퍼티는 읽으려 하면 undefined를 반환하며 스트릭트 모드에선 에러가 발생한다.
다중 프로퍼티 정의
프로퍼티 속성 읽기
Object.getOwnPropertyDescriptor() 메서드로 프로퍼티의 서술자 프로퍼티를 읽을 수 있다.
자바스크립트 객체 상세
자바스크립트의 - 가장 자주 사용되고 가장 기본적인 - 핵심 데이터 타입은 객체 데이터 타입이다. 자바스크립트는 한 개의 복잡한 데이터 타입인 객체 데이터 타입이 있으며, 5개의 단순한 데이터 타입이 있다: 숫자, 문자열, 불린, undefined, null. 단순한 (원시적인) 데이터 타입은 불변 즉, 변경할 수 없으나, 객체는 변경할 수 있다.
객체란 무엇인가
객체는 이름-값 쌍으로 저장되는 원시 데이터(때때로 참조 데이터 타입)의 순서 없는 목록이다. 목록의 각 항목은 프로퍼티라고 불리며(함수는 메서드로 불린다), 각 프로퍼티의 이름은 유일해야 하고, 하나의 문자열 또는 숫자가 될 수 있다.
간단한 객체:
var myFirstObject = {firstName: "Richard", favoriteAuthor: "Conrad"};
반복하면: 객체를 이름-값 쌍으로 저장된 목록 안에 항목과 프로퍼티를 포함하는 목록으로 생각하라. 위의 예제의 프로퍼티 이름은 firstName과 favoriteAuthor이다. 각각의 값은 "Richard"와 "Conrad"이다.
프로퍼티 이름은 문자열이나 숫자가 될 수 있으나, 프로퍼티 이름이 하나의 숫자라면 대괄호 표기법으로 접근되어야 한다. 대괄호 표기법은 나중에 더 자세히. 프로퍼티 이름이 숫자로 된 객체의 예제:
var ageGroup = {30: "Children", 100:"Very Old"};
console.log(ageGroup.30) // 에러가 발생한다.
// "Children" 값을 얻기 위해서는 프로퍼티 30의 값은 다음과 같이 접근해야 한다.
console.log(ageGroup["30"]); // Children
//프로퍼티 이름에 숫자를 사용하길 피하는 것이 가장 좋다.
자바스크립트 개발자로서 여러분은 주로 데이터를 저장하고 여러분 자신의 커스텀 메서드와 함수를 만들기 위해, 매우 자주 객체 데이터 타입을 사용할 것이다.
참조 데이터 타입과 원시 데이터 타입
참조 데이터 타입과 원시 데이터 타입의 주요 차이점 중 하나는 참조 데이터 타입의 값은 변수에 직접 저장되지 않고, 참조로 저장되며, 원시 데이터 타입은 값으로 저장된다는 것이다. 예로:
// 원시 데이터 타입 문자열은 값으로 저장된다.
var person = "Kobe";
var anotherPerson = person; // anotherPerson = person의 값
person = "Bryant"; // person의 값이 바뀌었다.
console.log(anotherPerson); // Kobe
console.log(person); // Bryant
"Bryant"로 person을 변경했을지라도, anotherPerson 변수는 person이 가졌던 값을 그대로 유지하는 것을 주목할 필요가 있다.
위에서 예시한 값으로 저장되는 원시 데이터와 참조로 저장되는 객체를 비교하라:
var person = {name: "Kobe"};
var anotherPerson = person;
person.name = "Bryant";
console.log(anotherPerson.name); // Bryant
console.log(person.name); // Bryant
이 예제에선, person 객체를 anotherPerson으로 복사하였다. 그러나 person의 값이 실제 값이 아닌 참조로 저장되었기 때문에 person.name 프로퍼티를 "Bryant"로 변경하면 anotherPerson이 변경된다. person 프로퍼티 값의 실제 사본을 저장하지 않고 참조만 하기 때문이다.
객체 데이터 프로퍼티는 속성(attribute)이 있다
각 데이터 프로퍼티(데이터를 저장하는 객체 프로퍼티)는 이름-값 쌍과 (기본값이 true인) 세 가지 속성이 있다:
-
Configurable Attribute: 프로퍼티가 삭제되거나 변경될 수 있는지를 지정한다.
-
Enumerable: 프로퍼티가 for/in 루프에서 반환될 수 있는지를 지정한다.
-
Writable: 프로퍼티가 변경될 수 있는지를 지정한다.
ECMAScript 5는 위의 데이터 프로퍼티와 함께 접근자 프로퍼티를 명시하고 있음을 주목하라. 접근자 프로퍼티는 함수(getter와 setter)이다. 2월 15일에 이미 예정된 포스트에서 ECMAScript 5를 더 배울 것이다.
객체 만들기
객체를 만드는 두 가지 일반적인 방법이 있다:
1. 객체 리터럴
객체를 만드는 가장 일반적이고 가장 쉬운 방법은 객체 리터럴로 만드는 것이다:
// 객체 리터럴을 사용해서 빈 객체를 초기화시킨다.
var myBooks = {};
// 객체 리터럴을 사용해서 4개의 항목을 가진 객체
var mango = {
color: "yellow",
shape: "round",
sweetness: 8,
howSweetAmI: function () {
console.log("Hmm Hmm Good");
}
}
2. 객체 생성자(constructor)
두 번째 가장 일반적인 방법은 객체 생성자로 만드는 것이다. 생성자는 새 객체를 초기화하는 함수이며, 생성자를 호출하기 위해 새 키워드를 사용한다.
var mango = new Object ();
mango.color = "yellow";
mango.shape= "round";
mango.sweetness = 8;
mango.howSweetAmI = function () {
console.log("Hmm Hmm Good");
}
객체의 프로퍼티 이름으로 "for"와 같은 몇몇 예약어를 사용할 수 있지만, 피하는 것이 현명하다.
객체는 숫자, 배열과 심지어 다른 객체를 포함하는 모든 데이터 타입을 수용할 수 있다.
객체를 만드는 실용적인 패턴
데이터를 저장하기 위해 여러분의 앱에서 한 번만 사용될 수도 있는 간단한 객체를 위해서, 위의 두 가지 방법으로 객체를 만드는데 충분하다.
과일과 각 과일에 대한 상세를 보여주는 앱을 상상해보라. 모든 과일은 다음과 같은 프로퍼티를 가진다: color, shape, sweetness, cost와 showName 함수. 새로운 과일 객체를 만들려고 할 때마다 다음 코드를 적는 것은 매우 지루하고 비생산적이다.
var mangoFruit = {
color: "yellow",
sweetness: 8,
fruitName: "Mango",
nativeToLand: ["South America", "Central America"],
showName: function () {
console.log("This is " + this.fruitName);
},
nativeTo: function () {
this.nativeToLand.forEach(function (eachCountry) {
console.log("Grown in:" + eachCountry);
});
}
}
10개의 과일이 있다면, 같은 코드를 10번 추가해야 할 것이다. nativeTo 함수를 변경해야 한다면? 10개의 다른 장소에서 변경해야 한다. Now extrapolate this to adding objects for members on a website and suddenly you realized the manner in which we have created objects so far is not ideal objects that will have instances, particularly when developing large applications.
이런 반복적인 문제를 해결하기 위해, 소프트웨어 엔지니어들은 앱을 더 효율적이고 능률적으로 개발하기 위해 패턴(반복적이고 일상적인 작업을 위한 해법)을 발명했다.
객체를 만들기 위한 두 가지 일반적인 패턴이 있다. 여러분이 자바스크립트 제대로 배우기 과정을 하고 있다면 코드 아카데미의 레슨에서 첫 번째 패턴이 자주 사용되는 것을 본 적이 있을 것이다:
1. 객체 만들기 생성자 패턴
function Fruit (theColor, theSweetness, theFruitName, theNativeToLand) {
this.color = theColor;
this.sweetness = theSweetness;
this.fruitName = theFruitName;
this.nativeToLand = theNativeToLand;
this.showName = function () {
console.log("This is a " + this.fruitName);
}
this.nativeTo = function () {
this.nativeToLand.forEach(function (eachCountry) {
console.log("Grown in:" + eachCountry);
});
}
}
이 패턴을 적절히 사용하여, 모든 종류의 과일을 만드는 것은 매우 쉽다. 이를테면:
var mangoFruit = new Fruit ("Yellow", 8, "Mango", ["South America", "Central America", "West Africa"]);
mangoFruit.showName(); // Mango
mangoFruit.nativeTo();
// Grown in:South America
// Grown in:Central America
// Grown in:West Africa
var pineappleFruit = new Fruit ("Brown", 5, "Pineapple", ["United States"]);
pineappleFruit.showName(); // Pineapple
fruitName 함수를 변경해야 한다면, 한 곳에서만 하면 된다. 패턴은 한 개의 과일 함수와 상속을 이용해서 모든 과일의 모든 기능성과 특징을 캡슐화한다.
주의사항:
- 상속 프로퍼티는 객체의 프로토타입 프로퍼티에 정의된다. 예로:
someObject.prototype.firstName = "rich";
- 자신의 프로퍼티는 객체 그 자체에 직접 정의된다, 예로:
// 먼저 객체를 만든다:
var aMango = new Fruit ();
// 이제 aMango 객체에 직접 mangoSpice 프로퍼티를 정의한다.
// aMango 객체에 직접 mangoSpice 프로퍼티를 정의했기 때문에 상속된 프로퍼티가 아닌 aMango 자신의 프로퍼티이다.
aMango.mangoSpice = "some value";
- 객체의 프로퍼티에 접근하기 위해서, object.property를 사용한다. 예로:
console.log(aMango.mangoSpice); // "some value"
- 객체의 매서드를 적용하려면(invoke), object.method()를 사용한다. 예로:
// 먼저 메서드를 추가한다.
aMango.printStuff = function () {return "Printing";}
// 이제 printStuff 메서드를 적용할 수 있다:
aMango.printStuff ();
2. 객체 만들기 프로토타입 패턴
function Fruit () {
}
Fruit.prototype.color = "Yellow";
Fruit.prototype.sweetness = 7;
Fruit.prototype.fruitName = "Generic Fruit";
Fruit.prototype.nativeToLand = "USA";
Fruit.prototype.showName = function () {
console.log("This is a " + this.fruitName);
}
Fruit.prototype.nativeTo = function () {
console.log("Grown in:" + this.nativeToLand);
}
그리고 아래는 이 프로토타입 패턴에 Fruit () 생성자를 호출하는 방법이다:
var mangoFruit = new Fruit ();
mangoFruit.showName(); //
mangoFruit.nativeTo();
// This is a Generic Fruit
// Grown in:USA
추가 읽기
이 두 가지 패턴의 완전한 논의와 각각의 동작법과 단점에 대한 철저한 설명을 위해, Professional JavaScript for Web Developers의 6장을 읽어라. 여러분은 자카스가 어떤 것을 최고로 추천하는지 배울 수 있을 것이다.(힌트: 위의 두 가지 다 아니다.)
객체 프로퍼티 접근하는 법
객체의 프로퍼티에 접근하는 두 가지 주요 방법은 점 표기법과 대괄호 표기법이다.
1. 점 표기법
// 지금까지의 예제에서 점 표기법을 사용해왔다. 여기 또 하나의 예제가 있다:
var book = {title: "Ways to Go", pages: 280, bookMark1:"Page 20"};
// 점 표기법을 사용해서 book 객체의 프로퍼티에 접근하려면, 이렇게 한다:
console.log (book.title); // Ways to Go
console.log (book.pages); // 280
2. 대괄호 표기법
// 대괄호 표기법으로 book 객체의 프로퍼티에 접근하려면, 이렇게 한다:
console.log (book["title"]); //Ways to Go
console.log (book["pages"]); // 280
//혹은, 변수로 프로퍼티 이름을 가질 수도 있다:
var bookTitle = "title";
console.log (book[bookTitle]); // Ways to Go
console.log (book["bookMark" + 1]); // Page 20
존재하지 않는 객체의 프로퍼티에 접근하면 undefined 결과가 나올 것이다.
고유와 상속 프로퍼티
객체는 상속 프로퍼티와 고유 프로퍼티를 가진다. 고유 프로퍼티는 객체에 정의되는 프로퍼티이고, 상속 프로퍼티는 프로토타입 객체로부터 상속된다.
객체에 프로퍼티(상속이던 고유이던)가 존재하는지를 알아내려면 연산자를 사용한다:
// schoolName이란 프로퍼티를 가진 새로운 school 객체를 만든다.
var school = {schoolName:"MIT"};
// schoolName은 school 객체의 고유 프로퍼티이므로 true를 출력한다.
console.log("schoolName" in school); // true
// school 객체에 schoolType 프로퍼티가 정의되지 않았고, Object.prototype 프로토타입 객체로부터 schoolType 프로퍼티를 상속하지 않았기 때문에 false를 출력한다.
console.log("schoolType" in school); // false
// school 객체가 Object.prototype으로부터 toString 메서드를 상속받았기 때문에 true를 출력한다.
console.log("toString" in school); // true
hasOwnProperty
객체가 특정 프로퍼티를 고유 프로퍼티 중 하나로 가졌는지 알아내려면 hasOwnProperty 메서드를 사용한다. 이 메서드는 객체를 하나하나 열거하거나 상속이 아닌 고유 프로퍼티만을 원할 때 매우 유용하다.
// schoolName 프로퍼티를 갖는 새로운 school 객체를 만든다.
var school = {schoolName:"MIT"};
// schoolName이 school 객체의 고유 프로퍼티이므로 true를 출력한다.
console.log(school.hasOwnProperty ("schoolName")); // true
// school 객체가 toString 메서드는 Object.prototype으로 상속되었으며, toString이 school 객체의 고유 프로퍼티가 아니므로 false를 출력한다.
console.log(school.hasOwnProperty ("toString")); // false
객체 프로퍼티 접근하고 열거하기
열거할 수 있는 (고유와 상속) 프로퍼티를 접근하기 위해서는 for/in 루프나 일반 for 루프를 사용한다.
// schoolName, schoolAccredited, schoolLocation의 세 가지 고유 프로퍼티를 갖는 새로운 school 객체를 만든다.
var school = {schoolName:"MIT", schoolAccredited: true, schoolLocation:"Massachusetts"};
//school 객체의 프로퍼티를 접근하기 위해 for/in 루프를 사용하라.
for (var eachItem in school) {
console.log(eachItem); // schoolName, schoolAccredited, schoolLocation를 출력한다.
}
상속 프로퍼티 접근하기
Object.prototype에서 상속된 프로퍼티는 열거할 수 없으며, for/in 루프가 그것들을 보여주지 않는다. 그러나 열거할 수 있는 상속 프로퍼티는 for/in 루프 반복으로 보인다. 예로:
// school 객체의 프로퍼티를 접근하기 위해서 for/in 루프를 사용하라.
for (var eachItem in school) {
console.log(eachItem); // schoolName, schoolAccredited, schoolLocation를 출력한다.
}
// school 객체가 상속할 새로운 HigherLearning 함수를 만든다.
/* 사이드 노트: 날카로운 독자인 윌슨이 밑의 댓글에 정확하게 지적하였듯이, educationLevel 프로퍼티는 HigherLearning 생성자를 사용한 객체에 실제로 상속되지 않았다; 대신, educationLevel 프로퍼티는 HigherLearning 생성자를 사용한 각 객체에 새로운 프로퍼티로서 만들어졌다. 프로퍼티가 상속되지 않은 이유는 프로퍼티를 정의하기 위해 "this" 키워드를 사용하기 때문이다.
*/
function HigherLearning () {
this.educationLevel = "University";
}
// HigherLearning 생성자로 상속한다.
var school = new HigherLearning ();
school.schoolName = "MIT";
school.schoolAccredited = true;
school.schoolLocation = "Massachusetts";
// school 객체의 프로퍼티를 접근하기 위해 for/in 루프를 사용하라.
for (var eachItem in school) {
console.log(eachItem); // educationLevel, schoolName, schoolAccredited, schoolLocation를 출력한다.
}
마지막 예제에서, HigherLearning 함수로 정의된 educationLevel 프로퍼티가 school의 프로퍼티 중 하나로 열거된 것을 주목하라. educationLevel은 고유 프로퍼티가 아니며, 상속되었다.
객체의 프로토타입 속성과 프로토타입 프로퍼티
객체의 프로퍼티 삭제하기
객체로부터 프로퍼티를 삭제하려면, delete 연산자를 사용한다. 상속된 프로퍼티는 지울 수 없으며, configurable 속성으로 설정된 프로퍼티도 지울 수 없다. (프로퍼티가 정의되었던 곳인) 프로토타입 객체에서 상속된 프로퍼티를 지워야 한다. 또한, (var 키워드로 선언되었던) 전역 객체의 프로퍼티는 지울 수 없다.
delete 연산자는 삭제가 성공적이면 true를 반환한다. 그리고 놀랍게도 삭제할 프로퍼티가 존재하지 않거나 프로퍼티가 지워질 수 없어도(non-configurable이나 객체에 의해 소유되지 않은 등) 역시 true를 반환한다.
이러한 예를 설명하면:
var christmasList = {mike:"Book", jason:"sweater" }
delete christmasList.mike; // mike 프로퍼티를 지운다.
for (var people in christmasList) {
console.log(people);
}
// jason만 출력한다.
// mike 프로퍼티는 지워졌다.
delete christmasList.toString; // true를 반환한다, 그러나 toString은 상속된 메서드이기 때문에 지워지지 않는다.
// 여기서 toString 메서드를 호출하면 잘 동작한다-지워지지 않았다.
christmasList.toString(); //"[object Object]"
// 프로퍼티가 그 인스턴스의 고유 프로퍼티라면 인스턴스의 프로퍼티를 지울 수 있다. 예로, educationLevel 프로퍼티는 인스턴스에 정의되었기 때문에 school 객체에서 educationLevel 프로퍼티를 지울 수 있다: HigherLearning 함수를 선언할 때 프로퍼티를 정의하기 위해 "this" 키워드를 사용하였다. HigherLearning 함수의 프로토타입에서 educationLevel 프로퍼티를 정의하지 않았다.
console.log(school.hasOwnProperty("educationLevel")); true
// educationLevel은 school에서 고유 프로퍼티이며, 지울 수 있다.
delete school.educationLevel; true
// educationLevel 프로퍼티는 school 인스턴스로부터 지워졌다.
console.log(school.educationLevel); undefined
// 그러나 HigherLearning 함수에 educationLevel 프로퍼티는 아직 존재한다.
var newSchool = new HigherLearning ();
console.log(newSchool.educationLevel); // University
// 다음의 educationLevel2 프로퍼티처럼 HigherLearning 함수의 프로토타입으로 정의했다면:
HigherLearning.prototype.educationLevel2 = "University 2";
// HigherLearning 인스턴스의 educationLevel2 프로퍼티는 고유 프로퍼티가 아니다.
// educationLevel2 프로퍼티는 school 인스턴스의 고유 프로퍼티가 아니다.
console.log(school.hasOwnProperty("educationLevel2")); false
console.log(school.educationLevel2); // University 2
// 상속된 educationLevel2 프로퍼티를 지워보자.
delete school.educationLevel2; true (앞에서 언급하였듯이 항상 true를 반환한다)
// 상속된 educationLevel2 프로퍼티는 지워지지 않는다.
console.log(school.educationLevel2); University 2
Serialize and Deserialize Objects
HTTP를 통해 객체를 전송하거나 문자열로 변환하기 위해서, 시리얼라이즈할(문자열로 변환할) 필요가 있을 것이다; JSON를 사용할 수 있다. 객체를 시리얼라이즈하기 하려면 함수를 문자열화하라. ECMAScript 5 전에는 JSON을 얻기 위해 (더글러스 크락포드의) 인기 있는 json2 라이브러리를 사용해야 했다는 것을 주목하라. 함수를 문자열화하는 것은 이제 ECMAScript 5에서는 표준이다.
객체를 디시리얼라이즈하기(문자열에서 객체로 변환하기) 위해서는, 같은 json2 라이브러리에서 JSON.parse 함수를 사용한다. 이 함수도 ECMAScript 5에서 표준이 되었다.
JSON.stringify 예제:
var christmasList = {mike:"Book", jason:"sweater", chelsea:"iPad" }
JSON.stringify (christmasList);
// 다음 문자열을 출력한다:
// "{"mike":"Book","jason":"sweater","chels":"iPad"}"
// 문자열화된 객체를 포매팅과 함께 출력하려면, 패러미터로 "null"과 "4"를 추가한다:
JSON.stringify (christmasList, null, 4);
/*
"{
"mike": "Book",
"jason": "sweater",
"chels": "iPad"
}"
*/
// JSON.parse 예제 \\
// 다음은 JSON 문자열이다, 그래서 (christmasListStr.mike와 같이) 점 표기법으로 접근할 수 없다.
var christmasListStr = '{"mike":"Book","jason":"sweater","chels":"iPad"}';
// 객체로 변환하자.
var christmasListObj = JSON.parse (christmasListStr);
// 이제 객체이므로 점 표기법을 사용한다.
console.log(christmasListObj.mike); // Book
객체를 다루기 위한 ECMAScript 5 추가사항을 포함하여, 자바스크립트 객체를 좀 더 상세히 커버하기 위해서는 JavaScript: The Definitive Guide 6th Edition의 6장을 읽어라.