javascrip 27강~29강(Spread, 디스트럭처링, 빌트인 과 래퍼객체)


JavaScript 27강 ~ 29강 예습

  • 27강 : Spread 문법

    • 함수 호출문의 인수 목록에서 사용하는 경우

    • 배열 리터럴 내부에서 사용하는 경우

      • concat
      • push - 뒤에 추가
      • splice - 중간 추가
      • 배열 복사 (slice)
      • 유사 배열 객체를 배열로 변환
    • 객체 리터럴 내부에서 사용하는 경우

    • Rest 파라미터

  • 28강 : 디스트럭처링 할당

    • 배열 디스트럭처링 할당
    • 객체 디스트럭처링 할당
  • 29강 : 표준 빌트인 객체와 래퍼 객체

    • 객체의 분류 3가지 ( 표준 빌트인 객체, 사용자 정의 객체, 호스트 객체(환경에 따른 객체) )
    • 표준 빌트인 객체
    • 표준 빌트인 객체 = 생성자 함수
    • 원시값과 래퍼 객체
    • 결론


27강

Spread 문법

ES6에서 도입된 Spread 문법은 ... 은 하나로 뭉쳐 있는 여러 값들의 집합을 펼쳐서 개별적인 값들의 목록으로 만든다.

  • Spread문법은 for…of로 순회할 수 있는 이터러블에 한정된다.
  • 매개변수에 사용할 경우 Spread가 아닌 Rest파라미터(인수들의 목록을 배열로 바꿔준다)이므로 착각하지 말 것.
1
2
3
4
5
6
7
8
9
10
11
12
13
// ...[1, 2, 3]는 [1, 2, 3]을 개별 요소로 분리한다(→ 1, 2, 3)
console.log(...[1, 2, 3]) // 1 2 3

// 문자열은 이터러블이다.
console.log(...'Hello'); // H e l l o

// Map과 Set은 이터러블이다.
console.log(...new Map([['a', '1'], ['b', '2']])); // [ 'a', '1' ] [ 'b', '2' ]
console.log(...new Set([1, 2, 3])); // 1 2 3

// 이터러블이 아닌 일반 객체는 Spread 문법의 대상이 될 수 없다.
console.log(...{ a: 1, b: 2 });
// TypeError: Found non-callable @@iterator

Spread 문법의 결과물은 단독으로 사용할 수 없고, 아래와 같이 쉼표로 구분한 값의 목록을 사용하는 문에서 사용한다.

  • 함수 호출문의 인수 목록 (매개변수로 주면 Rest파라미터)
  • 배열 리터럴의 요소 목록
  • 객체 리터럴의 프로퍼티 목록 (2019년 11월 - Stage 4 제안)

함수 호출문의 인수 목록에서 사용하는 경우

1
2
3
4
5
6
7
8
const arr = [1, 2, 3];

// 배열 arr의 요소 중에서 최대값을 구하기 위해 Math.max를 사용한다.
// apply 함수의 2번째 인수(배열)는 apply 함수가 호출하는 함수의 인수 목록이다.
// 따라서 배열이 펼쳐져서 인수로 전달되는 효과가 있다.
var maxValue = Math.max.apply(null, arr);

console.log(maxValue); // NaN

Spread 문법이 제공되기 이전에는 배열을 펼쳐서 요소값의 목록을 함수의 인수로 전달하고 싶은 경우, Function.prototype.apply를 사용하였다.


Spread 문법을 사용하면 보다 간결하고 가독성이 좋다.

1
2
3
4
5
6
7
const arr = [1, 2, 3];

// Spread 문법을 사용하여 배열 arr을 1, 2, 3으로 펼쳐서 Math.max에 전달한다.
// Math.max(...[1, 2, 3])는 Math.max(1, 2, 3)과 같다.
const maxValue = Math.max(...arr);

console.log(maxValue); // 3

배열 리터럴 내부에서 사용하는 경우

concat

  • ES5
1
2
3
// ES5
var arr = [1, 2].concat([3, 4]);
console.log(arr); // [1, 2, 3, 4]
  • Spread 문법
1
2
3
// ES6
const arr = [...[1, 2], 3, 4];
console.log(arr); // [1, 2, 3, 4]

push - 뒤에 추가

  • ES5
1
2
3
4
5
6
7
// ES5
var arr1 = [1, 2];
var arr2 = [3, 4];

Array.prototype.push.apply(arr1, arr2);

console.log(arr1); // [1, 2, 3, 4]
  • Spread 문법
1
2
3
4
5
6
7
8
// ES6
const arr1 = [1, 2];
const arr2 = [3, 4];

// arr1.push(3, 4)와 같다.
arr1.push(...arr2);

console.log(arr1); // [1, 2, 3, 4]

splice - 중간 추가

  • ES5
1
2
3
4
5
6
7
8
9
10
11
// ES5
var arr1 = [1, 4];
var arr2 = [2, 3];

// apply 메소드의 2번째 인수는 배열이다. 이것은 인수 목록으로 splice 메소드에 전달된다.
// [1, 0].concat(arr2) → [1, 0, 2, 3]
// arr1.splice(1, 0, 2, 3) → arr1[1]부터 0개의 요소를 제거하고
// 그자리(arr1[1])에 새로운 요소(2, 3)를 삽입한다.
Array.prototype.splice.apply(arr1, [1, 0].concat(arr2));

console.log(arr1); // [1, 2, 3, 4]
  • Spread 문법
1
2
3
4
5
6
7
// ES6
const arr1 = [1, 4];
const arr2 = [2, 3];

arr1.splice(1, 0, ...arr2);

console.log(arr1); // [1, 2, 3, 4]

배열 복사 (slice)

  • ES5
1
2
3
4
5
6
// ES5
var origin = [1, 2];
var copy = origin.slice();

console.log(copy); // [1, 2]
console.log(copy === origin); // false
  • Spread 문법
1
2
3
4
5
6
// ES6
const origin = [1, 2];
const copy = [...origin];

console.log(copy); // [1, 2]
console.log(copy === origin); // false

원본 배열의 각 요소를 얕은 복사(shallow copy)하여 새로운 복사본을 생성.


유사 배열 객체를 배열로 변환

  • 일반적인 방법 : slice 메소드를 apply 함수로 호출.
1
2
3
4
5
6
7
8
9
// ES5
function sum() {
const args = Array.prototype.slice.apply(arguments);

return args.reduce((pre, cur) => pre + cur, 0);

}

console.log(sum(1, 2, 3)); // 6
  • Spread 문법
1
2
3
4
5
6
7
8
9
// ES6
function sum() {
// 유사 배열 객체인 arguments를 배열로 변환
const args = [...arguments];

return args.reduce((pre, cur) => pre + cur, 0);
}

console.log(sum(1, 2, 3)); // 6

객체 리터럴 내부에서 사용하는 경우

​ Spread 프로퍼티는 Rest 프로퍼티와 함께 2019년 11월 TC39 프로세스의 stage 4(Finished) 단계에 제안.

Spread 문법의 대상은 이터러블이어야 하지만 Spread 프로퍼티는 객체 리터럴 내부에서 Spread 문법의 사용을 허용

  • Spread 이전 ▶︎ Object.assign( ) 이용.
1
2
3
4
5
6
7
8
9
10
11
12
// 객체의 병합
// 프로퍼티가 중복되는 경우, 뒤에 위치한 프로퍼티가 우선권을 갖는다.
const merged = Object.assign({}, { x: 1, y: 2 }, { y: 10, z: 3 });
console.log(merged); // { x: 1, y: 10, z: 3 }

// 특정 프로퍼티 변경
const changed = Object.assign({}, { x: 1, y: 2 }, { y: 100 });
console.log(changed); // { x: 1, y: 100 }

// 프로퍼티 추가
const added = Object.assign({}, { x: 1, y: 2 }, { z: 0 });
console.log(added); // { x: 1, y: 2, z: 0 }
  • Spread 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 객체의 병합
// 프로퍼티가 중복되는 경우, 뒤에 위치한 프로퍼티가 우선권을 갖는다.
const merged = { ...{ x: 1, y: 2 }, ...{ y: 10, z: 3 } };
console.log(merged); // { x: 1, y: 10, z: 3 }

// 특정 프로퍼티 변경
const changed = { ...{ x: 1, y: 2 }, y: 100 };
// changed = { ...{ x: 1, y: 2 }, ...{ y: 100 } }
console.log(changed); // { x: 1, y: 100 }

// 프로퍼티 추가
const added = { ...{ x: 1, y: 2 }, z: 0 };
// added = { ...{ x: 1, y: 2 }, ...{ z: 0 } }
console.log(added); // { x: 1, y: 2, z: 0 }

Rest 파리미터

ES6에서는 rest 파라미터를 사용하여 가변 인자의 목록을 배열로 직접 전달받을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ES5의 call함수를 이용하여 가변인자 배열을 받는다.
function sum_es5() {
// 유사 배열 객체인 arguments 객체를 배열로 변환한다.
var array = Array.prototype.slice.call(arguments);

return array.reduce(function (pre, cur) {
return pre + cur;
});
}

console.log(sum_es5(1, 2, 3, 4, 5)); // 15

// Rest 파라미터 사용시
function sum(...args) {
// Rest 파라미터 args에는 배열 [1, 2, 3, 4, 5]이 할당된다.
return args.reduce((pre, cur) => pre + cur);
}
console.log(sum(1, 2, 3, 4, 5)); // 15

28강

디스트럭처링 할당

구조화된 배열 또는 객체를 Destructuring(비구조화, 구조파괴) 하여 1개 이상의 변수에 개별적으로 할당 하는 것.

  • 배열 또는 객체 리터럴에서 필요한 값만을 추출하여 변수 할당 시 유용하다.

배열 디스트럭처링 할당

  • ES5 에서 배열을 디스트럭처링하여 각 변수에 할당하는 방법.
1
2
3
4
5
6
7
8
// ES5
var arr = [1, 2, 3];

var one = arr[0];
var two = arr[1];
var three = arr[2];

console.log(one, two, three); // 1 2 3

  • ES6의 배열 디스트럭처링 할당은 배열의 각 요소를 배열로부터 추출하여 변수의 할당한다.
1
2
3
4
5
6
7
8
// ES6 배열 디스트럭처링 할당
const arr = [1, 2, 3];

// 변수 one, two, three를 선언하고 배열 arr을 디스트럭처링하여 할당한다.
// 이때 할당 기준은 배열의 인덱스이다.
const [one, two, three] = arr;

console.log(one, two, three); // 1 2 3

  • 배열 디스트럭처링 할당의 기준은 배열의 인덱스이다. 즉, 순서대로 할당된다.

이때 변수의 개수와 배열 요소의 개수가 반드시 일치할 필요는 없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
let x, y, z;

[x, y] = [1, 2];
console.log(x, y); // 1 2

[x, y] = [1];
console.log(x, y); // 1 undefined

[x, y] = [1, 2, 3];
console.log(x, y); // 1 2

[x, , z] = [1, 2, 3];
console.log(x, z); // 1 3

  • 기본값을 설정할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
배열 디스트럭처링 할당을 위한 변수에 기본값을 설정할 수 있다.

let x, y, z;

// 기본값
[x, y, z = 3] = [1, 2];
console.log(x, y, z); // 1 2 3

// 기본값보다 할당된 값이 우선한다.
[x, y = 10, z = 3] = [1, 2];
console.log(x, y, z); // 1 2 3

  • 예제) Date 객체에서 년도, 월, 일 추출
1
2
3
4
5
6
const today = new Date(); // Mon Sep 16 2019 02:03:42 GMT+0900 (한국 표준시)
const formattedDate = today.toISOString().substring(0, 10); // "2019-09-15"

// 문자열을 분리하여 배열로 변환한 후, 배열 디스트럭처링 할당을 통해 필요한 요소를 취득한다.
const [year, month, day] = formattedDate.split('-');
console.log([year, month, day]); // ['2019', '09', '15']

  • Rest 파라미터 사용 가능

    Rest 요소는 Rest 파라미터와 마찬가지로 반드시 마지막에 위치해야 한다.

1
2
3
// Rest 요소
const [x, ...y] = [1, 2, 3];
console.log(x, y); // 1 [ 2, 3 ]

객체 디스트럭처링 할당

  • ES5
1
2
3
4
5
6
7
// ES5
var user = { firstName: 'JunHyeok', lastName: 'Kim' };

var firstName = user.firstName;
var lastName = user.lastName;

console.log(firstName, lastName); // JunHyeok Kim

  • ES6 (순서 의미 없다. 프로퍼티 키를 기준으로 한다.)
1
2
3
4
5
6
7
8
// ES6 객체 디스트럭처링 할당
const user = { firstName: 'JunHyeok', lastName: 'Kim' };

// 변수 lastName, firstName을 선언하고 객체 user를 디스트럭처링하여 할당한다.
// 이때 프로퍼티 키를 기준으로 디스트럭처링 할당이 이루어진다. 순서는 의미가 없다.
const { lastName, firstName } = user;

console.log(firstName, lastName); // JunHyeok Kim

  • 객체 디스트럭처링 할당을 위한 변수에 기본값을 설정할 수 있다.
1
2
3
4
5
const { firstName = 'JunHyeok', lastName } = { lastName: 'Kim' };
console.log(firstName, lastName); // Ungmo Lee

const { firstName: fn = 'JunHyeok', lastName: ln } = { lastName: 'Kim' };
console.log(fn, ln); // Ungmo Lee

  • 객체 디스트럭처링 할당은 프로퍼티 키로 객체에서 필요한 프로퍼티 값만을 추출할 수 있다.
1
2
3
4
5
const todo = { id: 1, content: 'HTML', completed: true };

// todo 객체로부터 id 프로퍼티만을 추출한다.
const { id } = todo;
console.log(id); // 1

  • 배열의 요소가 객체인 경우, 배열 디스트럭처링 할당과 객체 디스트럭처링 할당을 혼용할 수 있다.

    ,콤마로 몇 번쨰 배열 요소인지 구별해서 사용한다.

1
2
3
4
5
6
7
8
9
const todos = [
{ id: 1, content: 'HTML', completed: true },
{ id: 2, content: 'CSS', completed: false },
{ id: 3, content: 'JS', completed: false }
];

// todos 배열의 두번째 요소인 객체로부터 id 프로퍼티만을 추출한다.
const [, { id }] = todos;
console.log(id); // 2

  • 중첩 객체의 경우는 아래와 같이 사용한다.
1
2
3
4
5
6
7
8
9
10
const user = {
name: 'Lee',
address: {
zipCode: '03068',
city: 'Seoul'
}
};

const { address: { city } } = user;
console.log(city); // 'Seoul'

  • 객체 디스트럭처링 할당을 위한 변수에 Rest 파라미터와 유사하게 Rest 프로퍼티 …을 사용할 수 있다.

    Rest 프로퍼티는 Rest 파라미터와 마찬가지로 반드시 마지막에 위치해야 한다.

1
2
3
// Rest 프로퍼티
const { x, ...rest } = { x: 1, y: 2, z: 3 };
console.log(x, rest); // 1 { y: 2, z: 3 }

29강

표준 빌트인 객체와 래퍼 객체

객체의 분류 3가지

구분 설명
표준 빌트인 객체(standard built-in object) Object, Sting, Number, Array, Function과 같이 ECMAScript 사양에 정의된 객체를 말하며 애플리케이션 전역의 공통 기능을 제공한다.
호스트 객체(host object) 브라우저 환경에서 제공하는 window, XmlHttpRequest, HTMLElement 등의 DOM 노드 객체와 같이 호스트 환경에 정의된 객체를 말한다. 예를 들어 브라우저에서 동작하는 환경과 브라우저 외부에서 동작하는 환경의 자바스크립트(Node.js)는 다른 호스트 객체를 사용할 수 있다.
사용자 정의 객체(user-defined object) 표준 빌트인 객체와 호스트 객체처럼 외부에서 제공되는 객체가 아닌 사용자가 직접 정의한 객체를 말한다.

표준 빌트인 객체

ECMAScript 사양에 정의된 객체를 말하며 애플리케이션 전역의 공통 기능을 제공.

표준 빌트인 객체는 40개가 넘는다.

대표적인 표준 빌트인 객체

Object, String, Number, Boolean, Symbol, Date, Math, RegExp, Array, Map/Set, WeakMap/WeakSet, Function, Generator, Promise, Reflect, Proxy, JSON, Error 등등


표준 빌트인 객체의 특징

  • ECMAScript에 정의된 객체이므로 Node.js, 브라우저 등의 실행환경과 관계앖이 사용 가능하다.
  • 전역 객체의 프로퍼티다. 언제나 참조가 가능하다.

표준 빌트인 객체 = 생성자 함수

Math 를 제외한 표준 빌트인 객체는 모두 생성자 함수다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// String 생성자 함수에 의한 String 객체 생성
const strObj = new String('Lee');
console.log(typeof strObj); // object
console.log(strObj); // String {"Lee"}

// Number 생성자 함수에 의한 Number 객체 생성
const numObj = new Number(123);
console.log(typeof numObj); // object
console.log(numObj); // Number {123}

// Boolean 생성자 함수에 의한 Boolean 객체 생성
const boolObj= new Boolean(true);
console.log(typeof boolObj); // object
console.log(boolObj); // Boolean {true}

// Function 생성자 함수에 의한 Function 객체(함수) 생성
const func = new Function('x', 'return x * x');
console.log(typeof func); // function
console.dir(func); // ƒ anonymous(x )

// Array 생성자 함수에 의한 Array 객체(배열) 생성
const arr = new Array(1, 2, 3);
console.log(typeof arr); // object
console.log(arr); // (3) [1, 2, 3]

// RegExp 생성자 함수에 의한 RegExp 객체(정규 표현식) 생성
const regExp = new RegExp(/ab+c/i);
console.log(typeof regExp); // object
console.log(regExp); // /ab+c/i

// Date 생성자 함수에 의한 Date 객체 생성
const date = new Date();
console.log(typeof date); // object
console.log(date); // Tue Mar 19 2019 02:38:26 GMT+0900 (한국 표준시)

✸ 참고 : new 키워드를 안붙인다면 해당 타입을 반환한다. 예를들어 var a = Number(‘5’); 의 a는 number타입이다.


표준 빌트인 객체가 생성자 함수(new 키워드와 함께)로서 호출되어 생성한 인스턴스의 프로토타입은 표준 빌트인 객체의 prototype 프로퍼티에 바인딩된 객체이다.

1
2
3
4
5
6
// String 생성자 함수에 의한 String 객체 생성
const strObj = new String('Lee');
console.log(typeof strObj); // object
console.log(strObj); // String {"Lee"}

console.log(Object.getPrototypeOf(strObj) === String.prototype); // true

표준 빌트인 객체가 제공하는 다양한 기능의 메소드는 프로토타입의 객체에 프로토타입 메소드로 존재한다.

또는 표준 빌트인 객체의 메소드로 존재하여 인스턴스 없이 정적으로 호출이 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
// Number 생성자 함수에 의한 Number 객체 생성
const numObj = new Number(1.5);
console.log(typeof numObj); // object
console.log(numObj); // Number {1.5}

// toFixed는 프로토타입 메소드이다.
// 소숫점자리를 반올림하여 문자열로 반환한다.
console.log(numObj.toFixed()); // 2

// isInteger는 정적 메소드이다.
// 정수(Integer)인지 검사하여 그 결과를 Boolean으로 반환한다.
console.log(Number.isInteger(0.5)); // false

원시값과 래퍼 객체

위에서 표준빌트인 객체가 존재하는 이유에 대해 알아보자.

표준 빌트인 객체가 제공하는 프로토타입 메소드를 사용하려면 반드시 인스턴스를 생성하고 인스턴스로 프로토타입 메소드를 호출해야 한다. 그런데 숫자, 문자열, 불리언 원시값은 객체가 아니지만 마치 객체처럼 작동할 때가 있다. (정확히 마침표 표기법으로 접근시)

1
2
3
4
5
6
7
8
const str = 'hello';

// 원시 타입인 문자열이 객체처럼 프로퍼티와 메소드를 갖고 있다.
console.log(str.length); // 5
console.log(str.toUpperCase()); // HELLO

// 레퍼 객체로 프로퍼티 접근이나 메소드 호출한 후, 다시 원시값으로 되돌린다.
console.log(typeof str); // string

이는 원시값인 문자열, 숫자, 불리언 값의 경우, 마치 객체처럼 이들 원시값에 대해 마침표 표기법(또는 대괄호 표기법)으로 접근하면 자바스크립트 엔진은 일시적으로 원시값을 연관된 객체로 변환한다.

이 때 생성된 객체로 프로퍼티에 접근하거나 메소드를 호출하고 다시 원시값으로 되돌린다.

이처럼 문자열, 숫자, 불리언 값에 대해 객체처럼 접근하면 생성되는 임시 객체를 레퍼 객체(wrapper object)라 한다.

래퍼객체

​ 래퍼 객체의 처리가 종료하면 래퍼 객체의 [[StringData]] 내부 슬롯에 할당된 원시값을 되돌리고 래퍼객체는 가비지 컬렉션의 대상이 된다.


숫자(Number)의 경우도 같다.

  1. 숫자에 대해 마침표 표기법으로 접근하면 그 순간 레퍼 객체인 Number 생성자 함수의 인스턴스가 생성된다.
  2. 숫자는 레퍼 객체의 [[NumberData]] 내부 슬롯에 할당된다.
  3. 레퍼 객체인 Number 객체는 Number.prototype의 메소드를 상속받아 사용할 수 있다.
  4. 레퍼 객체의 처리가 종료하면 레퍼 객체의 [[NumberData]] 내부 슬롯에 할당된 원시값을 되돌리고 레퍼 객체는 가비지 컬렉션의 대상이 된다.

문자열, 숫자, 불리언 값 이외의 원시값은 레퍼 객체를 생성하지 않는다.

즉, 원시값 null과 undefined 값의 래퍼 객체가 없다. 따라서 null과 undefined 값을 객체처럼 사용하면 에러가 발생한다.


결론

따라서 String, Number, Boolean 생성자 함수를 new 연산자와 함께 호출하여 문자열, 숫자, 불리언 인스턴스를 생성할 필요가 없으며 권장하지도 않는다.


##

Share