키 기반 컬렉션 - JavaScript

키 기반 컬렉션
========

* « 이전
* 다음 »

이번 장에서는 입력된 키값을 기준으로 정렬되는 데이터의 집합(자료 구조)에 대해 소개 할 것이다. Map과 Set은 입력된 순서대로 반복적으로 접근 가능한 요소들을 포함하고 있다.

Maps
----

### `Map` 객체

ECMAScript 6에서 값들을 매핑하기 위한 새로운 데이터 구조를 소개 하고 있다. 그중 하나인 Map객체는 간단한 키와 값을 서로 연결(매핑)시켜 저장하며 저장된 순서대로 각 요소들을 반복적으로 접근할 수 있도록 한다.

다음 코드는 Map이 제공하는 기본적인 기능들을 보여 주고 있다. 더 많은 예제와 모든 기능에 대한 API를 볼려면 `Map` 페이지를 참고하면 된다. Map객체에 저장되어 있는 각 요소들을 [키, 값] 형태의 배열로 반복적으로 반환해주는 `for...of` 를 사용할 수 있다.

```js
var sayings = new Map();
sayings.set("dog", "woof");
sayings.set("cat", "meow");
sayings.set("elephant", "toot");
sayings.size; // 3
sayings.get("fox"); // undefined
sayings.has("bird"); // false
sayings.delete("dog");

for (var [key, value] of sayings) {
  console.log(key + " goes " + value);
}
// "cat goes meow"
// "elephant goes toot"
```

### Object와 Map 비교

전통적으로 objects 는 문자열을 값에 매핑하는 데 사용되었다. Object는 키를 값으로 설정하고, 값을 검색하고, 키를 삭제하고, 키에 저장된 내용을 검색 할 수 있게 만들어준다. 그러나 Map 객체는 더 나은 맵이 되도록 하는 몇 가지 장점을 가지고 있다.

* Object의 키는 `Strings`이며, Map의 키는 모든 값을 가질 수 있다.
* Object는 크기를 수동으로 추적해야하지만, Map은 크기를 쉽게 얻을 수 있다.
* Map은 삽입된 순서대로 반복된다.
* 객체(Object)에는 prototype이 있어 Map에 기본 키들이 있다. (이것은 map = Object.create(null) 를 사용하여 우회할 수 있다. )

Object 혹은 Map중에 어느 것을 사용할지를 결정하는데 도움을 줄 두가지 팁이 있다:

* 실행 시까지 키를 알수 없고, 모든 키가 동일한 type이며 모든 값들이 동일한 type일 경우에는 objects를 대신해서 map을 사용해라.
* 각 개별 요소에 대해 적용해야 하는 로직이 있을 경우에는 objects를 사용해라.
### `WeakMap` 객체

`WeakMap`객체는 object만을 키로 허용하고 값은 임의의 값을 허용하는 키/값 형태의 요소의 집합이다. 키가 가지고 있는 객체에 대한 참조는 객체에 대한 참조가 더이상 존재하지 않을 경우 garbage collection(GC)의 수거 대상이 되는 약한 참조를 의미한다. `WeakMap` API는 Map API와 동일하다.

단 한가지 Map객체와 다른 점은 WeakMap의 키들은 열거형이 아니라는 점이다. (즉, 키 목록을 제공해 주는 메서드가 없다는 것이다.) 만약에 키 목록을 제공한다면 garbage collection의 상태, 결과에 따라 키 목록이 변하게 될 것이다. 이는 비 결정성을 야기한다.

WeakMap에 대한 더 많은 정보와 예제 코드 그리고 "왜 WeakMap을 사용하지?"를 보고 싶다면 `WeakMap` 페이지를 참고하면 된다.

`WeakMap`를 사용하는 한가지 경우는 객체의 사적인 정보를 저장하기 위해서 이거나 상세 구현 내용을 숨기기 위한 것이다. 다음의 예제는 Nick Fitzgerald의 블로그 글- "Hiding Implementation Details with ECMAScript 6 WeakMaps"-에서 가져온 것이다. 객체가 가지고 있는 그렇지만 외부에 공개되지 않는(private) 데이터와 메서드들은 `WeakMap`객체인 `privates`에 저장이 된다. 인스턴스를 통해 접근 가능한 모든 것들과 prototype은 public이고 다른 것들은 외부에서는 접근이 불가하다 그 이유는 `privates`는 모듈로부터 내보내기(export)가 되지 않기 때문이다.

```js
const privates = new WeakMap();

function Public() {
  const me = {
    // Private data goes here
  };
  privates.set(this, me);
}

Public.prototype.method = function () {
  const me = privates.get(this);
  // Do stuff with private data in `me`...
};

module.exports = Public;
```

Sets
----

### `Set` object

`Set` (en-US)객체는 값들의 집합이다. 입력된 순서에따라 저장된 요소를 반복처리할 수 있다. Set은 중복된 값을 허용하지 않는다. 따라서 특정 값은 Set내에서 하나만 존재 하게 된다.

아래의 코드는 Set의 기본적인 동작들을 보여 준다. 더 많은 예제와 모든 API는 `Set` (en-US) 참조 페이지를 보면 된다.

```js
var mySet = new Set();
mySet.add(1);
mySet.add("some text");
mySet.add("foo");

mySet.has(1); // true
mySet.delete("foo");
mySet.size; // 2

for (let item of mySet) console.log(item);
// 1
// "some text"
```

### 배열과 Set의 상호 변환

`Array.from` 혹은 spread operator를 통해 Set객체를 가지고 `Array`을 생성할 수 있다. 또한 Set 생성자는 배열을 인자로 받을 수 있고 해당 배열을 Set객체의 요소로 저장한다. 한번 더 상기시키면 Set객체는 중복된 값을 저장하지 않기때문에 주어진 배열내의 중복된 요소들을 제거되어 Set으로 변환된다.

```js
Array.from(mySet);
[...mySet2];

mySet2 = new Set([1, 2, 3, 4]);
```

### 배열과 Set비교

일반적으로 Javascript에선 배열에 특정 요소의 집합을 저장한다. 하지만 Set객체는 몇가지 이점을 제공한다.

* `indexOf`메서드를 사용하여 배열내에 특정 요소가 존재하는지 확인하는 것은 느리다.
* 배열에선 해당 요소를 배열에서 잘라내야 하는 반면 Set객체는 요소의 값으로 해당 요소를 삭제하는 기능 제공한다.
* `NaN`은 배열에서 indexOf메서드로 찾을 수 없다.
* Set객체는 값의 유일성을 보장하기 때문에 직접 요소의 중복성을 확인할 필요가 없다.
### `WeakSet` 객체

`WeakSet` objects are collections of objects. An object in the `WeakSet` may only occur once; it is unique in the `WeakSet`'s collection and objects are not enumerable.

`WeakSet`객체는 객체를 저장하는 일종의 집합이다. `WeakSet내의 중복된 객체는 없으며 WeakSet내의 요소를 열거할 수는 없다.`

`Set` (en-US)과 가장 큰 차이점은 다음과 같다.

* `Set과는 다르게 WeakSet은 객체의 집합이며 객체만 저장할 수 있다. 특정 type의 값을 저장할 수는 없다.`
* WeakSet은 약한 참조를 가진다. WeakSet내의 객체에 대한 참조는 약하게 연결이 되어 있다. WeakSet내에 저장되어 있는 객체에 대한 참조가 없게되면 garbage collection 대상이되어 수거 된다. 따라서 현재 저장되어 있는 객체에 대한 목록은 없으며 WeakSet은 열거형이 아니다.

`WeakSet객체의 사용 사례는 제한되어 있다. 메모리 누수가 발생되지 않기때문에 안전하게 DOM요소를 키로 저장할 수 있고 예를 들면 추적을 위해 DOM요소들을 WeakSet에 저장할 수 있다.`

`Map과 Set의 키와 값의 동치성`
---------------------

Map객체의 key에 대한 동치성 비교와 Set객체의 값에 대한 동치성 비교 모두 "same-value-zero algorithm"에 근거한다.

* 동치성 비교는 `=== 비교 연산과 같이 작동한다.`
* -0과 +0은 같다고 간주한다.
* `NaN`는 자기 자신하고 같다고 간주한다. (===와는 반대로)
* « 이전
* 다음 »


숫자와 날짜 - JavaScript

숫자와 날짜
======

* « 이전
* 다음 »

이 장에서는 JavaScript에서 숫자와 날짜를 사용하기 위한 개념과 객체, 함수에 대해 소개합니다. 그리고 숫자를 10진법, 2진법, 16진법 등의 다양한 형태로 표현하는 방법과 더불어 `Math` 객체를 사용해 다양한 수학 연산을 수행하는 방법을 알 수 있습니다.

숫자
--

JavaScript에서 모든 숫자는 double-precision 64-bit binary format IEEE 754 (즉, ±2^−1022 과 ±2^+1023 또는 대략 ±10^−308 to ±10^+308 사이의 숫자이며 53bits의 수치정밀도 )로 구현되어 있습니다. ±2^53 - 1까지의 정수 값을 정확하게 나타낼 수 있습니다.

여기 부동 소수점 숫자를 나타낼 수 있으며, 숫자 형식은 세 개의 상징적인 값: `+``Infinity`, `-``Infinity`, and `NaN` (숫자가 아닌 값)을 갖습니다.

JavaScript에 최근 추가 된 것은 `BigInt`로, 매우 큰 정수를 나타낼 수 있습니다. `BigInt`를사용할땐 다음을 주의해야 합니다. 예를 들면, `BigInt`와 `Number` 값을 같은 연산으로 혼합하고 일치시킬 수는 없으며 `Math` 객체를 `BigInt`값과 함께 사용할 수 없습니다.

JavaScript에서 다른 기본형과 문맥에 대한 내용은 JavaScript data types and structures를 참조하세요.

여러분은 숫자 리터럴의 네 가지 유형을 사용할 수 있습니다: 10진수, 2진수, 8진수, 16진수

### 10진수

```js
1234567890;
42;

// 앞에 0이 붙은 숫자를 조심하세요:

0888; // 10진수 888로 해석됩니다.
0777; // non-strict mode에서 10진수 511로 해석됩니다.
```

10진수 리터럴도 영(`0`)으로 시작될 수 있다는 점에 유의하세요. 그러나 만약 영`0` 다음 숫자가 8보다 작으면, 그 숫자는 8진법으로 해석됩니다.

### 2진수

2진수 구문은 앞에 오는 0과 소문자 또는 대문자 라틴 문자 "B"(0B 또는 0b)를 사용합니다. 0b 다음의 숫자가 0 또는 1이 아니면 다음의 SyntaxError가 발생합니다. "0b 이후에 누락 된 2 진수"("Missing binary digits after 0b")입니다.

```js
var FLT\_SIGNBIT = 0b10000000000000000000000000000000; // 2147483648
var FLT\_EXPONENT = 0b01111111100000000000000000000000; // 2139095040
var FLT\_MANTISSA = 0b00000000011111111111111111111111; // 8388607
```

### 8진수

8 진수 구문은 앞에 0을 사용합니다. `0` 이후의 숫자가 0에서 7까지 범위 밖에 있는 경우, 숫자는 10진수로 해석됩니다.

```js
var n = 0755; // 493
var m = 0644; // 420
```

ECMAScript 5의 Strict 모드는 8 진수 구문을 금지합니다. 8 진수 구문은 ECMAScript 5의 일부가 아니지만, `0644 === 420` 및 `"\ 045"=== "%"`의 8 진수에 접두사를 붙이면 모든 브라우저에서 지원됩니다. ECMAScript 2015에서는 접두어가 `0o`인 경우 8 진수가 지원됩니다 (예 :

```javascript
var a = 0o10; // ES2015: 8
```

### 16진수

16진수 구문은 앞에 0 다음에 소문자나 대문자 라틴어 문자 "X"(`0x` 또는 `0X`)를 사용합니다. 0X 이후 숫자가 범위(0123456789ABCDEF) 밖에 있는 경우, 다음 SyntaxError가 발생합니다: "식별자는 숫자 리터럴 후 즉시 시작됩니다".

```js
0xfffffffffffffffff; // 295147905179352830000
0x123456789abcdef; // 81985529216486900
0xa; // 10
```

### 지수 계산
```

1E3   // 1000
2e6   // 2000000
0.1e2 // 10
```

`Number` 객체
-----------

`Number` 내장객체는 최대값, not-a-number, 무한대와 같은 숫자 상수를 위한 속성들이 있습니다. 여러분은 이러한 속성의 값을 변경 할 수 없고 다음과 같이 사용합니다:

```js
var biggestNum = Number.MAX\_VALUE;
var smallestNum = Number.MIN\_VALUE;
var infiniteNum = Number.POSITIVE\_INFINITY;
var negInfiniteNum = Number.NEGATIVE\_INFINITY;
var notANum = Number.NaN;
```

여러분은 직접 생성한 `Number` 객체의 속성이 아닌, 위와같이 항상 미리 정의된 `Number` 객체의 속성을 참조해야합니다.

다음 표에서는 `Number` 객체의 속성이 요약되어 있습니다.

| 특성 | 묘사 |
| --- | --- |
| `Number.MAX_VALUE` | 표현가능한 가장 큰 수 (`±1.7976931348623157e+308`) |
| `Number.MIN_VALUE` | 표현가능한 가장 작은 수(`±5e-324`) |
| `Number.NaN` | "숫자가 아닌" 특수값 |
| `Number.NEGATIVE_INFINITY` | 음의 무한대값; 오버 플로로 반환됨. |
| `Number.POSITIVE_INFINITY` | 양의 무한대 값; 오버 플로로 반환됨. |
| `Number.EPSILON` | 표현가능한 매우 작은 값`Number`.(`2.220446049250313e-16`) |
| `Number.MIN_SAFE_INTEGER` | JavaScript에서 안전한 최소의 정수.(−2^53 + 1, or `−9007199254740991`) |
| `Number.MAX_SAFE_INTEGER` | JavaScript에서 안전한 최대의 정수.(+2^53 − 1, or `+9007199254740991`) |

`Number`
 메소드들
 | 방법 | 묘사 |
| --- | --- |
| `Number.parseFloat()` |
 문자열 인수를 파싱하고 부동 소수점 숫자를 반환합니다. 전역
 `parseFloat()` 함수와
 동일합니다.
  |
| `Number.parseInt()` |
 문자열 라인 인수를 파싱해, 지정된 기수 또는 밑줄의 정수를 돌려줍니다.
 전역 `parseInt()`함수와 동일합니다.
  |
| `Number.isFinite()` | 전달된 값이 유한한 수인지 판정합니다. |
| `Number.isInteger()` | 전달된 값이 정수인지 판정합니다. |
| `Number.isNaN()` |
 이 전달된 값
 `Not-a-Number`여부를
 확인합니다. 원본 글로벌
 `(isNaN)`의 더욱
 강력한 버전입니다.
  |
| `Number.isSafeInteger()` | 제공된 값이 안전한 정수인지 여부를 확인합니다. |

`Number` 프로토 타입은 다양한 형식의 `Number` 객체에서 정보를 검색하는 메소드를 제공합니다. 다음 표는 `Number.prototype`의 메소드를 요약 한 것입니다.

| 방법 | Description |
| --- | --- |
| `toExponential()` | 지수표기법 안에서 번호를 나타내는 문자열을 반환합니다. |
| `toFixed()` | 문자열 고정 소수 점 표기법의 수를 나타내는 문자열을 반환합니다. |
| `toPrecision()` | 지정된 정밀에 고정 소수 점 표기법의 수를 나타내는 문자열을 반환합니다. |

`Math` 객체
---------

내장 `Math` 객체는 수학 상수 및 함수에 대한 속성 및 메서드를 포함합니다. 예를 들어, `Math` 객체의 `PI` 속성에는 pi (3.141 ...) 값이 있습니다.이 값은 응용 프로그램에서 다음과 같이 사용합니다.

```js
Math.PI;
```

마찬가지로 표준 수학 함수도 `Math`의 함수입니다. 여기에는 삼각 함수, 로그 함수, 지수 함수 및 기타 함수가 포함됩니다. 예를 들어 삼각 함수 sine을 사용하려면 다음과 같이 작성합니다.

```js
Math.sin(1.56);
```

`Math`의 모든 삼각 함수에는 라디안으로 매게변수를 입력해야 합니다.

다음 표에서는 `Math` 개체의 방법을 요약하였습니다.

`Math`의 메소드들| 방법 | 묘사 |
| --- | --- |
| `abs()` | 절대 값 |
| `sin()`,
 `cos()` (en-US),
 `tan()` | 표준 삼각 함수; 라디안에서의 인수 |
| `asin()` (en-US),
 `acos()`,
 `atan()` (en-US),
 `atan2()` (en-US) | 역삼각 함수; 라디안에 반환 값 |
| `sinh()`,
 `cosh()` (en-US),
 `tanh()` | 쌍곡삼각함수; 라디안에 반환 값. |
| `asinh()` (en-US),
 `acosh()`,
 `atanh()` (en-US) | 역쌍곡삼각함수; 라디안에 반환 값. |
|
`pow()`,
 `exp()`,
 `expm1()` (en-US),
 `log10()`,
 `log1p()` (en-US),
 `log2()`
 | 지수와 로그 기능. |
| `floor()`,
 `ceil()` | 가장큰/가장작은 정수 보다 적은/많은 또는 그와 동등한 원칙으로 반환해라. |
| `min()`,
 `max()` | 더적거나 더많은 쉼표의 (각각) 숫자 인수의 나뉜목록으로 반환해라. |
| `random()` | 0과 1사이의 난수를 반환해라. |
| `round()`,
 `fround()`,
 `trunc()`,
  | 반올림과 절단 기능들. |
| `sqrt()`,
 `cbrt()`,
 `hypot()` (en-US) | 제곱 근, 세 제곱 근, 평방 인수의 합의 제곱 근. |
| `sign()` | 그 수가 양수인지 음수인지 0인지를 가르키는 숫자의 표시. |
| `clz32()` (en-US),`imul()` |
 32비트 이진 표시의 주요 제로 비트 수.그 두 인수의 C-like 32비트
 곱셈의 결과.
  |

다른 많은 객체와 달리 자신의 `Math` 개체를 만들필요가 없습니다. 언제든 내장 된 `Math` 객체 바로 사용할 수 있습니다.

`Date` 객체
---------

JavaScript에는 날짜 데이터 타입이 없습니다. 그러나, `Date` 객체와 그 메소드를 사용하여 응용 프로그램에서 날짜와 시간을 처리 할 수 있습니다. `Date` 객체에는 날짜 설정, 가져 오기 및 조작을위한 많은 메소드가 있습니다. 속성(properties)이 없습니다.

JavaScript는 자바와 비슷하게 날짜를 처리합니다. 두 언어에는 동일한 날짜 메소드가 많으며 두 언어 모두 1970 년 1 월 1 일 00:00:00 이후의 밀리 초 수로 날짜를 저장합니다. 유닉스 타임 스탬프는 1970 년 1 월 1 일 00:00:00 이후의 초 수입니다.

Date 개체 범위는 UTC 1970 년 1 월 1 일을 기준으로 -100,000,000 일에서 100,000,000 일입니다.

Date 객체를 만들려면 :

```js
var dateObjectName = new Date([parameters]);
```

여기서 `dateObjectName`은 만들려는 `Date` 객체의 이름입니다. 새로운 객체 또는 기존 객체의 속성 일 수 있습니다.

`new` 키워드없이 `Date`를 호출하면 현재 날짜와 시간을 나타내는 문자열이 반환됩니다.

앞 구문에서 `parameters`는 아래 규칙을 따릅니다.

* 아무것도없을때: 오늘의 날짜와 시간을 만듭니다. 예를 들어, `today = new Date();`.
* 날짜를 나타내는 문자열의 형식: "Month day, year hours:minutes:seconds."예를 들어, `var Xmas95 = new Date("December 25, 1995 13:30:00")`.만약 당신이 시간, 분, 또는 초를 빠뜨린다면, 값은 0이 됩니다.
* 정수 값의 연도, 월, 날의 집합입니다. 예를 들어, `var Xmas95 = new Date(1995, 11, 25)`.
* 연도, 월, 일, 시, 분,초 동안 정수 값의 집합입니다.. 예를 들어, `var Xmas95 = new Date(1995, 11, 25, 9, 30, 0);`.
### `Data` 개체의 표현 방법

날짜와 시간을 조절하는 `Date`객체표현 방법은 아래 종류로 나뉩니다:

* "set" 함수, 날짜 개체 안에서의 날짜 및 시간 값을 설정합니다.
* "get" 함수, 날짜 개체 안에서의 날짜 및 시간 값을 얻습니다.
* "to" 함수, 날짜 개체로부터 문자열 값을 반환합니다.
* `Date` 문자열을 분석하기위해 parse와 UTC함수를 사용합니다.

"get"및 "set"메소드를 사용하여 초, 분,시, 일, 요일, 월 및 연도를 별도로 가져 와서 설정할 수 있습니다. 요일이 자동적으로 설정되기 (위해) 때문에, 요일을 돌려주는 getDay 메소드가 있습니다만, 대응하는 setDay 메소드는 없습니다. 이러한 메서드는 정수를 사용하여 다음과 같이 값을 나타냅니다.

* 초와 분: 0 to 59
* 시간: 0 to 23
* 요일: 0 (Sunday) to 6 (Saturday)
* 날짜: 1 to 31 (day of the month)
* 월: 0 (January) to 11 (December)
* 연도: years since 1900

예를 들어, 아래와 같이 값들을 정의해봅시다 :

```js
var Xmas95 = new Date("December 25, 1995");
```

그러면 `Xmas95.getMonth()`는 11을 반환합니다, 그리고 `Xmas95.getFullYear()`는 1995를 반환합니다.

`getTime`과 `setTime`방법들은 날짜를 나눌때 유용합니다. `getTime`함수는 `Date`객체에 대해 1970년 1월 1일 00:00시부터 밀리초단위로 리턴합니다.

예를 들어, 다음 코드는 현재 년도에 남아 수를 표시합니다:

```js
var today = new Date();
var endYear = new Date(1995, 11, 31, 23, 59, 59, 999); // Set day and month
endYear.setFullYear(today.getFullYear()); // Set year to this year
var msPerDay = 24 \* 60 \* 60 \* 1000; // Number of milliseconds per day
var daysLeft = (endYear.getTime() - today.getTime()) / msPerDay;
var daysLeft = Math.round(daysLeft); //returns days left in the year
```

이 예제에는 오늘 날짜가 포함된 `today`라는 명칭을 가진 `Date`객체를 만듭니다. 그리고 나서 `endYear`라는 `Date`객체를 만들고 현재연도를 설정합니다. 그런 다음 하루에 밀리 초 수를 사용하여 `getTime`을 사용하고 전체 일 수를 반올림하여 `today`와 `endYear` 사이의 일 수를 계산합니다.

`Parse` 함수는 날짜문자열부터 기존의 `Date`객체까지의 값을 할당하기에 유용합니다. 예를 들어, 다음 코드는 그 `IPOdate` 객체에 날짜값을 할당하기위해 `parse`와 `setTime`을 사용합니다;

```js
var IPOdate = new Date();
IPOdate.setTime(Date.parse("Aug 9, 1995"));
```

### 예제

다음 예제 에서 `JSClock()`는 digital 시계형식의 시간을 반환합니다.

```js
function JSClock() {
  var time = new Date();
  var hour = time.getHours();
  var minute = time.getMinutes();
  var second = time.getSeconds();
  var temp = "" + (hour > 12 ? hour - 12 : hour);
  if (hour == 0) temp = "12";
  temp += (minute < 10 ? ":0" : ":") + minute;
  temp += (second < 10 ? ":0" : ":") + second;
  temp += hour >= 12 ? " P.M." : " A.M.";
  return temp;
}
```

`JSClock` 함수는 먼저 time이라는 새 `Date` 객체를 만듭니다. 인수가 없으므로 현재 날짜와 시간으로 시간이 생성됩니다. 그런 다음 `getHours`, `getMinutes` 및 `getSeconds` 메소드를 호출하면 현재 시간, 분 및 초 값이`hour`, `minute`, `second`로 할당됩니다.

다음 네 문장은 시간을 기준으로 문자열 값을 만듭니다. 첫 번째 명령문은 변수 temp를 작성하고 조건식을 사용하여 값을 할당합니다. hour가 12보다 큰 경우 (hour - 12), 그렇지 않은 경우 시간이 0이 아닌 경우 시간이 12 일 경우 시간이 12가됩니다.

다음 명령문은 `temp` 값에 `minute` 값을 추가합니다. 분(`minute`)의 값이 10보다 작 으면 조건식은 앞에 0이 있는 문자열을 추가합니다. 그렇지 않으면 콜론을 구분하는 문자열을 추가합니다. 그런 다음 같은 방법으로 temp에 초 값을 추가합니다.

마지막으로 조건부 표현식에 "P.M."이 추가됩니다. 시간(`hour`)이 12 시간 이상이면 임시(`temp`)로; 그렇지 않으면 "A.M."을 `temp`에 추가합니다.

* « 이전
* 다음 »


표현식과 연산자 - JavaScript

표현식과 연산자
========

* « 이전
* 다음 »

이번 장에서는 JavaScript의 표현식과 함께 할당, 비교, 산술, 비트 계산, 논리, 문자열, 삼항 등 다양한 연산자를 살펴보겠습니다.

참고서에서 모든 연산자와 표현식의 자세한 목록도 확인할 수 있습니다.

연산자
---

JavaScript의 연산자는 다음과 같은 유형으로 나눌 수 있습니다. 이 절은 각각의 연산자에 대해 설명하고, 연산자 우선순위에 관한 정보를 제공합니다.

* 할당 연산자
* 비교 연산자
* 산술 연산자
* 비트 연산자
* 논리 연산자
* 문자열 연산자
* 조건 (삼항) 연산자
* 쉼표 연산자
* 단항 연산자
* 관계 연산자

JavaScript는 이항 연산자와 단항 연산자를 모두 포함하며, 유일한 삼항 연산자로 조건 연산자도 가지고 있습니다. 이항 연산자는 연산자의 앞과 뒤에 하나씩 총 두 개의 피연산자가 필요합니다.

```js
피연산자1 연산자 피연산자2
```

이항 연산자의 예시로는 `3+4`와 `x*y`가 있습니다.

단항 연산자는 연산자의 앞이나 뒤에 하나의 피연산자가 필요합니다.

```js
연산자 피연산자
```

또는

```js
피연산자 연산자
```

단항 연산자의 예시로는 `x++` 또는 `++x`가 있습니다.

### 할당 연산자

 할당 연산자는 오른쪽 피연산자의 값을 왼쪽 피연산자에 할당합니다. 기본적인 할당 연산자는 오른쪽의 피연산자 값을 왼쪽
 피연산자 값에 할당하는 등호(`=`)로, `x = y` 는 `y`의 값을 `x`에
 할당합니다.

아래의 표에서 볼 수 있듯, 연산과 할당을 동시에 수행하는 복합 할당 연산자도 존재합니다.

| 이름 | 단축 연산자 | 뜻 |
| --- | --- | --- |
| 할당 | `x = y` | `x = y` |
| 더하기 할당 | `x += y` | `x = x + y` |
| 빼기 할당 | `x -= y` | `x = x - y` |
| 곱하기 할당 | `x *= y` | `x = x * y` |
| 나누기 할당 | `x /= y` | `x = x / y` |
| 나머지 할당 | `x %= y` | `x = x % y` |
| 거듭제곱 할당 | `x **= y` | `x = x ** y` |
| 왼쪽 시프트 할당 | `x <<= y` | `x = x << y` |
| 오른쪽 시프트 할당 (en-US) | `x >>= y` | `x = x >> y` |
| 부호 없는 오른쪽 시프트 할당 | `x >>>= y` | `x = x >>> y` |
| 비트 AND 할당 | `x &= y` | `x = x & y` |
| 비트 XOR 할당 (en-US) | `x ^= y` | `x = x ^ y` |
| 비트 OR 할당 (en-US) | `x |= y` | `x = x | y` |
| 논리 AND 할당 (en-US) | `x &&= y` | `x && (x = y)` |
| 논리 OR 할당 (en-US) | `x ||= y` | `x || (x = y)` |
| 널 병합 할당 (en-US) | `x ??= y` | `x ?? (x = y)` |

#### 속성 할당

어떤 표현식이 객체로 평가된다면, 할당 표현식의 좌항은 객체로 평가되는 표현식의 속성에 할당할 수 있습니다. 예를 들어,

```js
let obj = {};

obj.x = 3;
console.log(obj.x); // 3 출력.
console.log(obj); // { x: 3 } 출력.

const key = "y";
obj[key] = 5;
console.log(obj[key]); // 5 출력.
console.log(obj); // { x: 3, y: 5 } 출력.
```

객체에 관한 더 많은 정보를 객체로 작업하기에서 찾아보세요.

어떤 표현식이 객체로 평가되지 않으면, 그 표현식의 속성에 대한 할당 표현식은 실제로는 할당하지 않습니다.

```js
let val = 0;

console.log((val.x = 3)); // 3 출력.
console.log(val.x); // undefined 출력.
console.log(val); // 0 출력.
```

수정 불가능한 속성이나, 속성이 없는 표현식(`null`이나 `undefined`)으로 할당을 시도하면 오류가 발생합니다.

#### 구조 분해

구조 분해 할당 구문은 JavaScript 표현식 중 하나로, 객체나 배열을 생성할 때와 비슷한 구문으로 사용해서 어떤 객체나 배열에서 데이터를 추출할 수 있습니다.

```js
var foo = ["one", "two", "three"];

// 구조 분해 없음
var one = foo[0];
var two = foo[1];
var three = foo[2];

// 구조 분해 사용
var [one, two, three] = foo;
```

#### 반환 값과 체이닝

일반적으로, 할당 표현식은 변수 선언(`const`, `let`, `var`)과 함께 쓰이거나 독립적인 명령문으로 사용됩니다.

```js
// 변수 x를 선언해서 f()의 결과로 초기화,
// x = f() 할당 표현식의 결과는 버려짐.
let x = f();

// 변수 x를 선언해서 g()의 결과로 초기화,
// x = g() 할당 표현식의 결과는 버려짐.
x = g(); // 변수 x를 g()의 결과로 재할당.
```

그런데, 다른 표현식들처럼 `x = f()`와 같은 할당 표현식도 결과 값으로 평가됩니다. 보통 이 값을 사용하진 않지만, 그래도 다른 평가식의 값으로 사용할 수 있다는 점은 바뀌지 않습니다.

할당 체이닝이나, 다른 표현식에 중첩된 할당은 예상하기 힘든 동작을 보일 수 있습니다. 그래서 어떤 JavaScript 스타일 가이드에서는 할당을 체이닝하거나 중첩하는 걸 지양하라 말합니다. 어쨌든, 할당 체이닝과 중첩을 가끔 마주치게 되므로 어떻게 동작하는지는 알아두는 게 좋습니다.

할당 표현식을 체이닝하거나 중첩함으로써 할당의 결과를 다시 다른 변수에 할당할 수 있습니다. 아니면 콘솔에 기록할 수도 있고, 배열 리터럴이나 함수 호출에 사용할 수도 있습니다.

```js
let x;
const y = (x = f()); // 또는 const y = x = f(); 와 같음.
console.log(y); // x = f() 할당의 반환 값을 기록.

console.log((x = f())); // 반환 값을 직접 기록.

// 할당 표현식은 보통 표현식을 기대하는 곳이라면
// 어디에나 중첩해 사용할 수 있으며
// 배열 리터럴의 요소나 함수의 매개변수도 그런 곳 중 일부임.
console.log([0, (x = f()), 0]);
console.log(f(0, (x = f()), 0));
```

평가 결과는 위쪽 표의 "뜻" 열에서 `=` 기호의 우항 표현식 결과와 같습니다. 그래서 `x = f()`의 평가 결과는 `f()`의 평가 결과와 같고, `x += f()`의 평가 결과는 `x + f()`와 같으며, `x **= f()`는 `x ** y`와 같습니다.

논리 할당인 `(x &&= y)`, `(x ||= y)`, `(x ??= y)`의 경우에는 할당 부분을 제외한 논리 연산의 결과를 반환합니다. 따라서 각각 `x && y`, `x || y`, `x ?? y`의 결과를 반환합니다.

할당 표현식을 괄호나 배열 리터럴같은 그룹 연산자 없이 체이닝하면, 할당 표현식은 **오른쪽에서 왼쪽으로 묶이지만**(우측 결합), **왼쪽에서 오른쪽으로 평가됩니다**.

참고로, 순수한 `=`을 제외한 모든 할당 연산자의 평가 결과 값은 항상 계산하기 전의 값에 기반합니다.

예를 들어, 다음과 같은 함수 `f`, `g`와 변수 `x`, `y`가 선언된 상황을 가정해 보겠습니다.

```js
function f() {
  console.log("F!");
  return 2;
}
function g() {
  console.log("G!");
  return 3;
}
let x, y;
```

다음 세 예제를 확인해 보세요.

```js
y = x = f();
y = [f(), (x = g())];
x[f()] = g();
```

##### 평가 예제 1

`=`은 우측 결합을 하기 때문에 `y = x = f()`는 `y = (x = f())`와 같습니다. 하지만 평가는 왼쪽에서 오른쪽으로 나아가므로,

1. 할당 표현식 `y = x = f()`의 평가를 시작합니다.
    1. 이 할당의 좌항인 `y`가, `y`라는 이름의 변수 참조로 평가됩니다.
    2. 할당 표현식 `x = f()`의 평가를 시작합니다.
        1. 이 할당의 좌항인 `x`가, `x`라는 이름의 변수 참조로 평가됩니다.
        2. 함수 호출 `f()`가 콘솔에 "F!"를 기록한 후 숫자 `2`로 평가됩니다.
        3. `f()`의 결과인 `2`를 `x`에 할당합니다.
    3. 할당 표현식 `x = f()`의 평가가 끝났습니다. 표현식의 결과 값은 `x`의 새로운 값, 즉 `2`입니다.
    4. 위의 결과인 `2`를 `y`에도 할당합니다.
2. 할당 표현식 `y = x = f()`의 평가가 끝났습니다. 표현식의 결과 값은 `y`의 새로운 값, 즉 `2`입니다. `x`와 `y`에 `2`를 할당했고, 콘솔에는 "F!"를 기록했습니다.

##### 평가 예제 2

`y = [ f(), x = g() ]` 또한 왼쪽에서 오른쪽으로 평가합니다.

1. 할당 표현식 `y = [ f(), x = g() ]`의 평가를 시작합니다.
    1. 이 할당의 좌항인 `y`가, `y`라는 이름의 변수 참조로 평가됩니다.
    2. 배열 리터럴 `[ f(), x = g() ]`의 평가를 시작합니다.
        1. 함수 호출 `f()`가 콘솔에 "F!"를 기록한 후 숫자 `2`로 평가됩니다.
        2. 할당 표현식 `x = g()`의 평가를 시작합니다.
            1. 이 할당의 좌항인 `x`가, `x`라는 이름의 변수 참조로 평가됩니다.
            2. 함수 호출 `g()`가 콘솔에 "G!"를 기록한 후 숫자 `3`으로 평가됩니다.
            3. `g()`의 결과인 `3`을 `x`에 할당합니다.
        3. 할당 표현식 `x = g()`의 평가가 끝났습니다. 표현식의 결과 값은 `x`의 새로운 값, 즉 `3`입니다. 이 결과인 `3`이 배열 리터럴의 두 번째 요소가 됩니다. (첫 번째 요소는 `f()`의 결과인 `2`입니다)
    3. 배열 리터럴 `[ f(), x = g() ]`의 평가가 끝났습니다. 그 결과는 두 개의 요소를 가진 배열, `[ 2, 3 ]`입니다.
    4. 위의 결과인 `[ 2, 3 ]` 배열을 `y`에 할당합니다.
2. 할당 표현식 `y = [ f(), x = g() ]`의 평가가 끝났습니다. 표현식의 결과 값은 `y`의 새로운 값, 즉 `[ 2, 3 ]`입니다. `x`에는 `3`, `y`에는 `[ 2, 3 ]`이 할당됐고, 콘솔에는 "F!" 후에 "G!"를 기록했습니다.

##### 평가 예제 3

`x[f()] = g()` 또한 왼쪽에서 오른쪽으로 평가합니다. (이 예제는 어떤 객체가 `x`에 할당됐다는 추가 가정 하에 진행합니다. 객체에 관한 더 많은 정보는 객체로 작업하기를 참고하세요)

1. 할당 표현식 `x[f()] = g()`의 평가를 시작합니다.
    1. 이 할당의 좌항인 `x[f()]` 속성 접근의 평가를 시작합니다.
        1. 이 속성 접근의 `x`가, `x`라는 이름의 변수 참조로 평가됩니다.
        2. 함수 호출 `f()`가 콘솔에 "F!"를 기록한 후 숫자 `2`로 평가됩니다.
    2. 이 할당의 속성 접근 `x[f()]`의 평가가 끝났습니다. 그 결과 값은 변수 속성 참조 `x[2]`입니다.
    3. 다음으로, 함수 호출 `g()`가 콘솔에 "G!"를 기록한 후 숫자 `3`으로 평가됩니다.
    4. `g()`의 결과인 `3`을 `x[2]`에 할당합니다. (이 단계는 `x`에 객체를 미리 할당한 경우에만 성공합니다)
2. 할당 표현식 `x[f()] = g()`의 평가가 끝났습니다. 표현식의 결과 값은 `x[2]`의 새로운 값, 즉 `3`입니다. `x[2]`에는 `3`이 할당됐고, 콘솔에는 "F!" 후에 "G!"를 기록했습니다.

#### 할당 체이닝을 피해야 할 이유

할당 체이닝이나, 다른 표현식에 중첩된 할당은 예상하기 힘든 동작을 보일 수 있습니다. 그래서 하나의 명령문 내에서 할당을 체이닝하는 것은 추천하지 않습니다.

특히 `const`, `let`, `var` 명령문에 변수 체인을 사용하면 종종 제대로 동작하지 않고, 가장 바깥 (왼쪽) 변수만 선언되며 나머지는 `const`/`let`/`var`가 선언하지 않을 것입니다. 예를 들어,

```js
let z = (y = x = f());
```

위 명령문은 얼핏 봤을 땐 `x`, `y`, `z` 세 변수를 선언하는 모습처럼 보입니다. 하지만 사실 위 코드는 변수 `z`만 선언합니다. 나머지 두 `y`와 `x`는 ([엄격 모드][/ko/docs/web/javascript/reference/strict\_mode]에선) 존재하지 않는 변수를 향하는 유효하지 않은 참조거나, 심지어 (느슨한 모드에선) `x`와 `y`를 암묵적인 [전역 변수][/ko/docs/glossary/global\_variable]로 선언할 수도 있습니다

### 비교 연산자

 비교 연산자는 피연산자를 서로 비교하고, 비교 결과가 참인지에 따라 논리 값을 반환합니다. 피연산자로는 숫자, 문자열, 논리형, 객체 값을 사용할 수 있습니다. 문자열은 Unicode 값을 사용한 표준 사전식 순서에 따라 비교합니다. 만약 두 피연산자가 서로 다른 타입이면, JavaScript는 피연산자들을 서로 비교하기에 적합한 타입으로 변환합니다. 이 동작은 대개 두 값을 모두 숫자로 변환한 후 비교한 결과를 낳습니다. 비교 연산에서 발생하는 타입 변환의 유일한 예외는
 `===`과 `!==` 연산자를 사용해 엄격 일치와 불일치 비교를 수행하는 경우인데, 두 연산자는
 비교 전에 피연산자를 변환하지 않습니다. 아래의 표는 다음 예제 코드를 사용해 각각의 비교 연산자를 설명합니다.

```js
const var1 = 3;
const var2 = 4;
```

비교 연산자| 연산자 | 설명 | `true`를 반환하는 예제 |
| --- | --- | --- |
| 동등
 (`==`)
  | 피연산자가 서로 같으면 `true`를 반환합니다. | `3 == var1`
`"3" == var1``3 == '3'` |
| 부등
 (`!=`)
  | 피연산자가 서로 다르면 `true`를 반환합니다. | `var1 != 4var2 != "3"` |
| 일치 (en-US)
 (`===`)
  |
 두 피연산자의 값과 타입이 모두 같은 경우 `true`를 반환합니다.
 `Object.is`와
 JavaScript에서의 같음을 참고하세요.
  | `3 === var1` |
| 불일치 (en-US)
 (`!==`)
  | 피연산자의 값 또는 타입이 서로 다를 경우 `true`를 반환합니다. | `var1 !== "3"3 !== '3'` |
| 큼 (en-US)
 (`>`)
  | 왼쪽 피연산자가 오른쪽 피연산자보다 크면 `true`를 반환합니다. | `var2 > var1"12" > 2` |
| 크거나 같음 (en-US)
 (`>=`)
  |
 왼쪽 피연산자가 오른쪽 피연산자와 같거나 크면 `true`를
 반환합니다.
  | `var2 >= var1var1 >= 3` |
| 작음 (en-US)
 (`<`)
  |
 왼쪽 피연산자가 오른쪽 피연산자보다 작으면 `true`를
 반환합니다.
  | `var1 < var2"2" < 12` |
| 작거나 같음 (en-US)
 (`<=`)
  |
 왼쪽 피연산자가 오른쪽 피연산자와 같거나 작으면 `true`를
 반환합니다.
  | `var1 <= var2var2 <= 5` |

**참고:** `=>`는 연산자가 아니라
 화살표 함수의 표기법입니다.

### 산술 연산자

 산술 연산자는 두 개의 숫자 값(리터럴 또는 변수)을 피연산자로 받아서 하나의 숫자 값을 반환합니다. 표준 산술 연산자는
 더하기(`+`), 빼기(`-`), 곱하기(`*`), 나누기(`/`)입니다. 이 연산자들은
 대부분의 다른 프로그래밍 언어에서 부동소수점 값을 연산할 때와 동일하게 동작합니다. (0 으로 나눌 경우
 `Infinity`를 반환하는 것에 주의하세요) 예를 들어,

```js
1 / 2; // 0.5
1 / 2 == 1.0 / 2.0; // 참
```

 JavaScript는 표준 산술 연산자(`+`, `-`, `*`, `/`) 외에도 아래의 표에
 나열된 산술 연산자를 제공합니다.

산술 연산자| 연산자 | 설명 | 예제 |
| --- | --- | --- |
| 나머지
 (`%`)
  | 이항 연산자입니다. 두 피연산자를 나눴을 때의 나머지를 반환합니다. | 12 % 5 는 2를 반환합니다. |
| 증가
 (`++`)
  |
 단항 연산자입니다. 피연산자에 1을 더합니다. 전위 연산자(`++x`)로 사용하면 피연산자에 1을 더한 값을
 반환합니다. 후위 연산자(`x++`)로 사용한 경우 피연산자에 1을 더하기 전의 값을 반환합니다.
  | `x`가 3일 때, `++x`는 `x`에 4를 할당한 후 4를 반환합니다. 반면
 `x++`는 3을 먼저 반환한 후 `x`에 4를 할당합니다.
  |
| 감소
 (`--`)
  | 단항 연산자입니다. 피연산자에서 1을 뺍니다. 반환 값은 증가 연산자처럼 동작합니다. | `x`가 3일 때, `--x`는 `x`에 2를 할당한 후 2를 반환합니다. 반면
 `x--`는 3을 먼저 반환한 후 `x`에 2를 할당합니다.
  |
| 단항 부정
 (`-`)
  | 단항 연산자입니다. 피연산자의 부호를 반대로 바꾼 값을 반환합니다. | `x`가 3일 때, `-x`는 -3을 반환합니다. |
| 단항 플러스
 (`+`)
  | 단항 연산자입니다. 피연산자가 숫자 타입이 아니면 숫자로 변환을 시도합니다. | `+"3"`은 `3`을 반환합니다.`+true`는 `1`을 반환합니다. |
| 거듭제곱 (`**`) | `base^exponent`, 즉 `base`를 `exponent`로 거듭제곱한 결과를 반환합니다. | `2 ** 3`은 `8`을 반환합니다.`10 ** -1`은 `0.1`을 반환합니다. |

### 비트 연산자

 비트 연산자는 피연산자를 10진수, 16진수, 8진수 숫자로 취급하지 않고, 대신 32개 비트의 집합으로 취급합니다. 예를 들어,
 10진수 9는 2진수 1001로 나타냅니다. 비트 연산자는 이러한 이진법 표현에 대해 연산을 수행하지만, 반환할 땐 JavaScript
 표준 숫자로 반환합니다.

아래의 표에서 JavaScript 비트 연산자의 요약본을 볼 수 있습니다.

| 연산자 | 사용법 | 설명 |
| --- | --- | --- |
| 비트 AND | `a & b` | 두 피연산자의 각 자리 비트의 값이 모두 1인 위치에 1을 반환합니다. |
| 비트 OR (en-US) | `a | b` | 두 피연산자의 각 자리 비트의 값이 모두 0인 위치에 0을 반환합니다. |
| 비트 XOR (en-US) | `a ^ b` | 두 피연산자의 각 자리 비트의 값이 서로 같은 위치에 0을 반환합니다. [두 피연산자의 각 자리 비트의 값이 서로 다른 위치에 1을 반환합니다.] |
| 비트 NOT | `~ a` | 피연산자의 각 자리의 비트를 뒤집습니다. |
| 왼쪽 시프트 | `a << b` | `a`의 이진 표현을 `b`만큼 왼쪽으로 이동하고, 오른쪽은 0으로 채웁니다. |
| 오른쪽 시프트 (en-US) | `a >> b` | `a`의 이진 표현을 `b`만큼 오른쪽으로 이동하고, 1 미만으로 이동한 비트는 버립니다. |
| 부호 없는 오른쪽 시프트 (en-US) | `a >>> b` | `a`의 이진 표현을 `b`만큼 오른쪽으로 이동하고, 1 미만으로 이동한 비트는 버립니다. 왼쪽은 0으로 채웁니다. |

#### 비트 논리 연산자

개념적으로, 비트 논리 연산자는 다음과 같이 동작합니다.

* 두 피연산자를 32비트 정수로 변환해서 비트(0 과 1)의 연속으로 표현합니다. 숫자가 32비트를 초과할 경우 가장 큰
 비트부터 버립니다. 다음은 32비트가 넘는 정수의 변환 예제입니다.
```

전: 1110 0110 1111 1010 0000 0000 0000 0110 0000 0000 0001
후:                1010 0000 0000 0000 0110 0000 0000 0001
```

* 왼쪽 피연산자의 각 비트와 오른쪽 피연산자의 각 비트를 순서대로 쌍으로 만듭니다. 즉, 첫 비트는 첫 비트와, 두 번째
 비트는 두 번째 비트와, ... 32번째 비트는 32번째 비트와 짝을 짓습니다.
* 연산자를 각각의 비트 쌍에 대해 적용한 결과를 비트 단위로 구축합니다.

예를 들어, 9의 이진 표현은 1001이고, 15의 이진 표현은 1111입니다. 비트 연산자를 이 두 값에 적용했을 때의 결과는 다음과 같습니다.

| 표현식 | 결과 | 이진법 설명 |
| --- | --- | --- |
| `15 & 9` | `9` | `1111 & 1001 = 1001` |
| `15 | 9` | `15` | `1111 | 1001 = 1111` |
| `15 ^ 9` | `6` | `1111 ^ 1001 = 0110` |
| `~15` | `-16` | `~ 0000 0000 ... 0000 1111 = 1111 1111 ... 1111 0000` |
| `~9` | `-10` | `~ 0000 0000 ... 0000 1001 = 1111 1111 ... 1111 0110` |

 참고로, 비트 NOT 연산자를 사용하면 32개의 비트를 모두 반전하는데, 가장 큰 (맨 왼쪽) 비트가 1이면 음수를 나타냅니다(2의
 보수 표현법). `~x`는 `-x - 1`과 같은 값으로 평가됩니다.

#### 비트 시프트 연산자

 비트 시프트 연산자는 시프트를 적용할 값과, 시프트할 자릿수를 피연산자로 받습니다. 시프트 방향은 사용하는 연산자에 따라
 다릅니다.

 시프트 연산자는 피연산자를 32비트 정수로 변환하고, 결과 값을 `Number`나 `BigInt`로 반환합니다.
 정확히는, 왼쪽 피연산자가 `BigInt`면 `BigInt`를 반환하고, 그 외에는 `Number`를
 반환합니다.

아래의 표에서 시프트 연산자의 종류를 볼 수 있습니다.

비트 시프트 연산자| 연산자 | 설명 | 예제 |
| --- | --- | --- |
| 왼쪽 시프트 (`<<`) |
 왼쪽 피연산자를 오른쪽 피연산자만큼 왼쪽으로 시프트합니다. 왼쪽으로 넘치는 비트는 버리고, 오른쪽을 0으로
 채웁니다.
  | `9<<2`는, 1001을 왼쪽으로 2번 시프트하면 100100이므로 36입니다. |
| 오른쪽 시프트 (`>>`) (en-US) |
 왼쪽 피연산자를 오른쪽 피연산자만큼 오른쪽으로 시프트합니다. 오른쪽으로 넘치는 비트는 버리고, 왼쪽은 제일 큰
 비트의 값으로 채웁니다.
  | `9>>2`는, 1001을 오른쪽으로 2번 시프트하면 10이므로 2입니다. 마찬가지로
 `-9>>2`는, 부호를 유지하므로 -3을 반환합니다.
  |
| 부호 없는 오른쪽 시프트 (`>>>`) (en-US) |
 왼쪽 피연산자를 오른쪽 피연산자만큼 오른쪽으로 시프트합니다. 오른쪽으로 넘치는 비트는 버리고, 왼쪽은 0으로
 채웁니다.
  | `19>>>2`는, 10011을 오른쪽으로 2번 시프트하면 100이므로 4입니다. 양의 수에 대해서는 오른쪽
 시프트와 부호 없는 오른쪽 시프트 둘 다 같은 결과를 반환합니다.
  |

### 논리 연산자

 논리 연산자는 보통 불리언(논리) 값과 함께 사용해서 불리언 값을 반환합니다. 그러나
 `&&`와 `||` 연산자는 사실 두 피연산자 중 하나를 반환하는 것으로, 만약 둘 중 하나가
 불리언 값이 아니라면 논리 연산자의 반환 값도 불리언 값이 아닐 수 있습니다. 아래의 표에서 논리 연산자의 설명을 볼 수
 있습니다.

논리 연산자| 연산자 | 사용법 | 설명 |
| --- | --- | --- |
| 논리 AND
 (`&&`)
  | `expr1 && expr2` | `expr1`을 `false`로 변환할 수 있으면 `expr1`을 반환합니다. 그 외의 경우에는
 `expr2`를 반환합니다. 따라서 불리언 값과 함께 사용한 경우, 둘 다 참일 때 `true`를, 그
 외에는 `false`를 반환합니다.
  |
| 논리 OR (`||`) | `expr1 || expr2` | `expr1`을 `true`로 변환할 수 있으면 `expr1`을 반환합니다. 그 외의 경우에는
 `expr2`를 반환합니다. 따라서 불리언 값과 함께 사용한 경우, 둘 중 하나가 참일 때 `true`를,
 그 외에는 `false`를 반환합니다.
  |
| 논리 NOT
 (`!`)
  | `!expr` |
 단일 피연산자를 `true`로 변환할 수 있으면 `false`를 반환합니다. 그 외에는
 `true`를 반환합니다.
  |

`false`로 변환할 수 있는 표현식은 평가 결과가 `null`, `0`, `NaN`, 빈
 문자열(`""`), `undefined`인 경우입니다.

다음 코드는 `&&` (논리 AND) 연산자의 예제입니다.

```js
var a1 = true && true; // t && t는 true 반환
var a2 = true && false; // t && f는 false 반환
var a3 = false && true; // f && t는 false 반환
var a4 = false && 3 == 4; // f && f는 false 반환
var a5 = "Cat" && "Dog"; // t && t는 Dog 반환
var a6 = false && "Cat"; // f && t는 false 반환
var a7 = "Cat" && false; // t && f는 false 반환
```

다음 코드는 `||` (논리 OR) 연산자의 예제입니다.

```js
var o1 = true || true; // t || t는 true 반환
var o2 = false || true; // f || t는 true 반환
var o3 = true || false; // t || f는 true 반환
var o4 = false || 3 == 4; // f || f는 false 반환
var o5 = "Cat" || "Dog"; // t || t는 Cat 반환
var o6 = false || "Cat"; // f || t는 Cat 반환
var o7 = "Cat" || false; // t || f는 Cat 반환
```

다음 코드는 `!` (논리 NOT) 연산자의 예제입니다.

```js
var n1 = !true; // !t는 false 반환
var n2 = !false; // !f는 true 반환
var n3 = !"Cat"; // !t는 false 반환
```

#### 단락 평가

 논리 연산자는 왼쪽에서 오른쪽의 순서로 평가하므로, 다음 규칙을 사용해서 "단락"(short-circuit) 평가가 가능한지
 판별합니다.

* `false && 아무거나`는 거짓으로 단락 평가합니다.
* `true || 아무거나`는 참으로 단락 평가합니다.

 논리 규칙은 위의 단락 평가가 항상 옳음을 보증합니다. *아무거나* 부분의 표현식은 평가도 하지 않으므로, 해당
 표현식에서 유발하게 되는 부수 효과도 전혀 발생하지 않음에 주의하세요.

 참고로 `||`을 사용한 두 번째 단락 평가의 경우, 최근 코드에서는 새로운
 널 병합 연산자
 (`??`)를 사용할 수도 있습니다. 널 병합 연산자는 첫 번째 연산자가 "널과 유사"한, 즉 `null`이거나 `undefined`일 때만 두 번째 피연산자를 반환합니다. `''`와
 `0`도 유효한 값이라면 널 병합 연산자가 기본 값을 나타낼 때 더 좋은 선택지입니다.

### 문자열 연산자

 문자열에 사용할 수 있는 비교 연산자들 외에도, 문자열 연결(`+`) 연산자는 두 문자열의 값을 서로 연결한 새로운
 문자열을 반환합니다.

예를 들어,

```js
console.log("나만의 " + "문자열"); // 콘솔에 "나만의 문자열"을 기록
```

단축 할당 연산자인 `+=` 또한 문자열을 연결결할 때 사용할 수 있습니다.

예를 들어,

```js
var mystring = "한";
mystring += "글"; // "한글"로 평가되며, mystring에 "한글"을 할당함
```

### 조건 (삼항) 연산자

조건 연산자는 JavaScript에서 세 개의 피연산자를 받는 유일한 연산자입니다. 조건 연산자는 주어진 조건에 따라 두 값 중 하나를 반환하며, 구문은 다음과 같습니다.

```js
condition ? val1 : val2;
```

 만약 `condition`이 참이라면, 조건 연산자는 `val1`을 반환하고, 그 외에는 `val2`를
 반환합니다. 다른 연산자를 사용할 수 있는 곳이라면 조건 연산자도 사용할 수 있습니다.

에들 들어,

```js
var status = age >= 18 ? "성인" : "미성년자";
```

 위의 명령문은 `age`가 18 이상이라면 `status` 변수에 "성인"을 할당하고, 그렇지 않으면
 "미성년자"를 할당합니다.

### 쉼표 연산자

쉼표 연산자(`,`)는 두 피연산자를 모두 평가한 후 오른쪽 피연산자의 값을 반환합니다. 쉼표 연산자는 주로 `for` 반복문 안에서 사용하여 한 번의 반복으로 여러 변수를 변경할 때 사용합니다. 꼭 필요하지 않다면, 그 외의 상황에 사용하는 것은 좋지 않은 코드 스타일로 여겨집니다. 대개 쉼표 연산자보다는 두 개의 분리된 명령문을 사용하는 편이 낫습니다.

쉼표 연산자의 예제로, 10행 10열의 2차원 배열 `a`를 가정할 때, 아래의 코드는 쉼표 연산자를 사용해서 두 개의 변수를 한번에 업데이트하는 모습을 보입니다. 코드의 실행 결과는 `a`의 요소를 대각선((0, 0), (1, 1), (2, 2), ...)으로 출력한 것입니다.

```js
var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var a = [x, x, x, x, x];

for (var i = 0, j = 9; i <= j; i++, j--);
// ^
console.log("a[" + i + "][" + j + "]= " + a[i][j]);
```

### 단항 연산자

단항 연산은 오직 하나의 피연산자만 사용하는 연산입니다.

#### `delete`

`delete` (en-US)연산자는 객체의 속성을 삭제합니다. 구문은 다음과 같습니다.

```js
delete object.property;
delete object[propertyKey];
delete objectName[index];
```

 위의 구문에서, `objectName`은 객체의 이름이고, `property`는 객체에 존재하는 속성,
 `propertyKey`는 존재하는 속성을 가리키는 문자열이나 심볼입니다.

`delete` 연산자가 속성을 성공적으로 삭제한 이후, 해당 속성을 접근하려고 하면 `undefined`가
 반환됩니다. `delete`는 속성을 제거할 수 있는 경우에는 `true`를 반환하고, 제거할 수 없을 땐
 `false`를 반환합니다.

```js
delete Math.PI; // false 반환 (설정 불가한 속성 삭제 불가)

const myObj = { h: 4 };
delete myobj.h; // true 반환 (사용자 정의 속성 삭제 가능)
```

##### 배열의 원소 삭제하기

 배열도 평범한 객체이므로, 기술적으로는 `delete`를 사용해서 요소를 제거하는 것이 가능합니다. 그러나 이는
 좋은 방법이 아니므로 피해야 합니다. 배열의 속성을 제거해도 배열 길이 속성은 영향을 받지 않으며, 다른 요소의 인덱스도
 그대로 남아있습니다. 이런 동작을 원한다면 단순히 값을 `undefined`로 덮어쓰는 편이 훨씬 낫습니다. 실제로
 배열을 변형하고자 하면 `splice`와 같은 다양한 배열 메서드를 사용하세요.

#### `typeof`

`typeof` 연산자는 다음과 같은 방법으로
 사용합니다.

```js
typeof operand;
typeof (operand);
```

`typeof` 연산자는 평가 전의 피연산자 타입을 나타내는 문자열을 반환합니다. `operand`는 문자열,
 변수, 키워드, 객체 등 타입을 알아낼 값입니다. 주위의 괄호는 선택 사항입니다.

다음과 같은 변수를 가정하겠습니다.

```js
var myFun = new Function("5 + 2");
var shape = "round";
var size = 1;
var foo = ["Apple", "Mango", "Orange"];
var today = new Date();
```

`typeof` 연산자는 위의 변수들에 대해서 다음과 같은 값을 반환합니다.

```js
typeof myFun; // "function" 반환
typeof shape; // "string" 반환
typeof size; // "number" 반환
typeof foo; // "object" 반환
typeof today; // "object" 반환
typeof dontExist; // "undefined" 반환
```

키워드 `true`와 `null`에 대해서는 다음과 같은 결과를 반환합니다.

```js
typeof true; // "boolean" 반환
typeof null; // "object" 반환
```

숫자와 문자열에서는 다음과 같은 결과를 반환합니다.

```js
typeof 62; // "number" 반환
typeof "Hello world"; // "string" 반환
```

객체의 속성에 사용하면 속성이 갖고 있는 값의 타입을 반환합니다.

```js
typeof document.lastModified; // "string" 반환
typeof window.length; // "number" 반환
typeof Math.LN2; // "number" 반환
```

메서드와 함수에 대해선 다음과 같은 결과를 반환합니다.

```js
typeof blur; // "function" 반환
typeof eval; // "function" 반환
typeof parseInt; // "function" 반환
typeof shape.split; // "function" 반환
```

사전 정의된 객체에 대해선 다음과 같은 결과를 반환합니다.

```js
typeof Date; // "function" 반환
typeof Function; // "function" 반환
typeof Math; // "object" 반환
typeof Option; // "function" 반환
typeof String; // "function" 반환
```

#### `void`

`void` 연산자는 다음과 같이 사용합니다.

```js
void (expression);
void expression;
```

`void` 연산자는 표현식을 평가할 때 값을 반환하지 않도록 지정합니다. `expression`은 평가할
 JavaScript 표현식입니다. 주위 괄호는 선택 사항이지만, 사용하면 좋습니다.

### 관계 연산자

관계 연산자는 피연산자를 서로 비교하고, 비교 결과가 참인지에 따라 불리언 값을 반환합니다.

#### `in`

`in` 연산자는 지정한 속성이 지정한 객체에
 존재할 경우 `true`를 반환합니다. 구문은 다음과 같습니다.

```js
propNameOrNumber in objectName;
```

`propNameOrNumber`는 속성이나 배열 인덱스를 나타내는 문자열, 숫자, 심볼 표현식이며
 `objectName`은 객체의 이름입니다.

다음 코드는 `in` 연산자의 예제입니다.

```js
// 배열
var trees = ["redwood", "bay", "cedar", "oak", "maple"];
0 in trees; // true 반환
3 in trees; // true 반환
6 in trees; // false 반환
"bay" in trees; // false 반환 (인덱스에 위치한 값이 아니라
// 인덱스 자체를 지정해야 함)
"length" in trees; // true 반환 (length는 Array의 속성임)

// 내장 객체
"PI" in Math; // true 반환
var myString = new String("coral");
"length" in myString; // true 반환

// 사용자 정의 객체
var mycar = { make: "Honda", model: "Accord", year: 1998 };
"make" in mycar; // true 반환
"model" in mycar; // true 반환
```

#### `instanceof`

`instanceof` 연산자는 지정한 객체가
 지정한 객체 타입에 속하면 `true`를 반환합니다.

```js
objectName instanceof objectType;
```

`objectName`은 `objectType`과 비교할 객체의 이름이고, `objectType`은
 `Date`, `Array`와 같은 객체 타입입니다.

 런타임에 객체의 타입을 확인할 필요가 있으면 `instanceof` 연산자를 사용하세요. 예컨대 예외 처리 시에, 잡아낸
 예외의 타입에 따라 다른 방법으로 처리할 수 있습니다.

 다음의 코드는 `instanceof` 연산자를 사용해서 `theDay` 객체가 `Date` 객체인지 알아내는
 예제입니다. `theDay` 객체는 `Date` 객체이기 때문에, `if` 명령문 안의 내용이
 실행됩니다.

```js
var theDay = new Date(1995, 12, 17);
if (theDay instanceof Date) {
  // 실행할 명령문
}
```

### 연산자 우선순위

 연산자의 우선순위는 표현식을 평가할 때 연산자를 적용하는 순서를 결정합니다. 괄호를 사용하면 우선순위를 바꿀 수
 있습니다.

아래 표는 우선순위가 높은 순서에서 낮은 순서로 연산자를 나열합니다.

| 연산자 유형 | 개별 연산자 |
| --- | --- |
| 맴버 접근 | `.` `[]` |
| 인스턴스 호출/생성 | `()` `new` |
| 증감 | `!` `~` `-` `+` `++` `--` `typeof` `void` `delete` |
| 거듭제곱 | `**` |
| 곱하기/나누기 | `*` `/` `%` |
| 더하기/빼기 | `+` `-` |
| 비트 시프트 | `<<` `>>` `>>>` |
| 관계 | `<` `<=` `>` `>=` `in` `instanceof` |
| 동등/일치 | `==` `!=` `===` `!==` |
| 비트 AND | `&` |
| 비트 XOR | `^` |
| 비트 OR | `|` |
| 논리 AND | `&&` |
| 논리 OR | `||` |
| 조건 | `?:` |
| 할당 | `=` `+=` `-=` `**=` `*=` `/=` `%=` `<<=` `>>=` `>>>=` `&=` `^=` `|=` `&&=` `||=` `??=` |
| 쉼표 | `,` |

 각각의 연산자로 향하는 링크를 포함한 더 자세한 표는
 JavaScript 참고서에서 찾을 수
 있습니다.

표현식
---

표현식이란 어떤 값으로 이행하는 임의의 유효한 코드 단위를 말합니다.

모든 표현식은 구문이 유효하다면 어떤 값으로 이행하지만, 개념적으로는 두 가지 범주로 나뉩니다. 하나는 부수 효과가 있는 (예시: 변수에 값을 할당) 표현식이고, 다른 하나는 평가하면 어떤 값으로 이행하는 표현식입니다.

`x = 7`은 앞의 범주에 포함됩니다. 이 표현식은 `=` 연산자를 사용해서 값 7을 `x` 변수에
 할당합니다. 표현식 자체도 7로 평가됩니다.

`3 + 4`는 뒤쪽 범주로 들어갑니다. 이 표현식은 `+` 연산자를 사용해서 3과 4를 더하지만, 결과인 7을
 변수에 할당하지는 않습니다.

JavaScript의 표현식은 다음과 같은 범주로 구분할 수 있습니다.

* 산수: 숫자, 예컨대 3.14159로 평가됩니다. 보통 산술 연산자를 사용합니다.
* 문자열: 문자열, 예컨대 "프레디", "234" 등으로 평가됩니다. 보통 문자열 연산자를
 사용합니다.
* 논리: 참이나 거짓으로 평가됩니다. 대개 논리 연산자를 포함합니다.
* 일차 표현식: JavaScript의 키워드와 일반 표현식입니다.
* 좌변 표현식: 좌변 값은 할당의 목적지입니다.
### 일차 표현식

JavaScript의 키워드와 일반 표현식입니다.

#### `this`

 현재 객체를 참조하려면
 `this` 키워드를 사용하세요. 일반적으로
 `this`는 메서드의 호출 객체를 참조합니다. 다음과 같이, `this`를 점이나 대괄호 표기법과 함께
 사용하세요.

```js
this["propertyName"];
this.propertyName;
```

 최대와 최소 값을 받아서, 어떤 객체의 `value` 속성 유효성을 검증하는 `validate`라는 함수를
 가정해봅시다.

```js
function validate(obj, lowval, hival) {
  if (obj.value < lowval || obj.value > hival) console.log("잘못된 값!");
}
```

다음과 같이, 각 양식 요소의 `onchange` 이벤트 처리기에서 `validate`를 호출할 때 `this`를 사용해서 양식 요소의 참조를 제공할 수 있습니다.

```html
<p>18과 99 사이의 수를 입력:</p>
<input type="text" name="age" size="3" onchange="validate(this, 18, 99);" />
```

#### 그룹 연산자

 그룹연산자 `()`는 표현식 평가의 우선순위를 조절합니다. 예를 들어, 곱하기와 나누기보다 더하기와 빼기 연산을
 먼저 수행할 수 있습니다.

```js
var a = 1;
var b = 2;
var c = 3;

// 기본 우선순위에서는
a + b \* c; // 7
// 이런 순서로 평가함
a + (b \* c); // 7

// 우선순위 재정의
// 곱하기보다 더하기를 먼저 수행
(a + b) \* c; // 9

// ...하면 아래와 같음
a \* c + b \* c; // 9
```

### 좌변 표현식

좌변 값은 할당의 목적지입니다.

#### `new`

`new` 연산자를 사용하면 사용자 정의 객체
 타입이나 내장 객체 타입의 인스턴스를 생성할 수 있습니다. 아래 코드처럼 사용하세요.

```js
var objectName = new objectType([param1, param2, ..., paramN]);
```

#### super

super 키워드는 객체의 부모가 가진 함수를 호출할 때
 사용합니다. 예를 하나 들면, 클래스에서 부모의 생성자를
 호출해야 할 때 유용하게 쓸 수 있습니다.

```js
super([arguments]); // 부모 생성자 호출
super.functionOnParent([arguments]);
```

* « 이전
* 다음 »


함수 - JavaScript

함수
==

* « 이전
* 다음 »

함수는 JavaScript에서 기본 구성 요소 중 하나입니다. JavaScript의 함수는 작업을 수행하거나 값을 계산하는 명령문의 집합인 프로시저(procedure)와 비슷하지만, 프로시저가 함수로 쓰이려면 입력을 반드시 받아야 하고 입력과 명확한 관계가 있는 출력을 반환해야 합니다. 함수를 사용하려면 함수를 호출할 스코프 내에서 함수를 정의해야 합니다.

자세한 내용은 JavaScript 함수에 대한 문서를 참조하세요.

함수 정의
-----

### 함수 선언

함수 정의(또는 함수 선언)는 다음과 같은 `함수` 키워드로 구성되어 있습니다.

* 함수의 이름
* 함수의 매개변수들, 괄호로 묶고 쉼표로 구분합니다.
* 함수를 정의하는 JavaScript 문으로 중괄호로 묶습니다. `{ /* ... */ }`

예를 들어, 아래의 코드는 `square`라는 간단한 함수를 정의합니다.

```js
function square(number) {
  return number \* number;
}
```

함수 `square`은 `number`라는 하나의 매개변수를 가집니다. 이 함수는 함수의 매개변수(`number`)를 곱한 값을 반환하는 간단한 구문을 가지고 있습니다. `return` 문은 함수가 반환하는 값을 지정합니다.

```js
return number \* number;
```

기본적으로 `number`와 같은 매개변수는 **값**으로 함수에 전달됩니다. 따라서 함수 내의 코드가 함수에 전달된 매개 변수에 완전히 새로운 값을 할당하는 경우에도 변경 사항은 **전역적으로 또는 해당 함수를 호출한 코드에 반영되지 않습니다**.

객체를 매개 변수로 전달할 때 함수가 객체의 속성을 변경한다면 다음 예제와 같이 함수 외부에서 해당 변경 사항을 볼 수 있습니다.

```js
function myFunc(theObject) {
  theObject.make = "Toyota";
}

const mycar = {
  make: "Honda",
  model: "Accord",
  year: 1998,
};

// `x`의 값은 "Honda"입니다.
const x = mycar.make;

// `make` 속성은 함수에 의해 `Toyota`로 변경됩니다.
myFunc(mycar);

// `y`의 값은 "Toyota"입니다.
const y = mycar.make;
```

배열(Array)을 매개 변수로 전달할 때 함수가 배열의 값을 변경한다면 다음 예제와 같이 함수 외부에서 해당 변경 사항을 볼 수 있습니다.

```js
function myFunc(theArr) {
  theArr[0] = 30;
}

const arr = [45];

console.log(arr[0]); // 45
myFunc(arr);
console.log(arr[0]); // 30
```

### 함수 표현식

위의 함수 선언은 구문상 명령문이지만 함수는 **함수 표현식**(function expression)으로도 만들 수 있습니다.

이러한 함수를 **익명 함수**라 하며. 함수가 이름을 가질 필요는 없음을 의미합니다. 예를 들어, 제곱 함수 `square`은 다음과 같이 정의 할 수 있습니다.

```js
const square = function (number) {
  return number \* number;
};
const x = square(4); // `x` 의 값은 16 입니다.
```

하지만 함수 표현식에서도 함수의 이름을 지정할 수 있습니다. 함수가 자신을 참조할 수 있고, 디버거의 스택 추적에서 함수를 보다 쉽게 식별할 수 있습니다.

```js
const factorial = function fac(n) {
  return n < 2 ? 1 : n \* fac(n - 1);
};

console.log(factorial(3));
```

함수 표현식은 함수를 다른 함수의 매개변수로 전달할 때 편리합니다. 다음 예제에서는 첫 번째 매개변수로 함수, 두 번째 매개변수로 배열을 전달받는 `map` 함수를 보여줍니다.

```js
function map(fn, arr) {
  const result = new Array(arr.length);
  for (let i = 0; i < arr.length; i++) {
    result[i] = fn(arr[i]);
  }
  return result;
}
```

계속해서 아래의 코드에서 함수는 함수 표현식으로 정의된 함수를 받고 두 번째 인수로 전달된 배열의 각 요소에 대해 함수를 호출합니다.

```js
function map(fn, arr) {
  const result = new Array(arr.length);
  for (let i = 0; i < arr.length; i++) {
    result[i] = fn(arr[i]);
  }
  return result;
}

const fn = function (x) {
  return x \* x \* x;
};

const numbers = [0, 1, 2, 5, 10];
const cube = map(fn, numbers);
console.log(cube);
```

함수는 `[0, 1, 8, 125, 1000]`을 반환합니다.

JavaScript에서는 조건에 따라 함수를 정의할 수 있습니다. 예를 들어, 아래 함수는 `num`이 `0`인 경우에만 `myFunc`를 정의합니다.

```js
let myFunc;

if (num === 0) {
  myFunc = function (theObject) {
    theObject.make = "Toyota";
  };
}
```

여기에 설명된 것 이외에도 `eval()` (en-US) 처럼 `Function` 생성자를 사용하여 런타임 중 문자열에서 함수를 만들 수 있습니다.

객체의 속성이 함수인 것을 **메서드**(method)라고 합니다. 자세한 내용은 객체로 작업하기 문서를 참고하세요.

함수 호출
-----

함수를 정의해도 함수가 실행되는 것은 아닙니다. 함수를 정의하는 것은 함수의 이름과 수행할 작업을 지정하는 것입니다.

함수를 **호출**하면 지정된 매개 변수를 가지고 작업이 수행됩니다. 예를 들어 함수 `square`를 정의했다면 함수는 다음과 같이 호출할 수 있습니다.

```js
square(5);
```

위의 문장은 `5`라는 인수를 가지고 함수를 호출합니다. 함수는 이 함수의 실행문을 실행하고 값 `25`를 반환합니다.

함수는 호출될 때 스코프 내에 있어야 합니다. 그러나 함수의 선언은 이 예시 처럼 호이스팅 될 수 있습니다. (코드에서 호출 아래에 선언문이 있습니다.)

함수의 스코프는 함수가 선언된 곳(또는 최상위 레벨에 선언되었다면 전역)입니다.

```js
console.log(square); // square는 초기값으로 undefined를 가지고 호이스트된다.
console.log(square(5)); // TypeError: square는 함수가 아니다.
square = function (n) {
  return n \* n;
};
```

함수의 인수는 문자열과 숫자에 제한되지 않습니다. 여러분은 함수에 전체 객체를 전달할 수 있습니다. 객체로 작업하기에서 정의된 `showProps()` 함수는 인수로 객체를 받는 함수의 예입니다.

함수는 자신을 호출할 수 있습니다. 예를 들어, 팩토리얼을 재귀적으로 계산하는 함수가 있습니다.

```js
function factorial(n) {
  if (n == 0 || n == 1) return 1;
  else return n \* factorial(n - 1);
}
```

다음과 같이 1부터 5까지의 팩토리얼을 계산할 수 있습니다.

```js
const a = factorial(1); // a의 값은 1이 됩니다.
const b = factorial(2); // b의 값은 2가 됩니다.
const c = factorial(3); // c의 값은 6이 됩니다.
const d = factorial(4); // d의 값은 24가 됩니다.
const e = factorial(5); // e의 값은 120이 됩니다.
```

함수를 호출하는 다른 방법들이 있습니다. 함수를 동적 호출해야 하거나, 함수의 인수의 수가 다르거나, 함수 호출의 맥락이 런타임에서 결정된 특정한 객체로 설정해야 하는 경우가 자주 있습니다.

함수가 그 자체로 객체이며 이 객체는 차례로 메서드를(`Function` 객체를 참조) 가지고 있습니다. `apply()` 메서드를 사용하여 이 목표를 달성할 수 있습니다.

함수 호이스팅
-------

```js
console.log(square(5)); // `25`

function square(n) {
  return n \* n;
}
```

이 코드는 `square` 함수가 정의되기 전에 호출되었지만 오류 없이 실행됩니다. JavaScript 인터프리터가 전체 함수 선언을 현재 스코프의 최상단으로 끌어올려지기 때문에 위의 코드는 다음과 같습니다.

```js
// 모든 함수 선언이 스코프의 최상단으로 끌어올려집니다.
function square(n) {
  return n \* n;
}

console.log(square(5)); // `25`
```

함수 호이스팅은 **함수 선언**에만 적용됩니다. 함수 표현식에선 쓸 수 없습니다. 아래의 코드는 실행되지 않습니다.

```js
console.log(square); // ReferenceError: 초기화 되기 전에는 `square`에 접근할 수 없습니다.

const square = function (n) {
  return n \* n;
};
```

함수 스코프
------

함수 내에서 정의된 변수는 변수가 함수의 스코프에서만 정의되어 있기 때문에, 함수 외부의 어느 곳에서든 접근할 수 없습니다. 그러나 함수가 정의된 스코프 내에서 정의된 모든 변수나 함수에는 접근할 수 있습니다.

즉, 전역함수는 모든 전역 범위에 정의된 모든 변수에 접근할 수 있습니다. 다른 함수 내부에 정의 된 함수는 상위 함수에 정의된 모든 변수에 접근할 수 있고, 또한 상위 함수가 접근할 수 있는 다른 변수에도 접근할 수 있습니다.

```js
// 다음 변수는 전역 스코프에 있습니다.
const num1 = 20;
const num2 = 3;
const name = "Chamakh";

// 이 함수는 전역 스코프에 있습니다.
function multiply() {
  return num1 \* num2;
}

multiply(); // 60

// 중첩된 함수의 예시
function getScore() {
  const num1 = 2;
  const num2 = 3;

  function add() {
    return `${name} scored ${num1 + num2}`;
  }

  return add();
}

getScore(); // "Chamakh scored 5"
```

스코프와 함수 스택
----------

### 재귀

함수는 자신을 참조하고 호출할 수 있습니다. 함수가 자신을 참조하는 방법에는 세 종류가 있습니다.

1. 함수의 이름
2. `arguments.callee`
3. 함수를 참조하는 스코프 내 변수

예를 들어, 다음 함수의 정의를 고려해보세요.

```js
var foo = function bar() {
  // 여기에 구문이 들어갑니다.
};
```

함수 본문 내에서 다음은 모두 동일합니다.

1. `bar()`
2. `arguments.callee()`
3. `foo()`

자신을 호출하는 함수를 *재귀 함수*라고 합니다. 어찌 보면 재귀는 루프와 유사합니다. 둘 다 동일한 코드를 여러 번 실행하고, 무한 루프를 방지하는 조건 (여기서는 무한 재귀를 방지하는 조건)이 필요합니다.

예를 들어, 다음 루프는 재귀 함수로 바꿀 수 있고 그 함수를 호출할 수 있습니다.

```js
var x = 0;
while (x < 10) {
  // 루프 조건: "x < 10"
  // 뭔가 합니다.
  x++;
}
```

```js
function loop(x) {
  if (x >= 10)
    // "x >= 10" 는 탈출 조건 ("!(x < 10)"와 동일)
    return;
  // 뭔가 합니다.
  loop(x + 1); // 재귀 호출
}
loop(0);
```

그러나 일부 알고리즘은 단순 반복 루프가 될 수 없습니다. 예를 들어, DOM과 같은 트리 구조의 모든 노드를 가져오는 것은 재귀를 사용하는 편이 더 간편합니다.

```js
function walkTree(node) {
  if (node == null) {
    return;
  }
  // 노드를 가지고 뭔가 합니다.
  for (var i = 0; i < node.childNodes.length; i++) {
    walkTree(node.childNodes[i]);
  }
}
```

함수 `loop` 와 비교하여, 각 재귀 호출 자체는 많은 재귀 호출을 수행합니다.

재귀 알고리즘을 비재귀 알고리즘으로 변환하는 것은 가능하지만 논리가 더 복잡해지는 경우가 있고 스택을 사용해야 합니다.

사실 재귀도 스택(함수 스택)을 사용합니다. 스택과 같은 동작은 다음 예제에서 확인할 수 있습니다.

```js
function foo(i) {
  if (i < 0) {
    return;
  }
  console.log(`begin: ${i}`);
  foo(i - 1);
  console.log(`end: ${i}`);
}
foo(3);

// Output:

// begin:3
// begin:2
// begin:1
// begin:0
// end:0
// end:1
// end:2
// end:3
```

### 중첩된 함수와 클로저

함수 내에 함수를 끼워 넣을 수 있습니다. 중첩된(내부) 함수는 그것을 포함하는 (외부) 함수에서만 사용할 수 있습니다.

그것은 또한 *클로저*를 형성합니다. 클로저는 (표현식을 "닫는")변수를 바인딩하는 환경과 함께 자유 변수를 가질 수 있는 표현(일반적으로는 함수)입니다.

중첩 함수는 클로저이므로, 중첩된 함수는 그것을 포함하는 함수의 인수와 변수를 "상속"할 수 있음을 의미합니다. 즉, 내부 함수는 외부 함수의 스코프를 포함합니다.

요약하자면,

* 내부 함수는 외부 함수의 명령문에서만 액세스할 수 있습니다.
* 내부 함수는 클로저를 형성합니다: 외부 함수는 내부 함수의 인수와 변수를 사용할 수 없는 반면에, 내부 함수는 외부 함수의 인수와 변수를 사용할 수 있습니다.

다음 예는 중첩된 함수를 보여줍니다.

```js
function addSquares(a, b) {
  function square(x) {
    return x \* x;
  }
  return square(a) + square(b);
}
a = addSquares(2, 3); // 13
b = addSquares(3, 4); // 25
c = addSquares(4, 5); // 41
```

내부 함수는 클로저를 형성하기 때문에 외부 함수를 호출하고, 외부 및 내부 함수 모두에 인수를 지정할 수 있습니다.

```js
function outside(x) {
  function inside(y) {
    return x + y;
  }
  return inside;
}
// 어떤 값이 들어와도 3을 더하는 함수라 생각해보세요.
fn_inside = outside(3);
result = fn\_inside(5); // 8

result1 = outside(3)(5); // 8
```

### 변수의 보존

`inside`가 반환될 때 외부 함수의 인수 `x`가 보존된다는 점을 알 수 있습니다. 클로저는 그것을 참조하는 모든 스코프에서 인수와 변수를 보존해야 합니다. 각 호출은 잠재적으로 다른 인수를 제공할 수 있기 때문에, 클로저는 `outside`에 대하여 매번 새로 생성됩니다. 메모리는 그 무엇도 `inside`에 접근하지 않을 때만 해제됩니다.

변수의 보존은 일반 객체에서 참조를 저장하는 것과 별 차이점은 없지만, 사용자가 직접 참조를 설정하고 검사하는게 아니여서 명확하지 않은 경우가 많습니다.

### 다중 중첩 함수

함수는 다중 중첩될 수 있습니다. 예시입니다.

* 함수 `A`에는 함수 `B`가 포함되며, `B`에는 함수 `C`가 포함됩니다.
* 함수 `B`, `C`는 모두 클로저를 형성합니다. 그래서 `B`는 `A`에, `C`는 `B`를 접근 할 수 있습니다.
* 또한 `C`는 `A`에 접근 할 수 있는 `B`에도 접근할 수 있기 때문에 `A`에도 접근 할 수 있습니다.

따라서 클로저는 여러 스코프가 포함될 수 있으며, 이 스코프를 포함하는 함수의 스코프를 재귀적으로 포함합니다. 이것을 *스코프 체이닝*이라 합니다. (그것을 "체이닝"이라 하는 이유는 추후에 설명할 것입니다.)

다음 예를 살펴 보겠습니다.

```js
function A(x) {
  function B(y) {
    function C(z) {
      console.log(x + y + z);
    }
    C(3);
  }
  B(2);
}
A(1); // logs 6 (1 + 2 + 3)
```

이 예에서, `C`는 `B`의 `y`와 `A`의 `x`에 접근할 수 있고, 다음과 같이 됩니다.

1. `B`는 `A`를 포함하는 클로저를 형성합니다. 즉, `B`는 `A`의 인수와 변수를 엑세스할 수 있습니다.
2. `C`는 `B`를 포함하는 클로저를 형성합니다.
3. `C`의 클로저는 `B`를 포함하고 `B`의 클로저는 `A`를 포함하기 때문에, `C`의 클로저도 `A`를 포함합니다. 따라서 `C`는 `A`의 인수와 변수에 접근할 수 있다는 것을 의미합니다. 즉, `C`는 `A`의 스코프를 체이닝합니다.

하지만 `A`는 `C`의 변수인 `B`의 인수나 변수에 접근 할 수 없기 때문에 그 반대로는 불가능합니다. 따라서 `C`는 `B`에게만 비공개로 유지됩니다.

### 이름 충돌

클로저의 스코프에서 두 개의 인수 또는 변수의 이름이 같은 경우, *이름 충돌*이 발생합니다. 이 경우 더 안쪽 스코프가 우선순위를 갖습니다. 가장 바깥 스코프는 우선순위가 가장 낮은 반면에, 가장 안쪽 스코프는 가장 높은 우선순위를 갖습니다. 이것이 스코프 체인(scope chain)입니다. 체인의 첫번째는 가장 안쪽 스코프이고, 마지막은 가장 바깥 쪽 스코프입니다. 다음을 참고하세요.

```js
function outside() {
  var x = 10;
  function inside(x) {
    return x \* 2;
  }
  return inside;
}
result = outside()(20); // 10 대신 20을 반환합니다
```

이름 충돌은 `x * 2`를 반환하는 부분에서 발생하며, `inside`의 매개 변수 `x`와 `outside`의 매개 변수 `x` 사이에서 발생합니다. 여기에서 스코프 체이닝은 {`inside`, `outside`, `전역 객체`}로 이루어집니다. 따라서 `inside`의 `x`는 `outside`의 `x`보다 높은 우선순위를 갖게 되고, 20(`inside`의 `x`)이 10(`outside`의 `x`) 대신에 반환됩니다.

클로저
---

클로저는 JavaScript의 강력한 기능 중 하나입니다. JavaScript는 함수의 중첩(함수 안에 함수를 정의하는 것)을 허용하고, 내부 함수가 외부 함수 안에서 정의된 모든 변수와 함수(및 외부함수가 접근할 수 있는 다른 모든 변수와 함수들까지)에 대해 전체 접근 권한을 부여합니다.

하지만 외부 함수는 내부 함수에 정의된 변수와 함수에 접근 할 수 없습니다. 이는 내부 함수의 변수에 대한 일종의 캡슐화를 제공합니다.

또한, 내부 함수는 외부 함수의 스코프에 접근할 수 있기 때문에, 내부 함수가 외부 함수의 수명 보다 오래 생존하는 경우 외부 함수에서 선언된 변수나 함수는 외부 함수의 실행 기간보다 오래 유지됩니다. 내부 함수가 외부 함수 밖의 모든 스코프에서 사용 가능할 때 클로저가 생성됩니다.

```js
const pet = function (name) {
  // 외부 함수는 'name'이라 불리는 변수를 정의합니다.
  const getName = function () {
    return name; // 내부 함수는 외부 함수의 'name' 변수에 접근합니다.
  };
  return getName; // 내부 함수를 반환함으로써, 외부 스코프에 노출됩니다.
};

const myPet = pet("Vivie");

myPet(); // "Vivie"로 반환합니다.
```

클로저는 위 코드보다 더 복잡해 질 수도 있습니다. 외부 함수의 내부 변수를 다루는 메서드를 포함한 객체도 반환될 수도 있습니다.

```js
const createPet = function (name) {
  let sex;

  const pet = {
    // `setName(newName)`은 이 문맥에서
    // `setName: function (newName)`과 동일합니다.
    setName(newName) {
      name = newName;
    },

    getName() {
      return name;
    },

    getSex() {
      return sex;
    },

    setSex(newSex) {
      if (
        typeof newSex === "string" &&
        (newSex.toLowerCase() === "male" || newSex.toLowerCase() === "female")
      ) {
        sex = newSex;
      }
    },
  };

  return pet;
};

const pet = createPet("Vivie");
pet.getName(); // Vivie

pet.setName("Oliver");
pet.setSex("male");
pet.getSex(); // male
pet.getName(); // Oliver
```

위의 코드에서 외부 함수의 `name` 변수는 내부 함수에 접근할 수 있으며, 내부 함수를 통해서만 내부 변수에 접근할 수 있습니다. 내부 함수의 내부 변수는 외부 인수와 변수를 안전하게 저장하는 역할을 합니다. 이들은 내부 기능이 작동할 수 있게 '지속적'이고 '캡슐화된' 데이터를 보유합니다. 함수를 변수에 할당하거나 이름을 가질 필요가 없습니다.

```js
const getCode = (function () {
  const secureCode = "0]Eal(eh&2"; // 외부에서 접근할 수 없게 하고 싶은 변수.

  return function () {
    return secureCode;
  };
})();

getCode(); // `secureCode`를 반환합니다.
```

**참고:** 클로저를 사용할 때 주의해야 할 점이 있습니다!

둘러싸인 함수가 외부 스코프의 변수와 동일한 이름을 가진 변수를 정의하면, 외부 스코프의 변수에 다시 참조할 방법이 없습니다. (내부 스코프 변수는 프로그램이 내부 스코프를 종료할 때까지 외부 스코프 변수를 덮어씁니다. 이건 이름 충돌로 생각할 수 있습니다.)

```js
// 외부 함수는 변수 `name`을 정의합니다.
const createPet = function (name) {
  return {
    // 내부의 함수 또한 변수 `name`을 정의합니다.
    setName(name) {
      // 그럼 외부 함수에서 정의된 `name`에는 어떻게 접근해야 할까요?
      name = name;
    },
  };
};
```

인수(arguments) 객체 사용하기
---------------------

함수의 인수는 배열과 비슷한 객체로 처리가 됩니다. 함수 내에서는, 전달 된 인수를 다음과 같이 다룰 수 있습니다.

```js
arguments[i];
```

`i` 는 `0`으로 시작하는 순서 번호입니다. 따라서 함수에 전달된 첫 번째 인수는 `arguments[0]` 입니다. 총 인수의 개수는 `arguments.length` 에서 얻을 수 있습니다.

인수(`arguments`) 객체를 이용하면, 보통 함수에 정의된 개수보다 많은 인수를 넘겨주면서 함수를 호출할 수 있습니다. 이것은 얼마나 많은 인수가 함수로 넘겨질지 모르는 상황에서 유용한 경우가 많습니다. `arguments.length`를 함수에 실제로 넘겨받은 인수의 수를 알아낼 때 사용할 수 있고 , 각각의 인수에 인수(`arguments`) 객체를 이용하여 접근 할 수 있습니다.

예를 들어, 여러 문자열을 연결하는 함수를 생각해 봅시다. 이 함수의 유일한 형식 인수는 각 문자열을 구분해주는 문자를 나타내는 문자열입니다. 이 함수는 다음과 같이 정의됩니다.

```js
function myConcat(separator) {
  // 리스트를 초기화한다
  let result = "";
  // arguments를 이용하여 반복한다
  for (let i = 1; i < arguments.length; i++) {
    result += arguments[i] + separator;
  }
  return result;
}
```

어떤 개수의 인수도 이 함수로 넘겨줄 수 있고, 이 함수는 각각의 인수를 하나의 문자열 "리스트" 로 연결합니다.

```js
// "red, orange, blue, "를 반환합니다.
myConcat(", ", "red", "orange", "blue");

// "elephant; giraffe; lion; cheetah; "를 반환합니다.
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

// "sage. basil. oregano. pepper. parsley. "를 반환합니다.
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");
```

**참고:** 인수(arguments) 객체는 배열과 닮은 것이지 배열이 아닙니다. 인수 객체는 번호가 붙여진 인덱스와 길이 속성을 가지고 있다는 점에서 배열과 닮은 것입니다. 인수 객체는 배열을 다루는 모든 메서드를 가지고 있지 않습니다.

더 자세한 정보를 얻고 싶으시면 `Function` 객체 문서를 참고하세요.

함수의 매개변수
--------

ECMAScript 2015에서 추가된 매개변수 구문(parameter syntax)에는 기본 매개변수와 나머지 매개변수라는 두 가지 특수한 유형이 있습니다.

### 기본 매개변수

JavaScript에서 함수의 매개변수는 `undefined` 가 기본으로 설정됩니다. 하지만 경우에 따라 다른 기본값을 설정하는 것이 유용할 수 있습니다. 이것이 바로 기본 매개변수가 하는 일입니다.

이전에는 기본값 설정을 위해 함수 본문에서 매개변수 값을 테스트하고, `undefined`인 경우에 값을 할당하는 것이 보편적인 방식이었습니다.

다음 예제에서 `b` 매개변수에 아무 값도 주지 않으면, `a * b`를 계산 할 때 `b` 매개변수의 값은 `undefined`가 되므로 `multiply` 함수 호출은 `NaN`을 반환할 것입니다. 그러나 두번째 줄에서 이 문제가 방지됩니다.

```js
// ECMAScript 2015 이전
function multiply(a, b) {
  b = typeof b !== "undefined" ? b : 1;
  return a \* b;
}

multiply(5); // 5
```

디폴트 매개변수를 사용한다면 함수 본문에서 일일히 확인할 필요가 없습니다. 간단히 `b` 의 기본값으로 `1`을 넣어주면 됩니다.

```js
// ECMAScript 2015 이후
function multiply(a, b = 1) {
  return a \* b;
}

multiply(5); // 5
```

더 자세한 내용을 보고 싶으시면, 기본 매개변수 문서를 참조하세요.

### 나머지 매개변수

나머지 매개변수 구문을 사용하면 배열로 임의 개수의 인수를 나타낼 수 있습니다.

이 예제에서, 우리는 나머지 매개변수를 2번째 인수부터 마지막 인수까지 얻기 위하여 사용하였습니다. 그리고 우리는 첫번째 값으로 나머지 매개변수에 곱하였습니다. 이 예제는 다음 섹션에서 소개할 화살표(arrow) 함수 입니다.

```js
function multiply(multiplier, ...theArgs) {
  return theArgs.map((x) => multiplier \* x);
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
```

화살표 함수
------

화살표 함수 표현 (**뚱뚱한 화살표(fat arrow) 함수라고 알려진**)은 함수 표현식에 비해 짧은 문법을 가지고 있고 사전적으로 this 값을 묶습니다. 화살표 함수는 언제나 익명입니다. hacks.mozilla.org 블로그 포스트 "ES6 In Depth: Arrow functions" 를 참조하세요.

화살표 함수 도입에는 더 짧은 함수와 바인딩 되지않은 `this`라는 두 가지 요인이 영향을 미쳤습니다.

### 더 짧은 함수

몇몇 함수 패턴에서는 더 짧은 함수를 사용하는 것이 더 유용합니다. 한번 비교해보세요.

```js
var a = ["Hydrogen", "Helium", "Lithium", "Beryllium"];

var a2 = a.map(function (s) {
  return s.length;
});
console.log(a2); // logs [8, 6, 7, 9]

// 화살표 함수 사용
var a3 = a.map((s) => s.length);
console.log(a3); // logs [8, 6, 7, 9]
```

### 사전적 `this`

화살표 함수 이전 까지 모든 new 함수는 고유한 this 값을 정의(생성자의 경우 new 객체, 엄격 모드에서는 호출 되지 않음, 함수가 "객체 메서드"로 호출 되는 경우의 기본 객체 등)했습니다. 이런 방식은 객체 지향 프로그래밍 스타일과 잘 맞지 않았습니다.

```js
function Person() {
  // `Person()` 생성자는 `this`를 자신으로 정의합니다.
  this.age = 0;

  setInterval(function growUp() {
    // 느슨한 모드에서 `growUp()` 함수는 `this`를 전역 객체로 정의합니다.
    // 이는 `Person()` 생성자에 의해 정의된 `this`와 다릅니다.
    this.age++;
  }, 1000);
}

var p = new Person();
```

ECMAScript 3/5 에서 이 문제는 `this` 안의 값을 뒤덮을 수 있는 변수에 할당하면서 해결했습니다.

```js
function Person() {
  var self = this; // `self` 대신 `that`을 선택하는 사람도 있습니다.
  // 어느 한 쪽만 선택하고 그것만 사용하도록 하죠.
  self.age = 0;

  setInterval(function growUp() {
    // 콜백은 기대하는 객체의 `self` 변수를 참조합니다.
    self.age++;
  }, 1000);
}
```

또는 적절한 `this` 값이 `growUp()` 함수에 전달되도록, 바인딩된 함수를 만들 수도 있습니다.

화살표 함수에는 `this`가 없습니다. 화살표 함수를 포함하는 객체 값이 사용됩니다. 따라서 다음 코드에서 `setInterval`에 전달 된 함수 내의 `this` 값은 화살표 함수를 둘러싼 함수의 `this`와 같은 값을 갖습니다.

```js
function Person() {
  this.age = 0;

  setInterval(() => {
    // `this`는 person 객체를 가리킵니다.
    this.age++;
  }, 1000);
}

var p = new Person();
```

미리 정의된 함수들
----------

JavaScript에는 몇 가지 최상위 레벨의 내장 함수가 있습니다:

* `eval()`
    + : **`eval()`** 메소드는 문자열로 표현된 JavaScript 코드를 실행합니다.
* `uneval()` (en-US)
비표준

    + : **`uneval()`** 메소드는 `Object`의 소스코드를 표현하는 문자열을 만듭니다.
* `isFinite()`
    + : 전역 **`isFinite()`** 함수는 전달받은 값이 유한한지 결정합니다. 만약 필요하다면, 매개변수는 첫번째로 숫자로 변환됩니다.
* `isNaN()`
    + : **`isNaN()`** 함수는 `NaN`인지 아닌지 결정합니다.

    **참고:** `isNaN` 함수 안의 강제 변환은 흥미로운 규칙을 가지고 있습니다.

    `Number.isNaN()`을 이용해 값이 NaN인지 확인할 수 있습니다.

    ECMAScript6에서 정의된 값이 숫자값이 아닌 경우에는 `typeof`를 사용할 수도 있습니다.
* `parseFloat()`
    + **`parseFloat()`** 함수는 문자열 인수 값을 해석하여 부동소숫점 수를 반환합니다.
* `parseInt()`
    + **`parseInt()`** 함수는 문자열 인수 값을 수학적인 수 체계에 따라 해석하여 특정한 진법의 정수를 반환합니다.
* `decodeURI()`
    + **`decodeURI()`** 함수는 사전에 `encodeURI`을 통해 만들어지거나 비슷한 과정을 통해 만들어진 URI(Uniform Resource Identifier) 를 해독합니다.
* `decodeURIComponent()`
    + **`decodeURIComponent()`** 함수는 인코딩으로 이전에 생성된 URI(`encodeURIComponent`)를 디코딩합니다. URI(Uniform Resource Identifier) 또는 유사한 루틴을 사용합니다.
* `encodeURI()`
    + **`encodeURI()`** 메소드는 URI(Uniform Resource Identifier)를 각 인스턴스의 특정한 문자를 한 개, 두 개, 세 개 또는 네 개의 UTF-8인코딩으로 나타내어지는 연속된 확장문자들과 바꾸는 방법으로 부호화 합니다. (두 "surrogate"문자로 구성된 문자들은 오직 네 개의 연속된 확장문자 입니다)
* `encodeURIComponent()`
    + **`encodeURIComponent()`** 메소드는 URI(Uniform Resource Identifier) 컴포넌트를 각 인스턴스의 특정한 문자를 한개, 두 개, 세 개 또는 네 개의 UTF-8 인코딩으로 나타내어지는 연속된 확장문자들과 바꾸는 방법으로 부호화 합니다.(두 "surrogate"문자로 구성된 문자들은 오직 네개의 연속된 확장문자 입니다.)
* `escape()` (en-US)
지원이 중단되었습니다

    + 곧 사라질 **`escape()`** 메소드는 한 문자열에서 특정 문자들이 16진 확장 비트열로 바뀌어진 문자열로 계산합니다. `encodeURI` 또는 `encodeURIComponent` 를 사용하세요.
* `unescape()` (en-US)
지원이 중단되었습니다

    + 곧 사라질 **`unescape()`** 메소드는 문자열에서 확장 비트열이 확장 비트열이 나타내는 문자로 바뀌어진 문자열로 계산합니다. `escape` (en-US)에서 확장 비트열이 소개될 것입니다. `unescape()` 메소드가 곧 사라지기 때문에, `decodeURI()` or `decodeURIComponent` 를 대신 사용하세요.
* « 이전
* 다음 »


객체로 작업하기 - JavaScript

객체로 작업하기
========

* « 이전
* 다음 » (en-US)

JavaScript는 간단한 객체 기반 패러다임 위에 만들어졌습니다. 객체는 속성의 컬렉션이고, 속성은 이름('키')과 값 사이의 연결 관계입니다. 속성의 값이 함수인 경우에는 메서드라고 부릅니다. 브라우저 안에 미리 정의된 객체 뿐만 아니라 여러분이 직접 객체를 정의할 수도 있습니다. 이번 장에서는 객체, 속성, 함수, 메서드 사용법과 함께 직접 새로운 객체를 생성하는 법을 알아봅니다.

객체 개요
-----

JavaScript의 객체는, 다른 프로그래밍 언어에서와 마찬가지로 현실 세계에서 객체(사물)라고 부르는 것과 비교할 수 있습니다. JavaScript 객체의 개념은 현실에서 인식 가능한 사물로 이해가 가능합니다.

JavaScript에서 객체는 속성과 타입을 가진 독립적인 개체(entity)입니다. 현실의 컵과 비교해본다면, 컵은 색, 디자인, 무게, 소재 등의 속성을 가진 객체라고 할 수 있습니다. 마찬가지로 JavaScript 객체 역시 특징을 결정짓는 속성을 가질 수 있습니다.

객체와 속성
------

JavaScript 객체는 자신과 연관된 속성들을 가집니다. 객체의 속성은 객체에 붙은 변수라고 설명할 수 있겠습니다. 객체의 속성은 일반적인 JavaScript 변수와 똑같은데, 다만 객체에 붙어있다는 점만 다릅니다. 속성에 접근할 땐 간단한 마침표 표기법을 사용합니다.

```js
objectName.propertyName;
```

다른 모든 JavaScript 변수와 마찬가지로, 객체의 이름(아마도 그냥 변수)과 속성의 이름 모두 대소문자를 구별합니다. 객체에 속성을 정의하려면 값을 할당하세요. 예를 들기 위해 `myCar`라는 객체를 생성하고, `make`, `model`, `year`라는 이름의 속성을 추가해보겠습니다.

```js
const myCar = new Object();
myCar.make = "Ford";
myCar.model = "Mustang";
myCar.year = 1969;
```

위의 코드는, 중괄호(`{}`) 안에 쉼표로 구분한 속성 이름과 값의 목록으로 나타내는 **객체 초기자**로 나타낼 수도 있습니다.

```js
const myCar = {
  make: "Ford",
  model: "Mustang",
  year: 1969,
};
```

객체에 할당되지 않은 속성은`undefined`입니다. (`null`이 아닙니다)

```js
myCar.color; // undefined
```

JavaScript의 객체 속성은 대괄호 표기법(속성 접근자)을 사용해 접근할 수도 있습니다. 객체를 때때로 '연관 배열'(associative array)이라고 부르기도 하는데, 속성은 자신에 접근할 수 있는 문자열 값과 연관되어 있기 때문입니다. 그러므로 `myCar` 객체의 속성에 아래와 같이 접근할 수도 있을 것입니다.

```js
myCar["make"] = "Ford";
myCar["model"] = "Mustang";
myCar["year"] = 1969;
```

객체 속성 이름은 유효한 JavaScript 문자열 혹은 문자열로 변환 가능한 것이면 모두 가능하며, 빈 문자열도 여기 포함됩니다. 그러나 유효한 JavaScript 식별자가 아닌 이름(공백이나 붙임표, 숫자로 시작하는 이름)을 가진 속성은 대괄호 표기법으로만 접근할 수 있습니다. 대괄호 표기법은 속성 이름이 동적으로 정해지는 경우, 즉 런타임 시점까지 알 수 없는 경우 유용합니다. 아래 예제를 살펴보세요.

```js
// 네 개의 변수를 쉼표로 구분해서
// 한 번에 생성하고 할당
const myObj = new Object(),
  str = "myString",
  rand = Math.random(),
  obj = new Object();

myObj.type = "마침표 구문";
myObj["date created"] = "공백을 포함한 문자열";
myObj[str] = "문자열 값";
myObj[rand] = "무작위 수";
myObj[obj] = "객체";
myObj[""] = "빈 문자열까지";

console.log(myObj);
```

모든 대괄호 표기법 안의 키는 심볼이 아닌 경우 문자열로 변환되는 점을 기억하세요. JavaScript 객체의 속성 이름(키)은 문자열이나 심볼 뿐이기 때문입니다(클래스 필드 제안서로 인해 비공개 속성 이름도 추가되겠지만, 비공개 속성 접근에는 `[]` 형식을 사용하지 않을 것입니다). 예를 들어, 위 코드의 `myObj`에 `obj`를 키로 추가하는 부분에서 JavaScript는 `obj.toString()` 메서드를 호출한 결과 문자열을 새로운 키로 사용합니다.

속성 접근은 변수에 저장된 문자열 값으로도 가능합니다.

```js
let propertyName = "make";
myCar[propertyName] = "Ford";

propertyName = "model";
myCar[propertyName] = "Mustang";
```

대괄호 표기법은 `for...in`과 함께 객체의 열거 가능한 속성을 순회할 때도 사용할 수 있습니다. 아래 함수는 이 방법의 동작 방식을 보이는 예제로, 객체와 객체 이름을 매개변수로 제공하면 객체의 속성들을 출력합니다.

```js
function showProps(obj, objName) {
  let result = "";
  for (let i in obj) {
    // obj.hasOwnProperty()를 사용해서 객체의 프로토타입 체인에 존재하지 않는 속성을 제외
    if (obj.hasOwnProperty(i)) {
      result += `${objName}.${i} = ${obj[i]}\n`;
    }
  }
  console.log(result);
}
```

`showProps(myCar, 'myCar')`를 호출하면 이렇게 출력됩니다.
```

myCar.make = Ford
myCar.model = Mustang
myCar.year = 1969
```

객체 속성 나열하기
----------

객체 속성을 나열하거나 순회하는 방법에는 세 가지 내장된 방법이 있습니다.

* for...in 반복문. 이 방법은 객체와 객체의 프로토타입 체인에 존재하는 모든 열거 가능한 속성을 순회합니다.
 이 방법은 객체와 객체의 프로토타입 체인 상의 열거 가능한 모든 프로퍼티를 순회한다.
* `Object.keys(o)`. 이 방법은 `o` 객체 자신만의(프로토타입 체인을 제외한) 열거 가능한 속성 이름("키")을 배열로 반환합니다.
* `Object.getOwnPropertyNames(o)`. 이 방법은 `o` 객체 자신만의 모든(열거 불가능하더라도) 속성 이름("키")을 배열로 반환합니다.

'숨은' 속성(프로토타입 체인 상의 속성 중, 같은 이름의 속성이 체인에서 앞서 존재해서 가려진 속성)을 나열하는 내장 방법은 없습니다. 그러나 밑의 함수를 사용하면 가능합니다.

```js
function listAllProperties(o) {
  let objectToInspect = o;
  let result = [];

  while (objectToInspect !== null) {
    result = result.concat(Object.getOwnPropertyNames(objectToInspect));
    objectToInspect = Object.getPrototypeOf(objectToInspect);
  }

  return result;
}
```

객체 생성하기
-------

JavaScript에는 미리 정의된 객체가 여럿 존재합니다. 여기에 더해 여러분만의 객체도 생성할 수 있습니다. 객체 생성에는 객체 초기자를 사용할 수도 있고, 생성자 함수를 정의한 후에 `new` 연산자와 함께 호출해서 객체 인스턴스를 생성할 수도 있습니다.

### 객체 초기자 사용하기

객체 초기자의 사용은 종종 "리터럴 표기에 의한 객체 생성"이라고 불립니다. "객체 초기자"(Object initializer)라는 단어는 C++에서 사용하는 용어와 같습니다.

객체 초기자를 사용한 객체 생성 구문은 다음과 같습니다.

```js
const obj = {
  property\_1: value_1, // 속성의 값은 식별자일 수도 있고
  2: value_2, // 숫자일 수도 있고
  // ...,
  "property n": value_n, // 문자열일 수도 있음
};
```

위 코드에서 `obj`는 새로운 객체의 이름이고, 콜론 앞에 위치한 속성 이름은 식별자(이름, 숫자, 스트링 리터럴), 각각의 `value_i`는 속성 이름에 할당할 표현식입니다. `obj`와 할당 구문은 선택 사항으로, 이 객체를 다른 곳에서 참조하지 않아도 되면 변수에 할당할 필요도 없습니다. (명령문을 기대하는 곳에 객체 리터럴을 배치하려면 블록 선언문으로 잘못 읽는 경우가 없도록 괄호로 감싸줄 필요가 있습니다)

객체 초기자는 표현식이며 자신이 속한 선언문이 실행될 때마다 새로운 객체를 생성합니다. 같은 초기자가 생성한 객체끼리라도 구별 가능하며 서로 비교했을 때 거짓을 반환합니다. 초기자가 생성하는 객체는 마치 `new Object()` 호출을 한 것과 같이 생성됩니다. 즉 객체 초기자 표현식의 결과 객체들은 `Object`의 인스턴스입니다.

다음 선언문은 `cond`가 참일 때만 객체를 생성해서 변수 `x`에 할당합니다.

```js
let x;
if (cond) {
  x = { greeting: "안녕하세요" };
}
```

다음 예제는 `myHonda`를 세 개의 속성과 함께 생성합니다. `engine` 속성 역시 자신만의 속성을 가진 객체임에 주목하세요.

```js
const myHonda = {
  color: "red",
  wheels: 4,
  engine: { cylinders: 4, size: 2.2 },
};
```

객체 초기자로 배열을 생성할 수도 있습니다. 배열 리터럴을 참고하세요.

### 생성자 함수 사용하기

다른 방법으로, 다음의 두 단계를 통해 객체를 생성할 수 있습니다.

1. 생성자 함수를 작성해서 객체 타입을 정의합니다. 객체 타입 이름의 첫 글자는, 그럴만한 이유로, 대문자로 시작하는 관례가 있습니다.
2. `new` 연산자를 사용해 객체 인스턴스를 생성합니다.

객체 타입을 정의하려면 타입 이름, 속성, 메서드를 지정하는 함수를 생성하세요. 예를 들어 차량을 표현하기 위한 객체 타입을 정의한다고 합시다. 그러면 객체 타입의 이름은 `Car`가 될 것이고, 브랜드와 모델, 연식 속성이 필요할 것입니다.

```js
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
```

함수에 전달한 값을 객체 속성으로 할당하기 위해 `this`를 사용하는 것에 주목하세요.

이제 `mycar` 객체를 생성할 수 있습니다.

```js
var mycar = new Car("Eagle", "Talon TSi", 1993);
```

이 선언문은 `mycar`를 생성한 후 매개변수로 지정한 값을 속성에 할당합니다. 이후 `mycar.make`는 문자열 "Eagle", `mycar.year`는 정수 1993 등을 가지게 됩니다.

`new`를 사용한 호출로 몇 개의 `Car` 객체라도 만들 수 있습니다.

```js
const kenscar = new Car("Nissan", "300ZX", 1992);
const vpgscar = new Car("Mazda", "Miata", 1990);
```

객체는 다른 객체도 속성으로 가질 수 있습니다. `Person`이라는 객체를 다음과 같이 정의한다고 하겠습니다.

```js
function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
}
```

두 개의 새로운 `Person` 객체도 인스턴스화 했다고 합시다.

```js
const rand = new Person("Rand McKinnon", 33, "M");
const ken = new Person("Ken Jones", 39, "M");
```

이제 `Car`의 정의를, `Person` 객체를 갖는 `owner` 속성을 추가하도록 수정할 수 있습니다.

```js
function Car(make, model, year, owner) {
  this.make = make;
  this.model = model;
  this.year = year;
  this.owner = owner;
}
```

그 후에, 새로운 객체 인스턴스를 생성할 땐 이렇게 할 수 있습니다.

```js
const car1 = new Car("Eagle", "Talon TSi", 1993, rand);
const car2 = new Car("Nissan", "300ZX", 1992, ken);
```

문자열 리터럴이나 정수 값이 아니라 객체 `rand`와 `ken`을 소유자 매개변수로 지정했음을 볼 수 있습니다. 이제 `car2`의 소유자 이름을 알려면 이렇게 속성에 접근합니다.

```js
car2.owner.name;
```

이미 정의된 객체에도 속성을 추가할 수 있습니다. 예를 들어,

```js
car1.color = "black";
```

위의 코드는 `car1`에 `color` 속성을 추가한 후 값으로 `'black'`을 할당하고 있습니다. 그러나 이 방법은 다른 객체에 영향을 주지 않습니다. 같은 타입의 모든 객체에 값을 추가하려면 `Car` 객체 타입의 정의를 수정해야 합니다.

### `Object.create` 메서드 사용하기

`Object.create()` 메서드로 객체를 생성할 수도 있습니다. 이 메서드는 생성자 함수 정의 없이도 생성할 객체의 프로토타입을 지정할 수 있으므로 유용할 수도 있습니다.

```js
// 동물 속성과 메서드 캡슐화
const Animal = {
  type: "무척추동물", // 속성 기본 값
  displayType: function () {
    // 동물 종류를 출력할 메서드
    console.log(this.type);
  },
};

// animal1이라는 이름의 동물 타입 객체 생성
const animal1 = Object.create(Animal);
animal1.displayType(); // 출력: 무척추동물

// fish라는 이름의 동물 타입 객체 생성
const fish = Object.create(Animal);
fish.type = "물고기";
fish.displayType(); // 출력: 물고기
```

상속
--

JavaScript의 모든 객체는 적어도 하나의 다른 객체를 상속합니다. 상속 대상 객체를 프로토타입이라고 부르고, 상속한 속성은 생성자의 `prototype` 객체에서 찾을 수 있습니다. 상속과 프로토타입 체인에서 자세히 알아보세요.

객체 속성 인덱싱
---------

객체의 속성에 접근할 땐 속성 이름이나 인덱스로 접근할 수 있습니다. 처음에 속성을 이름으로 정의했으면 그 속성은 항상 이름으로만 참조해야 하고, 처음에 인덱스로 정의했으면 항상 인덱스로만 참조해야 합니다.

이 제한은 (위에서 `Car` 객체 타입으로 했던 것과 같이) 생성자 함수로 객체와 속성을 생성할 때, 그리고 (`myCar.color = 'red'`처럼) 각각의 속성을 명시적으로 정의할 때 모두 해당됩니다. 만약 객체의 속성을 `myCar[5] = '25 mpg'`처럼 인덱스로 정의했으면 이후에 참조할 때도 `myCar[5]`로 해야 합니다.

다만 HTML 요소를 나타내는, `document.forms`와 같은 유사 배열 객체에는 예외로, 문서 상의 순서를 나타내는 인덱스로 접근할 수도 있고 (정의된 경우) 이름으로 접근할 수도 있습니다. 즉 문서의 두 번째 `<form>` 태그가 `name="myForm"` 특성을 가지고 있다면 `document.forms[1]`, `document.forms['myForm']`, `document.forms.myForm` 모두 사용할 수 있습니다.

객체 속성 정의하기
----------

`prototype` 속성을 사용하면 이전에 정의된 객체 타입에도 속성을 추가할 수 있습니다. 이렇게 정의된 속성은 해당 타입의 모든 인스턴스가 공유합니다. 아래 코드는 모든 `Car` 타입 객체에 `color` 속성을 추가한 후, `car1` 객체에 그 속성의 값을 할당하는 모습입니다.

```js
Car.prototype.color = null;
car1.color = "black";
```

상속과 프로토타입 체인에서 더 자세한 정보를 알아보세요.

메서드 정의
------

메서드(method)는 객체와 연관된 함수, 다른 말로 하자면 객체 속성 중 함수인 것을 말합니다. 메서드는 다른 함수와 똑같이 정의하지만, 객체 속성에 할당한다는 점이 다릅니다. 메서드 정의에서 더 자세한 정보를 알아보세요. 메서드의 예시는 다음과 같습니다.

```js
objectName.methodName = functionName;

const myObj = {
  myMethod: function(params) {
    // 뭔가 하기...
  }

  // 이렇게도 동작해요
  myOtherMethod(params) {
    // 뭔가 다른거 하기...
  }
};
```

`objectName`은 이미 존재하는 객체, `methodName`은 할당하려는 메서드, `functionname`은 함수의 이름입니다.

객체의 맥락으로 메서드를 호출하려면 다음과 같이 사용하세요.

```js
object.methodName(params);
```

객체 생성자 함수에 메서드 정의를 포함하면 메서드 타입에 메서드를 정의할 수 있습니다. 앞에서 정의했던 `Car` 객체의 속성을 정리해서 출력하는 함수를 정의해봅시다.

```js
function displayCar() {
  const result = `아름다운 ${this.year} ${this.make} ${this.model}`;
  prettyPrint(result);
}
```

`prettyPrint`는 가로 구분선을 그리고 문자열을 출력하는 함수라고 가정하겠습니다. `this`를 사용해서 메서드가 속한 객체를 참조하는 모습을 확인하세요.

이제 이 함수가 `Car`의 메서드가 되려면 객체 정의에 아래의 코드를 추가하세요.

```js
this.displayCar = displayCar;
```

따라서 `Car` 정의의 전체 내용은 다음과 같아집니다.

```js
function Car(make, model, year, owner) {
  this.make = make;
  this.model = model;
  this.year = year;
  this.owner = owner;
  this.displayCar = displayCar;
}
```

이제 각각의 객체에 대해 `displayCar` 메서드를 호출할 수 있습니다.

```js
car1.displayCar();
car2.displayCar();
```

`this`로 객체 참조하기
---------------

JavaScript에는 `this`라는 특별한 키워드가 있습니다. `this`를 사용하면 메서드 내에서 현재 객체를 참조할 수 있습니다. 예를 들어 `Manager`and `Intern` 두 객체를 가정해보겠습니다. 각각의 객체는 자신만의 `name`, `age`, `job` 속성을 가집니다. 아래의 `sayHi()` 함수에서 `this.name`에 접근하는 것에 주목하세요. 이 함수를 각각의 객체에 할당한 후 호출하면 `'안녕하세요, 제 이름은'`, 자신의 `name`, 그리고 `'입니다.'`로 구성된 문자열을 출력하게 됩니다.

```js
const Manager = {
  name: "John",
  age: 27,
  job: "소프트웨어 엔지니어",
};
const Intern = {
  name: "Ben",
  age: 21,
  job: "소프트웨어 엔지니어 인턴",
};

function sayHi() {
  console.log(`안녕하세요, 제 이름은 ${this.name}입니다.`);
}

// 두 객체 모두에 sayHi 함수 추가
Manager.sayHi = sayHi;
Intern.sayHi = sayHi;

Manager.sayHi(); // 안녕하세요, 제 이름은 John 입니다.'
Intern.sayHi(); // 안녕하세요, 제 이름은 Ben 입니다.'
```

위의 `this`는 자신이 속한 객체를 참조합니다. 이제 나이를 알려주는 문장을 콘솔에 기록하는 함수 `howOldAmI()`를 생성해볼 수 있을 것입니다.

```js
function howOldAmI() {
  console.log(`저는 ${this.age}살입니다.`);
}
Manager.howOldAmI = howOldAmI;
Manager.howOldAmI(); // 저는 27살입니다.
```

접근자와 설정자 정의하기
-------------

접근자는 특정 속성의 값을 반환하는 메서드입니다. 설정자는 특정 속성의 값을 설정하는 메서드입니다. 접근자와 설정자는 속성 추가를 지원하는 객체라면, 사전정의된 코어 객체와 사용자 정의 객체 모두에 추가할 수 있습니다.

접근자와 생성자는...

* 객체 초기자를 사용해 정의하거나,
* 나중에 필요한 시점에 아무 객체에나 접근자/설정자 추가 메서드로 추가할 수 있습니다.

객체 초기자를 사용해 접근자와 설정자를 정의하는 경우 필요한 것은, 접근자 메서드 이름 앞에 `get`, 설정자 메서드 이름 앞에 `set`을 붙이는 것 뿐입니다. 물론 모든 접근자 메서드는 아무 매개변수도 받지 않고, 설정자 메서드는 정확히 하나의 매개변수(설정할 값)만 받는다는 제한은 있습니다.

```js
const o = {
  a: 7,
  get b() {
    return this.a + 1;
  },
  set c(x) {
    this.a = x / 2;
  },
};

console.log(o.a); // 7
console.log(o.b); // 8 <-- 이 시점에 get b() 메서드 실행
o.c = 50; // <-- 이 시점에 set c(x) 메서드 실행
console.log(o.a); // 25
```

여기서 `o` 객체의 속성은 다음과 같습니다.

* `o.a` — 숫자
* `o.b` — `o.a`에 1을 더한 값을 반환하는 접근자
* `o.c` — `o.c`에 설정한 값의 반을 `o.a`에 설정하는 설정자

다만 객체 리터럴에서 "[gs]et *property*()"을 사용해 정의한 접근자와 설정자의 함수 이름은 접근자와 설정자의 실제 이름이 아닙니다. `[gs]et propertyName(){ }` 구문이 생긴 모습 때문에 그렇게 보일 수는 있지만요.

접근자와 설정자는 또한 객체 생성 이후에 `Object.defineProperties()` 메서드로 추가할 수 있습니다. 이 메서드의 첫 번째 매개변수는 접근자와 설정자를 추가할 객체고, 두 번째 매개변수는 속성 이름이 추가할 접근자/설정자 이름, 속성 값은 접근자/설정자를 정의하는 객체입니다. 다음은 바로 위 예제에서 정의한 것과 같은 접근자/생성자를 추가하는 코드입니다.

```js
const o = { a: 0 };

Object.defineProperties(o, {
  b: {
    get: function () {
      return this.a + 1;
    },
  },
  c: {
    set: function (x) {
      this.a = x / 2;
    },
  },
});

o.c = 10; // 설정자 실행, a 속성에 10 / 2 = 5 할당
console.log(o.b); // 접근자 실행, a + 1 = 6 반환
```

두 형태 중 선택할 형태는 여러분의 프로그래밍 스타일과 현재 해야 하는 작업에 달렸습니다. 프로토타입을 정의할 때 이미 객체 초기자를 사용 중이라면 아마 대부분 간결함과 자연스러움으로 인해 처음 형태를 선택하게 될 것입니다. 그러나 객체 프토토타입을 따로 작성하지 않은 등의 이유로 나중에 접근자와 설정자를 추가해야 할 필요가 있으면 두 번째가 유일한 방법입니다. 두 번째 방법은 JavaScript의 동적인 모습을 가장 잘 나타내는 표현 중 하나겠지만, 읽거나 이해하기 힘든 코드를 만들기도 합니다.

속성 삭제
-----

상속한 속성이 아닌 경우 `delete` (en-US) 연산자로 속성을 삭제할 수 있습니다.

```js
// a와 b 두 속성의 새로운 객체 생성
const myobj = new Object();
myobj.a = 5;
myobj.b = 12;

// a 속성을 제거해서 b 속성만 남김
delete myobj.a;
console.log("a" in myobj); // 출력: false
```

객체 비교
-----

JavaScript의 객체는 참조 타입입니다. 두 개의 객체는 서로 같은 속성을 갖더라도, 자신들끼리는 절대 같지 않으며 오직 자기 자신과의 비교만 참을 반환합니다.

```js
// 두 개의 변수, 두 개의 같은 속성을 가진 서로 다른 객체
const fruit = { name: "사과" };
const fruitbear = { name: "사과" };

fruit == fruitbear; // false 반환
fruit === fruitbear; // false 반환
```

```js
// 두 개의 변수, 하나의 객체
const fruit = { name: "사과" };
const fruitbear = fruit; // fruit 객체 참조를 fruitbear에 할당

// fruit과 fruitbear가 같은 객체를 가리킴
fruit == fruitbear; // true 반환
fruit === fruitbear; // true 반환

fruit.name = "포도";
console.log(fruitbear); // 출력: { name: "사과" }가 아니라 { name: "포도" }
```

비교 연산에 대한 더 자세한 정보는 동일 연산자에서 더 알아보세요.

같이 보기
-----

* 더 깊게 들어가려면 JavaScript 객체 모델의 세부 사항 (en-US)을 읽어보세요.
* 객체를 생성하는 다른 방법인 ECMAScript 2015 클래스에 대해 알아보려면 JavaScript 클래스 참고서를 방문하세요.
* « 이전
* 다음 » (en-US)


텍스트 서식 - JavaScript

텍스트 서식
======

* « 이전
* 다음 »

이 장에서는 JavaScript에서 문자열과 텍스트로 작업하는 방법을 소개합니다.

문자열
---

JavaScript의 문자열 유형은 원문의 데이터를 나타내는데 사용됩니다. 이는 16비트 부호 없는 정수 값(UTF-16 code units)의 "요소" 집합입니다. String의 각 요소(문자)는 String에서 하나의 위치를 차지합니다. 첫 번째 요소는 인덱스 0 다음은 인덱스 1 등등... 문자열의 길이는 요소의 수와 같습니다. 문자열 리터럴 또는 문자열 객체를 사용하여 문자열을 만들 수 있습니다.

주의 : 이페이지를 수정한다면 MDN bug 857438이 해결될 때 까지 U+FFFF이상의 문자를 포함하지 마세요. ( https://bugzilla.mozilla.org/show\_bug.cgi?id=857438 ).

### 문자열 리터럴

작은따옴표나 큰따옴표를 사용하여 간단한 문자열을 만들 수 있습니다:

```js
'foo';
"bar";
```

보다 많은 문자열을 이스케이프 시퀀스를 사용하여 만들 수 있습니다

#### 16진수 이스케이프 시퀀스

\x 뒤에 수는 16진수로 해석(interpreted)됩니다.

```js
"\xA9"; // "©"
```

#### 유니코드 이스케이프 시퀀스

유니코드 이스케이프 시퀀스는 \u 다음에 적어도 네 개의 16진수 숫자(digit)를 필요로 합니다.

```js
"\u00A9"; // "©"
```

#### 유니코드 코드 포인트 이스케이프

ECMAScript 6의 새로운 기능. 유니 코드 포인트 이스케이프를 사용하면 16 진수를 사용하여 모든 문자를 이스케이프 처리 할 수 있으므로 최대 `0x10FFFF`의 유니 코드 코드 포인트를 사용할 수 있습니다. 간단한 유니 코드 이스케이프를 사용하면 동일한 결과를 얻기 위해서 서로 게이트를 별도로 작성해야하는 경우가 있습니다.

`String.fromCodePoint()` (en-US) 나 `String.prototype.codePointAt()` (en-US)를 참고하세요.

```js
"\u{2F804}";

// the same with simple Unicode escapes
"\uD87E\uDC04";
```

### 문자열 개체

`문자열` 개체는 문자열 기본 데이터 형식의 래퍼입니다.

```js
var s = new String("foo"); // Creates a String object
console.log(s); // Displays: { '0': 'f', '1': 'o', '2': 'o'}
typeof s; // Returns 'object'
```

여러분은 문자열 리터럴 값에 문자열 개체의 방법 중 하나를 호출 할 수 있습니다.—JavaScript가 자동으로 문자열 리터럴을 임시 문자열 개체로 변환하고, 메서드를 호출하고, 그리고나서 임시 문자열 개체를 삭제합니다. 또한, `String.length` 속성을 문자열 리터럴과 함께 사용할 수 있습니다.

특별히 `String` 개체를 사용할 필요가 없지 않는 한, `String` 개체는 직관에 반하는 행동을 할 수 있기 때문에 여러분은 string 리터럴을 사용해야합니다. 예를들어:

```js
var s1 = "2 + 2"; // Creates a string literal value
var s2 = new String("2 + 2"); // Creates a String object
eval(s1); // Returns the number 4
eval(s2); // Returns the string "2 + 2"
```

문자열 개체는 문자열의 문자 수를 나타내는 하나의 속성, 길이를 갖습니다. 예를 들어, "Hello, World!"가 13자 이므로 다음 코드는 x를 값 13으로 할당합니다. `String` 객체는 문자열에있는 UTF-16 코드 단위의 수를 나타내는 길이가 하나의 속성을 가집니다. 예를 들어, 다음 코드에서는 "Hello, World!"가 UTF-16 코드 단위로 표현되는 13개의 문자를 가지고 있기 때문에 x 값이 13이 됩니다. 배열 브래킷 스타일을 사용하여 각 코드 단위에 액세스 할 수 있습니다. 문자열은 변경 불가능한 배열과 같은 객체이기 때문에 개별 문자를 변경할 수 없습니다.

```js
var mystring = "Hello, World!";
var x = mystring.length;
mystring[0] = "L"; // This has no effect, because strings are immutable
mystring[0]; // This returns "H"
```

유니 코드 스칼라 값이 U + FFFF (희귀 한 중국어 / 일본어 / 한국어 / 베트남어 문자 및 일부 이모티콘)보다 큰 문자는 각각 서로 다른 두 개의 코드 단위로 UTF-16에 저장됩니다. 예를 들어, 단일 문자 U + 1F600 "Emoji grinning face"를 포함하는 문자열은 길이가 2입니다. 대괄호를 사용하여 이러한 문자열의 개별 코드 단위에 액세스하면 일치하지 않는 대리 코드 단위가있는 문자열이 만들어지는 등의 바람직하지 않은 결과가 발생할 수 있습니다. 유니 코드 표준 위반 MDN 버그 857438이 수정 된 후에 예제가 이 페이지에 추가되어야합니다. `String.fromCodePoint ()` (en-US) 또는 `String.prototype.codePointAt ()` (en-US)도 참조하십시오.

`String` 객체는 다양한 메서드가 있습니다: 문자열 자체의 변경된 결과를 반환하는 `substring`과 `toUpperCase`가 그것!

다음 표는 `문자열` 개체의 메서드를 요약한 것입니다.

#### 문자열 메서드

| Method | Description |
| --- | --- |
| `charAt`,
 `charCodeAt` (en-US),
 `codePointAt` (en-US) | 문자열에서 지정된 위치에 있는 문자나 문자 코드를 반환합니다. |
| `indexOf`,
 `lastIndexOf` |
 문자열에서 지정된 부분 문자열의 위치나 지정된 부분 문자열의 마지막
 위치를 각각 반환합니다.
  |
| `startsWith`,
 `endsWith`,
 `includes` |
 문자열 시작하고, 끝나고, 지정된 문자열을 포함하는지의 여부를
 반환합니다.
  |
| `concat` | 두 문자열의 텍스트를 결합하고 새로운 문자열을 반환합니다. |
| `fromCharCode`,
 `fromCodePoint` (en-US) |
 유니코드 값의 지정된 시퀀스로부터 문자열을 구축합니다. 문자열
 인스턴스가 아닌 문자열 클래스의 메서드입니다.
  |
| `split` |
 부분 문자열로 문자열을 분리하여 문자열 배열로 문자열 개체를
 분할합니다.
  |
| `slice` | 문자열의 한 부분을 추출하고 새 문자열을 반환합니다. |
| `substring`,
 `substr` |
 어느 시작 및 종료 인덱스 또는 시작 인덱스 및 길이를 지정하여, 문자열의
 지정된 일부를 반환합니다.
  |
| `match`,
 `replace` (en-US),
 `search` (en-US) | 정규 표현식으로 작업합니다. |
| `toLowerCase`,`toUpperCase` | .모든 소문자 또는 대문자에서 각각 문자열을 반환합니다. |
| `normalize` | 호출 문자열 값의 유니 코드 표준화 양식을 반환합니다. |
| `repeat` | 주어진 회를 반복하는 개체 요소로 이루어진 문자열을 반환합니다. |
| `trim` | 문자열의 시작과 끝에서 공백을 자릅니다. |

### 다중 선 템플릿 문자열

템플릿 문자열 (en-US)은 포함 식을 용납하는 문자열 리터럴입니다. 여러분은 그것들과 함께 다중 선 문자열 및 문자열 보간 기능을 사용할 수 있습니다.

템플릿 문자열은 작은따옴표나 큰따옴표 대신에 back-tick (` `) (grave accent)문자로 묶습니다. 템플릿 문자열은 자리 표시자를 포함 할 수 있습니다. 이들은 달러 기호와 중괄호로 표시됩니다. (`${expression}`)

#### 다중 선

소스에 삽입하는 새로운 선 문자는 템플릿 문자열의 일부입니다. 정상적인 문자열을 사용하면, 여러분은 다중 선 문자열을 얻기 위해 다음과 같은 구문을 사용해야합니다:

```js
console.log(
  "string text line 1\n\
string text line 2",
);
// "string text line 1
// string text line 2"
```

다중 선 문자열과 같은 효과를 얻기 위해, 여러분은 이제 쓸 수 있습니다:

```js
console.log(`string text line 1
string text line 2`);
// "string text line 1
// string text line 2"
```

#### 포함식

일반 문자열 내에서 표현식을 포함하기 위해, 다음과 같은 구문을 사용합니다:

```js
var a = 5;
var b = 10;
console.log("Fifteen is " + (a + b) + " and\nnot " + (2 \* a + b) + ".");
// "Fifteen is 15 and
// not 20."
```

이제, 템플릿 문자열을 가지고, 여러분은 읽기와 같이 대체를 만드는 syntactic sugar의 사용을 할 수 있습니다:

```js
var a = 5;
var b = 10;
console.log(`Fifteen is ${a + b} and\nnot ${2 \* a + b}.`);
// "Fifteen is 15 and
// not 20."
```

자세한 내용은 JavaScript 참조에서 템플릿 문자열 (en-US)에 대해 읽어보세요.

국제화
---

`Intl` (en-US) 개체는 ECMA스크립트 국제 API에 언어와 문자열과 숫자서식과 날짜와 시간서식을 제공하는 명칭공간입니다. `Collator` (en-US), `NumberFormat`, 와 `DateTimeFormat` 개체들을 위한 생성자들은 `Intl` 개체의 특성들입니다.

### 날짜와 시간서식

`DateTimeFormat` 개체는 날짜와 시간을 서식하기에 유용합니다. 다음 코드는 미국에서 쓰이는 영어로 날짜를 서식합니다. (결과는 다른 시간대와 다릅니다)

```js
var msPerDay = 24 \* 60 \* 60 \* 1000;

// July 17, 2014 00:00:00 UTC.
var july172014 = new Date(msPerDay \* (44 \* 365 + 11 + 197));

var options = {
  year: "2-digit",
  month: "2-digit",
  day: "2-digit",
  hour: "2-digit",
  minute: "2-digit",
  timeZoneName: "short",
};
var americanDateTime = new Intl.DateTimeFormat("en-US", options).format;

console.log(americanDateTime(july172014)); // 07/16/14, 5:00 PM PDT
```

### 숫자 서식

`NumberFormat`개체는 통화를 위해 숫자를 서식하는것에 대해 유용하다.

```js
var gasPrice = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  minimumFractionDigits: 3,
});

console.log(gasPrice.format(5.259)); // $5.259

var hanDecimalRMBInChina = new Intl.NumberFormat("zh-CN-u-nu-hanidec", {
  style: "currency",
  currency: "CNY",
});

console.log(hanDecimalRMBInChina.format(1314.25)); // ￥ 一,三一四.二五
```

### 조합

`Collator` (en-US)개체는 문자열을 비교하고 구분하는 것에 대해 유용합니다.

예를 들어, 실제로 독일에선 phonebook과 dictionary라는 2개의 다른 종류의 명령어들이 있습니다. 전화기록부류는 소리를 강조합니다. 그리고 구분에 앞서 다른것들은 "ä", "ö"인것처럼 "ae", "oe"로 확장됐습니다.

```js
var names = ["Hochberg", "Hönigswald", "Holzman"];

var germanPhonebook = new Intl.Collator("de-DE-u-co-phonebk");

// as if sorting ["Hochberg", "Hoenigswald", "Holzman"]:
console.log(names.sort(germanPhonebook.compare).join(", "));
// logs "Hochberg, Hönigswald, Holzman"
```

어떤 독일말들은 여분의 변모음과 함께 활용한다. 그래서 사전안에서 이것은 변모음을 무시하라고 명령하기에 실용적이다.

```js
var germanDictionary = new Intl.Collator("de-DE-u-co-dict");

// as if sorting ["Hochberg", "Honigswald", "Holzman"]:
console.log(names.sort(germanDictionary.compare).join(", "));
// logs "Hochberg, Holzman, Hönigswald"
```

`Intl` (en-US)API에 대한 자세한 내용은 Introducing the JavaScript Internationalization API를 참조하세요.

* « 이전
* 다음 »


반복기 및 생성기 - JavaScript

반복기 및 생성기
=========

* « 이전
* 다음 » (en-US)

컬렉션 내 각 항목 처리는 매우 흔한 연산입니다. JavaScript는 간단한 `for` 루프에서 `map()` 및 `filter()`에 이르기까지, 컬렉션을 반복하는 많은 방법을 제공합니다. 반복기(iterator) 및 생성기(generator)는 반복 개념을 핵심 언어 내로 바로 가져와 `for...of` 루프의 동작(behavior)을 사용자 정의하는 메커니즘을 제공합니다.

자세한 내용은, 다음을 참조하세요:

* 반복 프로토콜
* `for...of`
* `function*` 및 `Generator`
* `yield` 및 `yield*`
반복자
---

JavaScript에서 **반복자**(**Iterator**)는 시퀀스를 정의하고 종료시의 반환값을 잠재적으로 정의하는 객체입니다. 더 구체적으로 말하자면, 반복자는 두 개의 속성( `value`, `done`)을 반환하는 next() 메소드 사용하여 객체의 Iterator protocol을 구현합니다. 시퀀스의 마지막 값이 이미 산출되었다면 `done` 값은 true 가 됩니다. 만약 `value`값이 `done` 과 함께 존재한다면, 그것은 반복자의 반환값이 됩니다.

반복자를 생성하면 `next()` 메소드를 반복적으로 호출하여 명시적으로 반복시킬 수 있습니다. 반복자를 반복시키는 것은 일반적으로 한 번씩만 할 수 있기 때문에, 반복자를 소모시키는 것이라고 할 수 있습니다. 마지막 값을 산출하고나서 `next()`를 추가적으로 호출하면 `{done: true}`. 가 반환됩니다.

JavaScript에서 가장 일반적인 반복자는 배열 반복자로, 배열의 각 값을 순서대로 반환합니다. 모든 반복자가 배열로 표현될수 있다고 상상할 수 있지만 , 이것은 사실은 아닙니다. 배열은 완전히 할당되어야 하지만, 반복자는 필요한만큼만 소모되므로 무제한 시퀀스로 표현할 수 있습니다. 이를 테면 0부터 무한대사이의 정수범위처럼 말이죠.

여기에 실습할 수 있는 예제가 있습니다. `start`에서 `end`까지 `step` 수 만큼 띄어진 정수 시퀀스를 정의하는 간단한 범위 반복자를 만들 수 있습니다. 최종적으로 시퀀스의 크기가 반환됩니다.

```javascript
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
  var nextIndex = start;
  var n = 0;

  var rangeIterator = {
    next: function () {
      var result;
      if (nextIndex < end) {
        result = { value: nextIndex, done: false };
      } else if (nextIndex == end) {
        result = { value: n, done: true };
      } else {
        result = { done: true };
      }
      nextIndex += step;
      n++;
      return result;
    },
  };
  return rangeIterator;
}
```

위의 반복자를 사용하면 아래와 같습니다:

```javascript
var it = makeRangeIterator(1, 4);

var result = it.next();
while (!result.done) {
  console.log(result.value); // 1 2 3
  result = it.next();
}

console.log("Iterated over sequence of size: ", result.value);
```

It is not possible to know reflectively whether a particular object is an iterator. If you need to do this, use Iterables.

Generator functions
-------------------

잘 만들어진 반복자(Iterator)는 유용한 도구인 반면, 이것을 생성할 때는 주의해서 프로그래밍을 해야 하는데, 반복자 내부에 명시적으로 상태를 유지할 필요가 있기 때문입니다. 생성자(Generator) 함수는 이에 대한 강력한 대안을 제공합니다: 실행이 연속적이지 않은 하나의 함수를 작성함으로서 개발자가 iterative algorithm을 정의할 수 있게 해줍니다. 생성자 함수는 `function*` 문법을 사용하여 작성됩니다. 생성자 함수가 최초로 호출될 때, 함수 내부의 어떠한 코드도 실행되지 않고, 대신 생성자라고 불리는 반복자 타입을 반환합니다. 생성자의 **next** 메소드를 호출함으로서 어떤 값이 소비되면, 생성자 함수는 **yield** 키워드를 만날 때까지 실행됩니다.

생성자 함수는 원하는 만큼 호출될 수 있고, 매번 새로운 생성자를 반환합니다다. 하지만, 각 생성자는 단 한 번만 순회될 수 있을 것입니다.

위의 예제 코드에 생성자를 적용한 것입니다. 두 코드의 행위는 동일하지만, 생성자를 사용한 쪽이 쓰거나 읽기가 훨씬 쉽습니다.

```javascript
function\* makeRangeIterator(start = 0, end = Infinity, step = 1) {
  let n = 0;
  for (let i = start; i < end; i += step) {
    n++;
    yield i;
  }
  return n;
}
```

Iterables
---------

객체는 값이 `for..of` 구조 내에서 반복되는 것 같은 그 반복 동작을 정의하는 경우 반복이 가능(**iterable**)합니다. `Array` 또는 `Map`과 같은 일부 내장 형은 기본 반복 동작이 있지만 다른 형(가령 `Object`)은 없습니다.

**반복가능**하기 위해서, 객체는 **@@iterator** 메서드를 구현해야 합니다. 즉, 객체( 혹은 그 프로토타입 체인에 등장하는 객체 중 하나)가 `Symbol.iterator` 키를 갖는 속성이 있어야 함을 뜻합니다.

하나의 iterable은 단 한 번, 혹은 여러번 반복가능합니다. 어떤 순간에 어떻게 사용할 지는 프로그래머에게 달려있습니다. 단 한 번 반복가능한 iterable(e.g. Generator)은 관습적으로 자신의 **@@iterator** 메소드로부터 **this**를 반환합니다. 반면, 여러 번 반복 가능한 iterables은 **@@iterator** 메소드가 호출되는 매 회 새로운 iterator를 반드시 반환해야합니다.

### 사용자 정의 iterable

이와 같이 자신의 반복가능 객체를 만들 수 있습니다:

```javascript
    var myIterable = {
        \*[Symbol.iterator]() {
            yield 1;
            yield 2;
            yield 3;
        }
    }

    for (let value of myIterable) {
        console.log(value);
    }
    // 1
    // 2
    // 3

    or

    [...myIterable]; // [1, 2, 3]
```

### 내장 iterable

`String`, `Array`, `TypedArray`, `Map` 및 `Set` (en-US)은 모두 내장 반복가능 객체입니다, 그들의 프로토타입 객체가 모두 `Symbol.iterator` 메서드가 있기 때문입니다.

### iterable을 기대하는 구문

일부 문(statement) 및 식(expression)은 iterable합니다, 가령 `for-of` 루프, spread syntax, `yield*` 및 해체 할당.

```js
for (let value of ["a", "b", "c"]) {
  console.log(value);
}
// "a"
// "b"
// "c"

[..."abc"]; // ["a", "b", "c"]

function\* gen() {
  yield\* ["a", "b", "c"];
}

gen().next(); // { value:"a", done:false }

[a, b, c] = new Set(["a", "b", "c"]);
a; // "a"
```

Generator 심화
------------

생성자 함수는 요청에 따라 그 산출된(yielded, yield 식으로 산출된) 값을 계산하고, 계산하기 비싼(힘든) 수열 또는 위에 설명한 대로 무한 수열이라도 효율적으로 나타내게 합니다.

`next()` 메서드는 또한 생성기의 내부 상태를 수정하는 데 쓰일 수 있는 값을 받습니다. `next()`에 전달되는 값은 생성기가 중단된 마지막 `yield` 식의 결과로 처리됩니다.

여기 sequence(수열)을 재시작하기 위해 `next(x)`를 사용하는 피보나치 생성기가 있습니다:

```js
function\* fibonacci() {
  var fn1 = 0;
  var fn2 = 1;
  while (true) {
    var current = fn1;
    fn1 = fn2;
    fn2 = current + fn1;
    var reset = yield current;
    if (reset) {
      fn1 = 0;
      fn2 = 1;
    }
  }
}

var sequence = fibonacci();
console.log(sequence.next().value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
console.log(sequence.next().value); // 3
console.log(sequence.next().value); // 5
console.log(sequence.next().value); // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
```

제너레이터의 `throw()` 메서드를 호출하고 throw해야 하는 예외 값을 전달하여 생성자가 예외를 throw하도록 할 수 있습니다. 이 예외는 생성기의 현재 일시 중단된 컨텍스트에서 throw됩니다. 마치 현재 일시 중단된 `yield` 가 대신 `throwvalue` 문인 것처럼 말입니다.

예외가 생성기 내에서 포착되지 않으면 `throw()` 호출을 통해 전파되고 이후의 `next()` 호출은 `done` 속성이 `true` 가 됩니다.

제너레이터에는 주어진 값을 반환하고 제너레이터 자체를 완료하는 `return(value)` 메서드가 있습니다.

* « 이전
* 다음 » (en-US)


인덱스 기반 컬렉션 - JavaScript

인덱스 기반 컬렉션
==========

* « 이전
* 다음 »

 이번 장에서는 인덱스 값에 의해 정렬이 되는 데이터 컬렉션에 대해 소개합니다. 배열과 유사 배열 구조인
 `Array` 객체와 `TypedArray` 객체를 포함합니다.

배열은 이름과 인덱스를 사용하여 참조하는 값들의 순서가 있는 목록입니다.

 예를 들면, 숫자로 된 사원번호로 인덱스되고 사원명을 가지고 있는 `emp`라는 배열을 고려해보겠습니다.
 그래서 `emp[0]`은 사원번호 0번, `emp[1]`는 사원번호 1번, 이런 식이 되겠죠.

 JavaScript는 명시적인 배열 데이터 형식을 가지고 있지 않습니다. 그러나 미리 정의된 `Array` 객체와 배열과 메서드를 사용하여
 어플리케이션에서 배열로 작업할 수 있습니다. `Array` 객체에는 합치기(joining), 순서 뒤집기(reversing)
 그리고 정렬(sorting)과 같은 다양한 방법으로 배열을 조작하는 메서드가 있습니다. 또한 배열의 길이를 결정하는 속성과
 정규 표현식과 함께 사용할 수 있는 다양한 속성이 있습니다.

 이 글에서는 배열에 초점을 맞추겠지만, 배열과 형식화 배열(typed array)은 유사한 메서드를 많이 공유하기 때문에
 형식화 배열에도 동일한 개념이 많이 적용됩니다. 형식화 배열에 대한 자세한 내용은
 형식화 배열 가이드를 참조하세요.

배열 생성
-----

아래의 구문들은 동일한 배열을 생성합니다.

```js
const arr1 = new Array(element0, element1, /\* … ,\*/ elementN);
const arr2 = Array(element0, element1, /\* … ,\*/ elementN);
const arr3 = [element0, element1, /\* … ,\*/ elementN];
```

`element0, element1, …, elementN`은 배열의 요소에 대한 값의 목록입니다. 이러한 값이 명시되어 있을 경우 배열은
 해당 값을 배열의 요소로 초기화됩니다. 배열의 `length` 속성은 인수의 수로 설정됩니다.

 대괄호 구문을 "배열 리터럴" 또는 "배열 초기자(array initializer)"라고 합니다.
 배열 성성을 위한 다른 형태보다 짧아서 일반적으로 선호하는 방법입니다. 자세한 내용은 배열 리터럴을 참조하세요.

길이가 0이 아니지만 요소가 없는 배열을 만들려면 다음 중 하나를 사용할 수 있습니다.

```js
// 이...
const arr1 = new Array(arrayLength);

// ...결과도 위와 같습니다
const arr2 = Array(arrayLength);

// 이것 역시 동일합니다
const arr3 = [];
arr3.length = arrayLength;
```

**참고:** 위의 예제 코드에서, `arrayLength`는 반드시 `Number`여야 합니다.
 그렇지 않으면, 하나의 요소(주어진 값)을 가지는 배열이 생성됩니다.
 `arr.length`를 호출하면 `arrayLength`가 반환이 되지만 해당 배열은 실제로 아무런 요소를 가지고 있지 않습니다.
 `for...in` 반복문을 실행하면 해당 배열은 아무런 요소를 반환하지 않습니다.

추가로 아래의 예제에서 볼 수 있듯이, 새로이 정의된 혹은 이미 존재하는 객체 변수의 속성으로 배열을 할당할 수 있습니다.

```js
const obj = {};
// …
obj.prop = [element0, element1, /\* … ,\*/ elementN];

// OR
const obj = { prop: [element0, element1, /\* … ,\*/ elementN] };
```

 값이 `숫자`인 하나의 요소만을 가지는 배열을 생성하고자 할 경우, 반드시 대괄호 문법을 사용해야 합니다.
 하나의 `숫자` 값을 `Array()` 생성자에게 전달할 경우, 그 숫자 값은 해당 배열의 요소가 아니라 `arrayLength`로 해석됩니다.

```js
// 아래 코드는 숫자 42라는 하나의 요소를 가진 배열을 생성합니다.
const arr = [42];

// 아래 코드는 arr.length가 42이면서 요소가 없는 배열을 생성합니다.
const arr = Array(42);

// 아래도 동일합니다.
const arr = [];
arr.length = 42;
```

`N`의 분수 부분이 0이 아닌 정수가 아닌 경우 `Array(N)`을 호출하면 `RangeError`가 발생합니다. 다음 예제는 이 동작을 설명합니다.

```js
const arr = Array(9.3); // RangeError: Invalid array length
```

 만약 임의의 데이터 유형의 단일 요소를 가지는 배열을 생성하고자 할 경우, 배열 표기법을 사용하는 것이 안전합니다.
 혹은 빈 배열을 먼저 선언한 후 임의의 데이터 값을 해당 배열에 추가하는 것도 방법이 됩니다.

요소가 하나인 배열을 만들기 위해 `Array.of` 정적 메서드를 사용할 수 있습니다.

```js
const wisenArray = Array.of(9.3); // wisenArray는 오직 하나의 요소 9.3만을 가지고 있습니다.
```

배열 요소의 참조
---------

요소도 속성이므로 속성 접근자를 사용하여 요소에 접근할 수 있습니다. 다음과 같은 배열을 정의한다고 가정해 보겠습니다.

```js
const myArray = ["Wind", "Rain", "Fire"];
```

 배열의 첫번째 요소는 `myArray[0]`로 참조할 수 있고 두번째 요소는 `myArray[1]`로 참조할 수 있습니다.
 배열의 인덱스 값은 0부터 시작합니다.

**참고:** 속성 접근자를 객체처럼 배열의 다른 속성에 접근하기 위해 사용할 수도 있습니다.

```js
const arr = ["one", "two", "three"];
arr[2]; // three
arr["length"]; // 3
```

배열 채우기
------

요소에 값을 할당함으로서 배열을 채울 수 있습니다. 아래는 그 예입니다.

```js
const emp = [];
emp[0] = "Casey Jones";
emp[1] = "Phil Lesh";
emp[2] = "August West";
```

**참고:** 위 코드에서 배열 연산자에 정수가 아닌 값을 제공하면 배열 요소 대신 배열을 나타내는 객체에 속성이 생성됩니다.

```js
const arr = [];
arr[3.4] = "Oranges";
console.log(arr.length); // 0
console.log(Object.hasOwn(arr, 3.4)); // true
```

배열 생성 시 배열을 채울 수도 있습니다.

```js
const myArray = new Array("Hello", myVar, 3.14159);
// OR
const myArray = ["Mango", "Apple", "Orange"];
```

### 길이(length)에 대한 이해

실제 구현에서 JavaScript의 배열은 배열에 포함된 요소들을 배열의 인덱스 값을 속성 이름으로 사용하여 표준 객체의 속성처럼 저장합니다.

`length` 속성은 좀 특별합니다. 배열의 길이는 항상 마지막 요소의 인덱스에 1을 더한 값을 반환합니다.
 다음 예제에서 `'Dusty'`는 인덱스 `30`번 째에 위치하기 때문에 `cats.length`는 `30 + 1`을 반환합니다.

 기억하실 것은 JavaScript 배열의 인덱스는 항상 `1`부터가 아닌 `0`부터 시작한다는 점입니다.
 이는 아래 예제처럼 배열의 `length` 속성은 배열에 저장된 가장 큰 인덱스보다 1만큼 큰 값이 된다는 뜻입니다.

```js
const cats = [];
cats[30] = ["Dusty"];
console.log(cats.length); // 31
```

`length` 속성에 값을 할당할 수 있습니다.

저장된 항목 수보다 작은 값을 쓰면 배열이 잘립니다. `0`을 쓰면 배열이 완전히 비워집니다.

```js
const cats = ["Dusty", "Misty", "Twiggy"];
console.log(cats.length); // 3

cats.length = 2;
console.log(cats); // [ 'Dusty', 'Misty' ] - Twiggy 값이 제거되었습니다

cats.length = 0;
console.log(cats); // []; cats 배열은 비었습니다

cats.length = 3;
console.log(cats); // [ <3 empty items> ]
```

### 배열의 요소를 반복처리하기

배열을 사용한 일반적인 연산은 배열의 값을 반복하여 각 값을 어떤 방식으로 처리하는 것입니다. 가장 간단한 방법은 아래와 같습니다.

```js
const colors = ["red", "green", "blue"];
for (let i = 0; i < colors.length; i++) {
  console.log(colors[i]);
}
```

 배열의 어떤 요소도 불리언 컨텍스트에서 `false`로 평가되지 않는다는 것을 알고 있다면
 (예를 들어 배열의 요소가 DOM노드로만 구성된 경우) 보다 효율적인 코드 관용구를 사용할 수 있습니다.

```js
const divs = document.getElementsByTagName("div");
for (let i = 0, div; (div = divs[i]); i++) {
  /\* Process div in some way \*/
}
```

 이렇게 하면 배열의 길이를 확인하는 오버헤드를 피할 수 있으며, 루프가 반복될 때마다 `div` 변수가
 현재 항목에 재할당되어 편의성을 높일 수 있습니다.

`forEach()` 메서드는
 배열을 반복하는 또 다른 방법을 제공합니다.

```js
const colors = ["red", "green", "blue"];
colors.forEach((color) => console.log(color));
// red
// green
// blue
```

`forEach`에 전달된 함수는 배열의 모든 항목에 대해 한 번씩 실행되며, 배열 항목이 함수의 인수로 전달됩니다.
 할당되지 않은 값은 `forEach` 반복문에서 반복되지 않습니다.

 배열을 정의할 때 생략된 요소는 `forEach` 반복문으로 배열의 요소를 순회할 때 처리 대상이 되지 않는 것에 유의하시기 바랍니다.
 하지만 `undefined`이 요소에 수동으로 할당된 경우 목록에 나열됩니다.

```js
const sparseArray = ["first", "second", , "fourth"];

sparseArray.forEach((element) => {
  console.log(element);
});
// Logs:
// first
// second
// fourth

if (sparseArray[2] === undefined) {
  console.log("sparseArray[2] is undefined"); // true
}

const nonsparseArray = ["first", "second", undefined, "fourth"];

nonsparseArray.forEach((element) => {
  console.log(element);
});
// Logs:
// first
// second
// undefined
// fourth
```

 JavaScript 객체의 요소는 표준 객체 속성으로 저장되기 때문에 `for...in` 반복문을 사용하여 JavaScript 배열을 반복하는 것은 바람직하지 않습니다.
 왜냐면 일반 요소들과 그리고 모든 열거할 수 있는 속성들이 나열되기 때문입니다.

### 배열의 메서드

`Array` 객체는 다음과 같은 메서드들을 가지고 있습니다.

`concat()` 메서드는
 두 개의 배열을 합쳐 새로운 배열을 반환합니다.

```js
let myArray = ["1", "2", "3"];
myArray = myArray.concat("a", "b", "c");
// myArray is now ["1", "2", "3", "a", "b", "c"]
```

`join()` 메서드는
 배열의 모든 요소를 하나의 문자열로 연결하여 반환합니다.

```js
const myArray = ["Wind", "Rain", "Fire"];
const list = myArray.join(" - "); // list is "Wind - Rain - Fire"
```

`push()` 메서드는
 하나 혹은 그 이상의 요소를 배열의 마지막에 추가하고 추가된 요소를 포함한 `length`를 반환합니다.

```js
const myArray = ["1", "2"];
myArray.push("3"); // myArray is now ["1", "2", "3"]
```

`pop()` 메서드는
 배열의 마지막 요소를 제거하고 그 제거된 요소를 반환합니다.

```js
const myArray = ["1", "2", "3"];
const last = myArray.pop();
// myArray is now ["1", "2"], last = "3"
```

`shift()` 메서드는
 배열의 첫번째 요소를 제거하고 그 제거된 요소를 반환합니다.

```js
const myArray = ["1", "2", "3"];
const first = myArray.shift();
// myArray is now ["2", "3"], first is "1"
```

`unshift()` 메서드는
 하나 혹은 그 이상의 요소를 배열의 앞쪽에 추가하고 추가한 요소를 포함한 길이를 반환합니다.

```js
const myArray = ["1", "2", "3"];
myArray.unshift("4", "5");
// myArray becomes ["4", "5", "1", "2", "3"]
```

`slice()` 메서드는
 배열의 특정 부분을 추출하여 그 추출된 부분을 포함하는 새로운 배열을 반환합니다.

```js
let myArray = ["a", "b", "c", "d", "e"];
myArray = myArray.slice(1, 4); // [ "b", "c", "d"]
// 인덱스 1에서 시작하여 인덱스 3까지의 모든 요소
```

`at()` 메서드는
 배열의 지정된 인덱스에 있는 요소를 반환하거나 인덱스가 범위를 벗어난 경우 `undefined`을 반환합니다.
 특히 배열의 끝에서 요소에 접근하는 음수 인덱스에 사용됩니다.

```js
const myArray = ["a", "b", "c", "d", "e"];
myArray.at(-2); // "d", the second-last element of myArray
```

 The `splice()` 메서드는
 배열에서 요소를 제거한 후 (선택적으로) 대체합니다. 이 메서드는 배열에서 제거된 항목을 반환합니다.

```js
const myArray = ["1", "2", "3", "4", "5"];
myArray.splice(1, 3, "a", "b", "c", "d");
// myArray 는 이제 ["1", "a", "b", "c", "d", "5"] 가 됩니다.
// 이 코드는 첫 번째 인덱스("2"값이 있는 곳)에서 시작하여
// 3개의 요소를 삭제한 후 그 자리에 연속된 모든 요소를 모두 삽입합니다.
```

`reverse()` 메서드는
 배열의 요소를 첫 번째 배열 요소가 마지막이 되고 마지막 요소가 첫 번째가 되는 식으로 배열의 요소를 바꿉니다.
 배열에 대한 참조를 반환합니다.

```js
const myArray = ["1", "2", "3"];
myArray.reverse();
// transposes the array so that myArray = ["3", "2", "1"]
```

`flat()` 메서드는
 지정된 깊이까지 재귀적으로 연결된 모든 하위 배열 요소가 포함된 새 배열을 반환합니다.

```js
let myArray = [1, 2, [3, 4]];
myArray = myArray.flat();
// myArray is now [1, 2, 3, 4], since the [3, 4] subarray is flattened
```

`sort()` 메서드는
 배열의 요소를 새로운 배열이 아닌 주어진 배열 내에서 정렬하고 배열에 대한 참조를 반환합니다.

```js
const myArray = ["Wind", "Rain", "Fire"];
myArray.sort();
// 배열을 정렬하게 되어서 이제 myArray = ["Fire", "Rain", "Wind"]
```

`sort()` 메서드는 어떻게 해당 배열의 요소를 정렬할지 결정하는 콜백 함수를 인자로 받을 수 있습니다.
 콜백 함수는 배열에서 온 두 값을 두 개의 인수로 사용하여 호출됩니다. 이 함수는 이 두 값을 비교하여 두 값의 순서를 나타내는
 양수, 음수 또는 0을 반환합니다. 예를 들어 다음은 문자열의 마지막 문자를 기준으로 배열을 정렬합니다.

```js
const sortFn = (a, b) => {
  if (a[a.length - 1] < b[b.length - 1]) {
    return -1; // 음수 => a < b, a 는 b 앞으로 옵니다.
  } else if (a[a.length - 1] > b[b.length - 1]) {
    return 1; // 양수 => a > b, a 는 b 뒤에 옵니다.
  }
  return 0; // 0 => a = b, a 와 b 는 원래 순서를 유지합니다.
};
myArray.sort(sortFn);
// sorts the array so that myArray = ["Wind","Fire","Rain"]
```

* 정렬 시스템에 의해 `a`가 `b`보다 작으면, `-1`(또는 임의의 음수)을 반환합니다.
* 정렬 시스템에 의해 `a`가 `b`보다 크면, `1`(또는 임의의 양수)을 반환합니다.
* `a`와 `b`가 동등하다 간주되면 `0`을 반환합니다.

`indexOf()` 메서드는
 배열에서 `searchElement`를 찾아서 첫 번째 일치하는 요소의 인덱스를 반환합니다.

```js
const a = ["a", "b", "a", "b", "a"];
console.log(a.indexOf("b")); // 1

// 이제 다시 시도합니다. 마지막 일치한 요소부터 다시 시작합니다.
console.log(a.indexOf("b", 2)); // 3
console.log(a.indexOf("z")); // -1, 왜냐하면 'z'를 찾지 못했습니다.
```

`lastIndexOf()` 메서드는
 `indexOf`메서드와 유사하게 작동하지만 배열의 뒤쪽에서부터 요소를 찾습니다.

```js
const a = ["a", "b", "c", "d", "a", "b"];
console.log(a.lastIndexOf("b")); // 5

// 이제 다시 시도합니다. 마지막 일치한 요소부터 다시 시작합니다.
console.log(a.lastIndexOf("b", 4)); // 1
console.log(a.lastIndexOf("z")); // -1
```

`forEach()` 메서드는
 배열의 모든 요소에 대해 반복적으로 주어진 `callback` 함수를 실행하며 `undefined`를 반환합니다.

```js
const a = ["a", "b", "c"];
a.forEach((element) => {
  console.log(element);
});
// Logs:
// a
// b
// c
```

 콜백을 받는 `forEach` 메서드(그리고 아래의 다른 메서드)는 어떤 방식으로든 전체 배열을 반복하기 때문에
 반복적인(iterative) 메서드로 알려져 있습니다. 각 메서드는 `thisArg`라는 선택적 두 번째 인수를 받습니다.
 이 인수가 제공되면 `thisArg`는 콜백 함수 안에 있는 `this` 키워드의 값이 됩니다.
 제공되지 않으면, 함수가 명시적 객체 컨텍스트 외부에서 호출되는 다른 경우와 마찬가지로, `this`는 함수가 엄격한 모드가 아닐 경우 전역 객체(`window`, `globalThis` 등)을 참조하게 됩니다. 엄격할 모드일 경우 `undefined`을 참조합니다.

**참고:** 위에서 소개한 `sort()` 메서드는 콜백 함수가 오직 비교에만 사용되며 요소 순서에 따라 특정 순서로 호출하지 않을 수 있기 때문에 반복 메서드가 아닙니다. `sort()`는 `thisArg` 매개변수도 받지 않습니다.

`map()` 메서드는
 배열의 모든 요소에 대해 `callback` 함수를 실행하고 이 결과를 새로운 배열에 담아 반환합니다.

```js
const a1 = ["a", "b", "c"];
const a2 = a1.map((item) => item.toUpperCase());
console.log(a2); // ['A', 'B', 'C']
```

`flatMap()` 메서드는
 `map()`에 이어 깊이 1의 `flat()`을 실행합니다.

```js
const a1 = ["a", "b", "c"];
const a2 = a1.flatMap((item) => [item.toUpperCase(), item.toLowerCase()]);
console.log(a2); // ['A', 'a', 'B', 'b', 'C', 'c']
```

`filter()` 메서드는
 `callback` 함수가 `true`를 반환하는 요소를 새로운 배열에 담아 반환합니다.

```js
const a1 = ["a", 10, "b", 20, "c", 30];
const a2 = a1.filter((item) => typeof item === "number");
console.log(a2); // [10, 20, 30]
```

`find()` 메서드는 `callback`이 `true`를 반환하는 첫 번째 항목을 반환합니다.

```js
const a1 = ["a", 10, "b", 20, "c", 30];
const i = a1.find((item) => typeof item === "number");
console.log(i); // 10
```

`findLast()` 메서드는 `callback`이 `true`를 반환하는 마지막 항목을 반환합니다.

```js
const a1 = ["a", 10, "b", 20, "c", 30];
const i = a1.findLast((item) => typeof item === "number");
console.log(i); // 30
```

`findIndex()` 메서드는 `callback`이 `true`를 반환하는 첫 번째 항목의 인덱스를 반환합니다.

```js
const a1 = ["a", 10, "b", 20, "c", 30];
const i = a1.findIndex((item) => typeof item === "number");
console.log(i); // 1
```

`findLastIndex()` 메서드는 `callback`이 `true`를 반환하는 마지막 항목의 인덱스를 반환합니다.

```js
const a1 = ["a", 10, "b", 20, "c", 30];
const i = a1.findLastIndex((item) => typeof item === "number");
console.log(i); // 5
```

`every()` 메서드는
 `callback`이 배열의 모든 항목에 대해 `true`를 반환하면 `true`를 반환합니다.

```js
function isNumber(value) {
  return typeof value === "number";
}
const a1 = [1, 2, 3];
console.log(a1.every(isNumber)); // true
const a2 = [1, "2", 3];
console.log(a2.every(isNumber)); // false
```

`some()` 메서드는
 배열에서 적어도 `callback`이 `true`를 적어도 한번이라도 반환하면 `true`를 반환합니다.

```js
function isNumber(value) {
  return typeof value === "number";
}
const a1 = [1, 2, 3];
console.log(a1.some(isNumber)); // true
const a2 = [1, "2", 3];
console.log(a2.some(isNumber)); // true
const a3 = ["1", "2", "3"];
console.log(a3.some(isNumber)); // false
```

`reduce()` 메서드는 배열 내의 요소를 하나의 요소로 줄이기 위해 `callback(accumulator, currentValue, currentIndex, array)`을 배열의 각 값마다 적용합니다.
 `reduce` 함수는 `callback`함수가 반환하는 마지막 값을 반환합니다.

`initialValue`이 명시되면 `callback`은 첫 번째 매개변수 값으로 `initialValue`을,
 두 번째 매개변수 값으로 배열의 첫 번째 항목의 값을 사용하여 호출됩니다.

`initialValue`이 명시되지 않은 경우, `callback`의 처음 두 매개변수 값은 배열의 첫 번째와 두 번째 요소가 됩니다.
 이후 호출할 때마다 첫 번째 매개변수 값은 이전 호출에서 `callback`이 반환된 값이 되며,
 두 번째 매개변수 값은 배열의 다음 값이 됩니다.

`callback`이 처리 중인 항목의 인덱스에 접근하거나 전체 배열에 접근해야 하는 경우 선택적인 매개변수를 사용할 수 있습니다.

```js
const a = [10, 20, 30];
const total = a.reduce(
  (accumulator, currentValue) => accumulator + currentValue,
  0,
);
console.log(total); // 60
```

`reduceRight()` 메서드는 `reduce()`처럼 동작하지만 마지막 요소부터 시작합니다.

`reduce`와 `reduceRight`는 반복 배열 메서드 중 가장 명확하지 않은 메서드입니다.
 시퀀스를 단일 값으로 줄이기 위해 두 값을 재귀적으로 결합하는 알고리즘에 사용해야 합니다.

희소 배열
-----

 배열에는 `undefined`이라는 값으로 채워진 슬롯과는 다른 "빈 슬롯"이 포함될 수 있습니다.
 빈 슬롯은 다음 중 한 가지 방법으로 만들 수 있습니다:

```js
// 배열 생성자
const a = Array(5); // [ <5 empty items> ]

// 배열 리터럴의 연속된 쉼표
const b = [1, 2, , , 5]; // [ 1, 2, <2 empty items>, 5 ]

// array.length보다 큰 인덱스의 슬록에 직접적으로 값 설정하기
const c = [1, 2];
c[4] = 5; // [ 1, 2, <2 empty items>, 5 ]

// .length를 직접 설정하여 배열 늘리기
const d = [1, 2];
d.length = 5; // [ 1, 2, <3 empty items> ]

// 요소 삭제
const e = [1, 2, 3, 4, 5];
delete e[2]; // [ 1, 2, <1 empty item>, 4, 5 ]
```

일부 작업에서는 빈 슬롯이 'undefined'으로 채워진 것처럼 동작합니다.

```js
const arr = [1, 2, , , 5]; // 희소 배열 생성

// 인덱스를 사용한 접근
console.log(arr[2]); // undefined

// For...of
for (const i of arr) {
  console.log(i);
}
// Logs: 1 2 undefined undefined 5

// 전개연산
const another = [...arr]; // "another" is [ 1, 2, undefined, undefined, 5 ]
```

그러나 다른 경우(특히 대다수 배열 반복 메서드)에서는 빈 슬롯을 건너뜁니다.

```js
const mapped = arr.map((i) => i + 1); // [ 2, 3, <2 empty items>, 6 ]
arr.forEach((i) => console.log(i)); // 1 2 5
const filtered = arr.filter(() => true); // [ 1, 2, 5 ]
const hasFalsy = arr.some((k) => !k); // false

// 속성 열거
const keys = Object.keys(arr); // [ '0', '1', '4' ]
for (const key in arr) {
  console.log(key);
}
// Logs: '0' '1' '4'
// 전개 연산의 결과 값이 객체일 경우 배열의 반복자가 아닌 속성 열거를 사용합니다
const objectSpread = { ...arr }; // { '0': 1, '1': 2, '4': 5 }
```

배열 메서드가 희소 배열에서 작동하는 방식에 대한 전체 목록은 `Array` 참조 페이지를 참조하세요.

다차원 배열
------

 배열은 중첩될 수 있습니다. 즉, 하나의 배열은 또 다른 배열을 요소로 포함할 수 있습니다.
 JavaScript 배열의 이런 특성을 사용하여, 다차원 배열을 생성할 수 있습니다.

아래의 예제는 2차원 배열을 생성하는 예제입니다.

```js
const a = new Array(4);
for (let i = 0; i < 4; i++) {
  a[i] = new Array(4);
  for (let j = 0; j < 4; j++) {
    a[i][j] = `[${i}, ${j}]`;
  }
}
```

이 예제는 다음과 같은 열을 포함하는 배열을 생성합니다.
```

Row 0: [0, 0] [0, 1] [0, 2] [0, 3]
Row 1: [1, 0] [1, 1] [1, 2] [1, 3]
Row 2: [2, 0] [2, 1] [2, 2] [2, 3]
Row 3: [3, 0] [3, 1] [3, 2] [3, 3]
```

다른 속성을 담기 위한 배열 사용
------------------

관련 정보를 저장하기 위해 배열을 객체처럼 사용할 수도 있습니다.

```js
const arr = [1, 2, 3];
arr.property = "value";
console.log(arr.property); // "value"
```

 예를 들어, 정규식과 문자열이 일치하는 결과로 배열이 생성되면 배열은 일치에 대한 정보를 제공하는 속성 및 요소를 반환합니다. 배열의 반환 값은 `RegExp.prototype.exec()` (en-US), `String.prototype.match()` 및 `String.prototype.split()`입니다.
 정규 표현식과 함께 배열을 사용하는 방법에 대한 자세한 내용은 정규 표현식을 참조하십시오.

유사 배열 객체 다루기
------------

`document.getElementsByTagName()`에서 반환되는 `NodeList` 또는 함수 본문 내에서 사용할 수 있는 `arguments` 객체와 같은 일부 JavaScript 객체는 표면적으로는 배열처럼 보이고 동작하지만 모든 메서드를 공유하지는 않습니다. `arguments` 객체는 `length` 속성을 제공하지만 `forEach()`와 같은 배열 메서드는 구현하지 않습니다.

배열 메서드는 유사 배열 객체에서 직접적으로 호출할 수 없습니다.

```js
function printArguments() {
  arguments.forEach((item) => {
    console.log(item);
  }); // TypeError: arguments.forEach is not a function
}
```

하지만 `Function.prototype.call()`을 사용하여 간접적으로 호출할 수 있습니다.

```js
function printArguments() {
  Array.prototype.forEach.call(arguments, (item) => {
    console.log(item);
  });
}
```

배열 프로토타입 메서드는 배열과 유사한 방식으로 문자에 대한 순차적 접근을 제공하므로 문자열에도 사용할 수 있습니다.

```js
Array.prototype.forEach.call("a string", (chr) => {
  console.log(chr);
});
```

* « 이전
* 다음 »