// Symbol 함수를 호출하여 유일무이한 심볼 값을 생성
const mySymbol = Symbol()
console.log(typeof mySymbol); // symbol
// 심볼 값은 외부로 노출되지 않아 확인할 수 없다.
console.log(mySymbol); // Symbol()
new Symbol(); // TypeError: Symbol is not a constructor
// 심볼 값에 대한 설명이 같더라도 유일무이한 심볼 값을 생성
const mySymbol1 = Symbol('mySymbol');
const mySymbol2 = Symbol('mySymbol');
console.log(mySymbol1 === mySymbol); // false
// 전역 심볼 레지스트리에 mySymbol이라는 키로 저장된 심볼 값이 없으면 새로운 심볼 값을 생성
const s1 = Symbol.for('mySymbol')
// 전역 심볼 레지스트리에 저장된 심볼 값의 키를 추출
Symbol.keyFor(s1); // -> mySymbol
// Symbol 함수를 호출하여 생성한 심볼 값은 전역 심볼 레지스트리에 등록되어 관리되지 않는다.
const s2 = Symbol('foo')
// 전역 심볼 레지스트리에 저장된 심볼 값의 키를 추출
Symbol.keyFor(s2); // -> undefined
// 위, 아래, 왼쪽, 오른쪽을 나타내는 상수를 정의한다.
// 중복될 가능성이 없는 심볼 값으로 상수 값을 생성한다.
const Direction = {
UP: Symbol('up');
DOWN: Symbol('down');
LEFT: Symbol('left');
RIGHT: Symbol('right');
};
// Javascript enum
// Direction 객체는 불변 객체이며 프로퍼티 값은 유일무이한 값이다.
const Direction = Object.freeze({
UP: Symbol('up');
DOWN: Symbol('down');
LEFT: Symbol('left');
RIGHT: Symbol('right');
});
const obj = {
// 심볼 값으로 프로퍼티 키를 생성
[Symbol.for('mySymbol')]: 1
};
obj[Symbol.for('mySymbol')]; // -> 1
const obj = {
// 심볼 값으로 프로퍼티 키를 생성
[Symbol.for('mySymbol')]: 1
};
for (const key in obj) {
console.log(key); // 아무것도 출력되지 않는다.
}
console.log(Object.keys(obj)); // []
console.log(Object.getOwnPropertyNames(obj)); // []
// getOwnPropertySymbols 메서드는 인수로 전달한 객체의 심볼 프로퍼티 키를 배열로 반환한다.
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(mySymbol)]
// getOwnPropertySymbols 메서드로 심볼 값을 찾을 수 있다.
const symbolkey1 = Object.getOwnPropertySymbols(obj)[0];
console.log(obj[symbolKey1]); // 1
// 표준 빌트인 객체를 확장하는 것은 권장하지 않는다.
Array.prototype.sum = function () {
return this.reduce((acc, cur) => acc + cur, 0);
};
[1,2].sum(); // -> 3
// 심볼 값으로 프로퍼티 키를 동작 생성하면 다른 프로퍼티 키와 절대 충돌하지 않아 안전하다.
Array.prototype[Symbol.for('sum')] = function() {
return this.reduce((acc, cur) => acc + cur, 0);
};
[1,2][Symbol.for('sum')](); // -> 3
// 1~5 범위의 정수로 이루어진 이터러블
const iterable = {
// Symbol.iterator 메서드를 구현하여 이터러블 프로토콜을 준수
[Symbol.iterator]() {
let cur = 1;
const max = 5;
// Symbol.iterator 메서드는 next 메서드를 소유한 이터레이터를 반환
return. {
next() {
return { value: cur++, done: cur > max + 1 };
}
};
}
};
for (const num of iterable) {
console.log(num); // 1 2 3 4 5
const isIterable = v => v !== null && typeof v[Symbol.iterator] === 'function';
// 배열, 문자열, Map, Set 등은 이터러블이다.
isIterable([]); // -> true
isIterable(''); // -> true
isIterable(new Map()); // -> true
isIterable(new Set()); // -> true
isIterable({}); // -> false
const array = [1, 2, 3];
const obj = { a: 1, b: 2 };
// 배열은 Array.prototype의 Symbol.iterator 메서드를 상속받는 이터러블이다.
console.log(Symbol.iterator in array); // true
// 일반 객체는 Symbole.iterator 메서드를 구현하거나 상속받지 않는다.
console.log(Symbol.iterator in obj); // false
// 이터러블인 배열은 for ...of 문으로 순회 가능하다.
for (const iten of array) {
console.log(item);
}
// 이터러블이 아닌 일반 객체는 for ...of 문으로 순회할 수 없다.
for (const item of obj) { // -> TypeError: obj is not iterable
console.log(item);
}
// 이터러블인 배열은 스프레드 문법의 대상으로 사용할 수 있다.
console.log([...array]); // [1, 2, 3]
// 스프레드 프로퍼티 제안(Stage 4)은 객체 리터럴 내부에서 스프레드 문법의 사용을 허용한다.
console.log({ ...obj}); // {a: 1, b: 2}
// 이터러블인 배열은 배열 디스트럭처링 할당의 대상으로 사용할 수 있다.
const [a, ...rest] = array;
console.log(a, rest); // 1, [2, 3]
// 이터러블이 아닌 일반 객체는 배열 디스트럭처링 할당의 대상으로 사용할 수 없다.
const [a, b] = obj; // -> TypeError: obj is not iterable
// 배열은 이터러블 프로토콜을 준수한 이터러블이다.
const array = [1, 2, 3];
// Symbol.iterator 메서드는 이터레이터를 반환한다. 이터레이터는 next 메서드를 갖는다.
const iterator = array[Symbol.iterator]();
// next 메서드를 호출하면 이터러블을 순회하여 순회 결과를 나타내는 이터네이터 리절트 객체를 반환한다.
// 이터레이터 리절트 객체는 value 와 done 프로퍼티를 갖는 객체다.
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
for (변수선언문 of 이터러블) { ... }
for (변수선언문 in 객체) { ... }
// 이터러블
const iterable = [1, 2, 3];
// 이터러블의 Symbol.iterator 메서드를 호출하여 이터레이터를 생성한다.
const iterator = iterable[Symbol.iterator]();
for (;;) {
// 이터레이터의 next 메서드를 호출하여 이터러블을 순회한다.
// 이때 next 메서드는 이터레이터 리절트 객체를 반환한다.
const res = iterator.next();
// next 메서드가 반환한 이터레이터 리절트 객체의 done 프로퍼티 값이 true이면 이터러블의 순회를 중단한다.
if (res.done) break;
// 이터레이터 리절트 객체의 value 프로퍼티 값을 item 변수에 할당한다.
const item = res.value;
console.log(item); // 1 2 3
// 유사 배열 객체
const arrayLike = {
0: 1,
1: 2,
2: 3,
length: 3
};
// Array.from 은 유사 배열 객체 또는 이터러블을 배열로 반환한다.
const arr = Array.from(arrayLike);
console.log(arr); // [1, 2, 3]
// 피보나치 수열을 구현한 사용자 정의 이터러블
const fibonacci = {
// Symbol.iterator 메서드를 구현하여 이터러블 프로토콜을 준수한다.
[Symbol.iterator]() {
let [pre, cur] = [0, 1];
const max = 10; // 수열의 최대값
// Symbol.iterator 메서드는 next 메서드를 소유한 이터레이터를 반환해야 하고
// next 메서드는 이터레이터 리절트 객체를 반환해야 한다.
return {
next() {
[pre, cur] = [cur, pre + cur];
// 이터레이터 리절트 객체를 반환한다.
return { value: cur, done: cur >= max };
}
};
}
};
// 이터러블인 fibonacci 객체를 순회할 때마다 next 메서드가 호출된다.
for (const num of fibonacci) {
console.log(num); // 1 2 3 5 8
}
var arr = [1, 2, 3];
// apply 함수의 2번째 인수(배열)는 apply 함수가 호출하는 함수의 인수 목록이다.
// 따라서 배열이 펼쳐져서 인수로 전달되는 효과가 있다.
var max = Math.max.apply(null, arr); // -> 3
// 스프레드 문법을 사용하여 배열 arr을 1, 2, 3으로 펼쳐서 Math.max 에 전달
const max = Math.max(...arr); // -> 3
// Rest 파라미터는 인수들의 목록을 배열로 전달받는다.
function foo(...rest) {
console.log(rest); // 1, 2, 3 -> [1, 2, 3]
}
// 스프레드 문법은 배열과 같은 이터러블을 펼쳐서 개별적인 값들의 목록을 만든다.
// [1, 2, 3] -> 1, 2, 3
foo(...[1, 2, 3]);
ES5에서 이터러블을 배열로 변환하려면 Function.prototype.apply 또는 Function.prototype.call 메서드를 사용하여 slice 메서드를 호출
// ES5
function sum() {
// 이터러블이면서 유사 배열 객체인 arguments를 배열로 변환
var args = Array.prototype.slice.call(arguments);
return args.reduce(function (pre, cur)) {
return pre + cur;
}, 0);
}
console.log(sum(1, 2, 3)); // 6
// 이터러블이 아닌 유사 배열 객체
const arrayLike = {
0: 1,
1: 2,
2: 3,
length: 3
};
const arr = Array.prototype.slice.call(arrayLike); // -> [1, 2, 3]
console.log(Array.isArray(arr)); // true
// 스프레드 프로퍼티
// 객체 복사(얕은 복사)
const obj = { x: 1, y: 2 };
const copy = { ...obj };
console.log(copy); // { x: 1, y: 2 }
console.log(obj === copy); // false
// 객체 병합
const merged = { x: 1, y: 2, ...{ a: 3, b: 4 } };
console.log(merged); // { x: 1, y: 2, a: 3, b: 4 };
// 객체 병합. 프로퍼티가 중복되는 경우 뒤에 위치한 프로퍼티가 우선권을 갖는다.
const merged = Object.assign({}, { x: 1, y: 2 }, { y: 10, z: 3 }
console.log(merged); // { x: 1, y: 10, z: 3 }
const [x, y] = [1, 2];
const [x, y]; // SyntaxError: Missing initializer in destructuring declaration
const [a, b] = {}; // TypeError: {} is not iterable
// 배열 디스트럭처링 할당의 변수 선언과 할당을 분리할 수 있지만, const 키워드로 변수를 선언할 수 없으므로 권장하지 않는다.
let x, y;
[x, y] = [1, 2]
const set = new Set();
console.log(set); // Set(0) {}
const set1 = new Set([1, 2, 3, 3]);
console.log(set1); // Set(3) {1, 2, 3}
const set2 = new Set('hello');
console.log(set2); // Set(4) {"h", "e", "l", "o" }
// 배열의 중복 요소 제거
const uniq = array => array.filter((v, i, self) => self.indexOf(v) === i);
console.log(uniq([2, 1, 2, 3, 4, 3, 4])); // [2, 1, 3, 4]
// Set을 사용한 배열의 중복 요소 제거
const uniq = array => [...new Set(array)];
console.log(uniq([2, 1, 2, 3, 4, 3, 4])); // [2, 1, 3, 4]
const { size } = new Set([1, 2, 3, 3]);
console.log(size); // 3
const set = new Set();
console.log(set); // Set(0) {}
set.add(1).add(2).add(2);
console.log(set); // Set(2) {1, 2}
console.log(NaN === NaN); // false
console.log(0 === -0); // true
// NaN과 NaN을 같다고 평가하여 중복 추가를 허용하지 않는다.
set.add(NaN).add(NaN);
console.log(set); // Set(1) {NaN}
// +0과 -0을 같다고 평가하여 중복 추가를 허용하지 않는다.
set.add(0).add(-0);
console.log(set); // Set(2) {NaN, 0}
const set = new Set([1, 2, 3]);
console.log(set.has(2)); // true
console.log(set.has(4)); // false
const set = new Set([1, 2, 3]);
// 요소 2를 삭제한다.
set.delete(2);
console.log(set); // Set(2) {1, 3}
// 요소 1을 삭제한다.
set.delete(1);
console.log(set); // Set(3) {1}
set.delete(1).delete(2); // TypeError: set.delete(...).delete is not a function
set.clear();
console.log(set); // Set(0) {}
const set = new Set([1, 2, 3]);
// Set 객체는 Set.prototype의 Symbol.iterator 메서드를 상속받는 이터러블이다.
console.log(Symbol.iterator in set); // true
// 이터러블인 Set 객체는 for...of 문으로 순회할 수 있다.
for (const value of set) {
console.log(value); // 1 2 3
}
// 이터러블인 Set 객체는 스프레드 문법의 대상이 될 수 있다.
console.log([...set]); // {1, 2, 3}
// 이터러블인 Set 객체는 배열 디스트럭처링 할당의 대상이 될 수 있다.
const [a, ...rest] = set;
console.log(a, rest); // 1, [2, 3]
const map = new Map();
console.log(map); // Map(0) {}
const map1 = new Map([['key1', 'value1'], ['key2', 'value2']]);
console.log(map1); // Map(2) {"key1" => "value1", "key2" => "value2"}
const map2 = new Map([1, 2]); // TypeError: Iterator value 1 is not an entry object
const map = new Map([['key1', 'value1'], ['key1', 'value2']]);
console.log(map); // Map(1) {"key1" => "value2"}
const map = new Map([['key1', 'value1'], ['key2', 'value2']]);
console.log(Object.getOwnPropertyDescription(Map.prototype, 'size'));
// {set: undefined, enumerable: false, configurable: true, get: f}
map.size = 10; // 무시된다.
console.log(map.size); // 2
const map = new Map();
map.set('key1', 'value1').set('key2', 'value2')
cosnole.log(map); // Map(2) {'key1' => 'value1', 'key2' => 'value2'}