반응형

 

1. 변수

 

ES5에서는 모든 변수는 var 키워드로 선언했습니다.

ES6에서는 var 키워드가 아닌 let과 const 키워드를 사용합니다.

var를 사용할 수 없는 것은 아니지만 let과 const가 훨씬 좋기 때문에 var를 굳이 사용하지 않습니다.

 

 

var vs let, const

 

var키워드로 선언된 변수는 다음과 같은 문제점이 있습니다.

 

 

1. 함수 레벨 스코프

 

var는 함수 단위의 스코프를 가지기 때문에 블록 단위의 스코프를 무시합니다. 이는 스코프 경계를 모호하게 하는 문제점을 가집니다. 이런 문제를 잘 보여주는 예시를 하나 보겠습니다.

 

* 예시 코드

 


  See the Pen https://codepen.io/cocoder16/pen/ZEbaPdv">
  ZEbaPdv by cocoder16 (https://codepen.io/cocoder16">@cocoder16)
  on https://codepen.io">CodePen.

 

위 예시는 버튼 5개에 onclick 이벤트를 장착하여 버튼을 누르면 몇 번째 버튼인지 출력되는 예제입니다.

for문으로 각 버튼마다 이벤트 리스너를 장착하고 그때의 i 값을 출력하도록 했기 때문에 0, 1, 2, 3, 4가 출력될 것이라 예상할 수 있습니다. 하지만 실제로 버튼을 클릭해보면 어떤 버튼을 눌러도 5가 출력된다는 것을 확인할 수 있습니다.

 

여기서 var 키워드가 함수 레벨 스코프인 것의 문제점이 드러납니다. for문이 종료된 후의 i의 값은 5이고 이 i는 for문 밖에서도 접근 가능한 전역 변수이기 때문에 이벤트 핸들러 함수가 실행될 때 함수 내에는 i가 선언되어있지 않으므로 함수밖에 있는 전역 변수 i = 5 값을 가져오게 됩니다.

 

let, const는 이런 문제점을 보완하여 함수 레벨 스코프가 아닌 블록 레벨 스코프를 가지고 있습니다. 

블록 레벨 스코프는 {} 중괄호를 기준으로 스코프로 갖습니다. 따라서 if문, for문 등 {} 내에서 선언된 변수들은 그 안에서만 유효 범위를 가지게 됩니다.

 

이제 위의 예제를 제대로 작동하도록 만들어보겠습니다. var 키워드를 유지해서 위 예제를 제대로 동작하게 하려면 for문 내의 코드를 즉시 실행 함수로 감싸주는 방법이 있습니다.

 

* var키워드로 위 예제 개선하기

for (var i = 0; i < 5; i++) {
    (function (i) {
        btns[i].addEventListener('click', function(){
            output.innerHTML = i;
        });
    })(i);
}

 

이렇게 하면 실행 당시의 i값을 기억하기에 의도대로 작동할 것입니다.

하지만 이런 복잡한 방법을 쓰지 않고도 let, const가 가진 블록 레벨 스코프의 이점을 활용할 수 있습니다.

 

* let, const키워드로 개선하기

 


  See the Pen https://codepen.io/cocoder16/pen/mdeqgVJ">
  mdeqgVJ by cocoder16 (https://codepen.io/cocoder16">@cocoder16)
  on https://codepen.io">CodePen.

 

 

2. var 키워드 생략 및 변수 중복 선언 허용

 

var 키워드를 생략하고도 변수를 선언할 수 있기 때문에 변수 선언에 실수가 있어도 발견하기가 어려우며, 변수를 중복 선언할 수 있어 동일한 변수를 실수로 중복 사용할 수 있기 때문에 문제가 생깁니다.

 

하지만 let과 const는 이런 것들을 허용하지 않기 때문에 더 엄격한 체크가 가능합니다.

 

x = 1; //allowed
var x = 1;
var x = 2; //allowed

let y = 1;
let y = 2; //error

const z = 1;
const z = 2; //error

 

 

3. 변수 호이스팅

 

var 키워드로 선언된 변수는 선언된 변수를 선언문 이전에 참조할 수 있습니다. 이것도 코드를 느슨하게 만든다는 문제가 있습니다.

 

하지만 let과 const키워드로 선언된 변수는 선언문 이전에 참조할 수 없습니다.

 

console.log(x); //undefined
var x = 1;

console.log(y); //error
let y = 1;

console.log(z); //error
const z = 1;

 

var의 경우 확인해보면 참조되더라도 할당된 값까지 알 수 있는 것은 아닙니다. x에 1을 할당하였으나 undefined로 출력되는 것을 알 수 있었습니다.

 

엄밀히 얘기하면 let과 const도 호이스팅이 일어납니다. 뿐만 아니라 모든 선언 키워드(var, let, const, function, function*, class)는 다 호이스팅됩니다.

 

하지만 let과 const는 var와 다르게 마치 호이스팅이 안 되는 것처럼 참조를 못하게 만들었습니다.

 

이것이 가능한 이유는 let, const로 선언한 변수는 var로 선언한 변수와는 다르게 선언 단계와 초기화 단계가 분리되어 진행되기 때문에 선언 단계에 스코프에 변수를 등록은 하지만 초기화 단계는 변수 선언문에 도달했을 때 이루어지기 때문입니다. 따라서 호이스팅이 된 스코프의 시작 지점부터 초기화 시작 지점까지는 변수를 참조할 수 없게 되고 이 구간을 일시적 사각지대(Temporal Dead Zone; TDZ)라고 부릅니다.

 

 

let vs const

 

let과 const는 var와 비교한 부분에 있어서는 전부 공통점을 가지지만 이 둘 사이에는 두 개의 차이점이 존재합니다.

하나는 let은 재할당이 가능하지만 const는 재할당이 불가능하다는 것과

다른 하나는 let은 선언과 할당을 분리해도 되지만, const는 반드시 선언과 동시에 할당이 이루어져야 한다는 것입니다.

 

// let

let x; // 선언과 할당을 분리해도 된다.
x = 1;
x = 2; // 재할당도 된다.

// const

const y; // error.

const y = 1; // 반드시 선언과 할당을 동시에 해야한다.
y = 2; // error. 재할당 불가능.

 

const는 재할당은 금지되지만 객체에 대해서는 좀 헷갈릴 수 있는 부분이 있습니다.

 

1. const는 객체에 대한 참조를 변경하지 못합니다. 이것은 재할당을 의미하기 때문입니다.

 

const obj = { a: 1 };
obj = { b: 2 }; //error

 

여기는 헷갈리지 않습니다.

 

2. 그러나 const는 객체의 프로퍼티를 조작할 수는 있습니다. 새 프로퍼티의 추가, 프로퍼티 삭제, 프로퍼티 값의 변경 모두 가능합니다.

 

const obj = { a: 1 };

// 새 프로퍼티 추가
obj.b = 2;

// 프로퍼티 수정
obj.a = 3;

// 프로퍼티 삭제
delete obj.a;

 

 

 

 

 

2. 화살표 함수

 

ES6 문법 중 화살표를 이용해 함수를 선언하는 방법은 다음과 같습니다.

 

const func = () => { };

 

기존에 function 키워드로 함수를 선언하는 방법과 비교했을 때, function키워드를 화살표 =>로 바꿨다고 생각하면 됩니다.

 

화살표 함수는 다양한 형태로 사용될 수 있는데 매개변수가 하나일 때 괄호를 생략한다던가 return과 {}를 생략하는 경우도 있습니다. 하나하나 차근차근 보겠습니다.

 

우선 그전에 미리 알아야 할 가장 중요한 점은 화살표 함수는 익명 함수로만 사용할 수 있기 때문에 함수 선언문이 아닌 함수 표현식을 사용해야 한다는 것입니다.

 

* 예시 코드

// 화살표 함수 선언 (함수표현식)
const x = () => {
    return 'x';
};

// 매개변수가 하나일 때에는 소괄호 생략 가능하다.
const func = x => { };
// 여러 개이면 생략 불가능하다.
const func1 = (x, y) => { };

// 함수 몸체의 코드가 한 줄이고 중괄호를 생략하면 그것은 return값이 된다.
// 아래의 두 함수는 똑같은 함수다.
const func2 = () => { return 'x' };
const func3 = () => 'x';

// 단 객체를 리턴할 때에는 소괄호로 감싸줘야한다.
const func4 = () => ({ x: 'x' });

// 여러 줄의 코드를 쓸 때는 생략없이 사용하면 된다.
const func5 = () => {
    //코드
    return 'x';
};

 

 

 

화살표 함수 vs function 키워드로 만든 함수

 

이 둘을 언제 써야 할지 구분 짓는 기준은 this가 바인딩되는 대상 혹은 규칙입니다.

 

function 키워드 함수는 함수를 호출하는 맥락에 따라 this에 바인딩되는 객체가 '동적'으로 결정됩니다.

이에 반해 화살표 함수는 this에 바인딩할 객체가 '정적'으로 결정됩니다.

따라서 개발자는 코드를 짤 때 함수를 만들면서 그 안에 있는 this가 어디에 바인딩될지 확정적으로 예측이 가능합니다.

 

function 키워드로 만든 함수의 경우 this가 바인딩되는 규칙은 다음 글을 참조하시면 됩니다.

 

https://cocoder16.tistory.com/45

 

Javascript에서 this가 바인딩되는 대상 정리

자바스크립트에서 this가 가리키는 대상은 동적으로 변합니다. 자바스크립트에서 this의 바인딩은 동적입니다. 그래서 this가 어떤 객체를 가리키는지 예측이 어렵다는 단점이 있습니다. 이런 단

cocoder16.tistory.com

 

function 키워드 함수 내의 this의 바인딩 규칙은 복잡합니다. 경우의 수도 여러 가지이고 동적으로 결정되기 때문입니다.

 

화살표 함수의 경우 this가 바인딩되는 규칙은 단 한 가지입니다.

바로 this가 있는 스코프의 바로 상위 스코프에서의 this값을 가리킵니다.

 

() => {
	this
	() => {
		this // 이 this는 위의 this의 값을 가리킨다.
    }
}

 

따라서 function 키워드 함수에서처럼 내부 함수에서 외부 함수의 this값을 사용하기 위해 다음과 같이 this를 새 변수에 할당해주는 더러워 보이는 코드를 사용하지 않아도 됩니다.

 

function outer(){
  var that = this;
  function inner(){
    console.log(that);
  }
  inner();
}

 

그리고 화살표 함수는 function 키워드 함수처럼 call, apply, bind 메소드를 이용해서 this를 변경할 수 없습니다.

그저 바로 상위 스코프에서의 this값만을 가리킬 뿐입니다.

 

 

 

메소드 선언

 

ES5라면 메소드는 다음과 같이 만들 것입니다.

var person = {
    name: 'Kim',
    introduce: function () {
        console.log(this.name);
    }
};       

person.introduce(); // this는 메소드를 호출한 객체인 person를 가리킨다.

 

이 코드를 실행하면 Kim 이 콘솔에 출력됩니다.

그런데 메소드를 화살표 함수로 선언하면 다른 현상이 발생합니다.

 

const person_arrow = {
    name: 'Kim',
    introduce: () => {
        console.log(this.name);
    }
};
        
person_arrow.introduce(); // this는 상위 스코프에서의 this이므로 window 객체를 가리킨다.

 

ES6에서 메소드를 만들 때 this를 정적으로 객체에 바인딩하고 싶다면 축약 메소드를 사용해야 합니다.

 

const person = {
    name: 'Kim',
    introduce () {
        console.log(this.name);
    }
};
        
person.introduce(); // this는 정적으로 person 객체에 바인딩 됩니다.

 

이경우에는 아까처럼 Kim이 출력됩니다. 

축약 메소드 또한 화살표 함수처럼 this가 정적으로 바인딩된다는 장점을 가지고 있기 때문에 this를 사용하기 편리합니다.

 

 

 

화살표 함수를 사용하면 안 되는 경우

 

1. 메소드 선언

 

위에서 본 것처럼 메소드를 선언할 때에는 화살표 함수를 사용하지 말고 축약메소드를 사용하는 것이 좋습니다. this를 window가 아닌 메소드를 가진 객체에 바인딩할 수 있기 때문입니다.

화살표 함수는 정적으로 this가 바인딩되기 때문에 어디에 바인딩될지 확정적으로 예측할 수 있으므로, this가 바인딩되는 대상이 무엇인지 확인하는 것을 통해 화살표 함수를 사용해야 할 때와 아닐 때를 구별할 수 있습니다.

 

 

2. prototype에 메소드 할당

 

prototype에 메소드를 할당할 때에는 화살표 함수를 사용하지 말고 function 키워드를 사용해야 합니다.

화살표함수를 사용하면 this가 prototype 객체에 바인딩되는 게 아니라 상위 스코프 this(window)에 바인딩됩니다.

따라서 function 키워드를 사용하여 맥락에 따라 동적으로 호출하는 객체에 this가 바인딩되도록 해줍니다.

 

const me = {
    name: 'cocoder'
};

Object.prototype.introduce = function () {
    console.log(`Hi ${this.name}`);
}
        
me.introduce(); // Hi cocoder

 

템플릿 리터럴 문법

 

위 예시 코드에서 다음 부분이 생소할 수 있습니다.

console.log(`Hi ${this.name}`);

 

이것은 백틱(`)을 이용해 문자열을 표기하는 방법으로 ES6에 도입된 문법입니다.

문자열 안에 ${}를 이용해 자바스크립트 코드를 삽입할 수 있으며 이 자바스크립트 코드가 반환하는 값을 문자열에 삽입할 수 있습니다.

 

 

3. 생성자 함수 생성

 

화살표 함수는 생성자 함수로 사용할 수 없습니다. prototype 프로퍼티를 가지고 있지 않기 때문입니다.

따라서 function 키워드를 이용하여 기존 ES5의 생성자 함수를 사용하거나 ES6에 도입된 class를 이용하여 생성자 함수를 만들어야 합니다.

 

 

 

 

 

 

3. Spread의 문법

 

Spread는 대상을 개별 요소로 분리합니다. 대상은 반드시 iterable 객체이어야 합니다.

사용법은 간단합니다.

iterable 객체 앞에 ... 을 붙이면 됩니다.

 

// 배열
console.log(...[1,2,3]) // 1 2 3

// 문자열
console.log(..."cocoder"); // c o c o d e r

 

 

 

Spread로 Apply 메소드 대체하기

 

ES5에서는 배열의 각 원소들을 함수의 인자로 전달하기 위해서는 apply메소드를 사용하였습니다. 하지만 이제는

Spread로 apply 메소드를 대체할 수 있습니다. 이 둘을 비교해보겠습니다.

 

* apply 메소드 사용

function plus(x, y, z) {
    console.log(x+y+z);
}

const arr = [1, 2, 3];
plus.apply(null, arr); // 6

 

* Spread 사용

function plus(x, y, z) {
    console.log(x + y + z);
}

const arr = [1, 2, 3];
plus(...arr); // 6

 

 

 

Spread로 유사 배열 객체를 객체로 만들기

 

ES5의 문법으로 유사 배열 객체를 배열로 만드는 방법은 다음과 같습니다.

var array = Array.prototype.slice.call(arguments); // array는 배열, arguments는 유사배열객체

 

이것을 사용하려면 slice와 call이라는 메소드를 기억해야 하는데요.

Spread를 사용하면 메소드를 기억하지 않고도 저것을 할 수 있습니다.

 

const array = [...arguments];

 

훨씬 간단해졌습니다. 

 

 

 

Spread로 배열 조작하기

 

1) Array.prototype.concat 대체하기

 

concat은 배열A 원소 끝에 배열B의 원소들을 붙여서 병합하는 메소드입니다. 이것도 Spread문법으로 쉽게 할 수 있습니다. 아래의 두 식 모두 배열A에 배열B의 원소들을 추가한 새로운 배열을 반환하는 코드입니다. 

 

var arr1 = [1, 2, 3];
var arr2 = [4, 5];

// 배열A.concat(배열B)
console.log(arr1.concat(arr2)); // [1, 2, 3, 4, 5]

// [ ...배열A, ...배열B ]
console.log([...arr1, ...arr2]); // [1, 2, 3, 4, 5]

 

 

2) Array.prototype.push 대체하기

 

push는 배열A 끝에 추가할 원소들을 따로따로 넣어서 추가하는 메소드입니다. 이것도 Spread문법으로 가능합니다.

 

const arr = [1, 2, 3];

// push
console.log(arr.push(4, 5, 6)); // [1, 2, 3, 4, 5, 6]

// Spread
console.log([...arr, 4, 5, 6]); // [1, 2, 3, 4, 5, 6]

 

 

3) 배열의 중간에 배열 넣기

 

splice의 파라미터 값으로 Spread를 사용해서 배열의 중간에 배열을 삽입해보겠습니다.

 

const arr = [1, 2, 5, 6]; // 몸체가 될 배열
const arr1 = [3, 4]; // 삽입될 배열

arr.splice(2, 0, ...arr1);
console.log(arr); // [1, 2, 3, 4, 5, 6]

 

 

 

4. Rest 파라미터의 목적과 사용법

 

Rest 파라미터는 함수에서 매개변수명 앞에 ...을 붙여서 만든 매개변수를 의미합니다.

 

Rest 파라미터의 목적은 함수에 전달된 파라미터들을 배열로 만드는 것입니다.

 

ES5에서 arguments라는 객체가 전달된 파라미터들을 유사 배열 객체로 다루지만 이것은 배열은 아니기 때문에 Array.prototype의 메소드를 사용하려면 유사 배열 객체를 배열로 변환하는 단계를 거쳐야만하는 불편함이 있었습니다.

 

* 유사 배열 객체를 배열로 만드는 방법

var array = Array.prototype.slice.call(arguments);

 

* arguments를 배열로 변환하여 이용하는 예시

function sum() {
    var array = Array.prototype.slice.call(arguments); // 유사 배열 객체를 배열로 만드는 방법
            
    return array.reduce(function (pre, cur) { // 배열 내장 메소드인 reduce를 사용
        return pre + cur;
    });
}

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

 

그런데 Rest를 이용하면 Rest파라미터 자체가 배열이 되므로 이런 변환 과정 없이 바로 배열의 내장 메소드를 이용할 수 있습니다.

 

* 위와 똑같은 함수를 Rest를 이용해 만들기

function sum1(...rest) {
    return rest.reduce(function (pre, cur) {
        return pre + cur;
    });
}
        
console.log(sum1(1,2,3,4)); // 10

 

Rest는 다른 여러 파라미터들과 같이 사용할 수 있습니다. 그럼에도 Rest에는 한 가지 제약이 존재합니다.

Rest는 반드시 가장 마지막 순서의 파라미터로 와야 합니다.

 

function (param1, param2, paramN, ...Rest) { }

 

 

 

 

 

 

5. 편리해진 객체 리터럴 작성법

 

1) 변수명과 동일한 프로퍼티명으로 사용하기

 

객체 리터럴 안에 프로퍼티명만 적고 값을 할당하지 않아도 그 프로퍼티명과 똑같은 변수가 있다면 그 변수의 값이 프로퍼티에 할당됩니다.

 

* 예시 코드

const x = 1; y = 2;
const obj = { x, y };
console.log(obj); // { x: 1, y: 2 }

// 프로퍼티 값 수정
obj.x = 3;
console.log(obj); // { x: 3, y: 2 }
console.log(x); // 1

 

 

2) Spread 문법으로 객체 병합하기

 

위 예제도 마찬가지었지만 리터럴 방식으로 생성했기 때문에 프로퍼티의 값을 수정해도 프로퍼티명과 같은 이름을 가진 변수가 수정되지는 않습니다.

 

* 예시 코드

obj1 = { a: 1, b: 2 }
obj2 = { c: 3 }

obj3 = { ...obj1, ...obj2 };
console.log(obj3); // { a: 1, b: 2, c: 3 }

// 프로퍼티 값 수정
obj3.a= 4;
console.log(obj1); // { a: 1, b: 2 }
console.log(obj3); // { a: 4, b: 2, c: 3 }

 

 

3) 축약 메소드

 

객체의 프로퍼티 값이 함수인 경우 그 함수를 메소드라고 합니다. 기존에 메소드를 선언하는 방법은 다음과 같았습니다.

 

var obj = {
    method: function() {}
}

 

이렇게 선언한 메소드의 내부에 있는 this는 메소드를 호출한 객체에 동적으로 바인딩됩니다.

ES6에서는 this가 정적으로 바인딩되는 화살표 함수라는 것이 생겼으나 이것은 메소드를 만들 때 사용하기엔 적합하지 않습니다. 왜냐하면 화살표함수로 메소드를 만든다면 메소드 내부에 있는 this는 메소드 외부에 있는 this가 가리키는 대상에 바인딩될 것이기 때문에 window객체 등 원치 않은 객체에 바인딩될 확률이 매우 높습니다.

 

ES6에서는 축약 메소드를 이용해 this를 정적으로 바인딩할 수 있습니다.

문법은 다음과 같습니다.

 

const person = {
    name: "cocoder",
    sayName() {
    	console.log(this.name); // this는 메소드가 속한 객체(person)에 정적으로 바인딩.
    }
}

 

 

4) 객체 리터럴을 상속하는 새로운 프로퍼티 __proto__

 

ES5에서 객체 리터럴을 상속하려면 다음과 같이 해야 합니다.

 

var parent = {
    name: 'parent',
    say: function() {
        console.log(name);
    }
};

var child = Object.create(parent); // parent객체를 상속받는 child객체를 생성

child.name = 'child';
child.say(); // child

 

이렇게 하면 child객체의 프로토타입 객체는 parent 객체에 바인딩되어 상속을 구현합니다.

 

ES6에서는 __proto__ 프로퍼티로 리터럴 내부에서 간단하게 부모 객체를 직접 지정할 수 있습니다.

 

const parent = {
  name: 'parent',
  say() {
    console.log(this.name);
  }
};

const child = {
  __proto__: parent,
  name: 'child'
};

child.say(); // child

 

 

 

6. 디스트럭처링 (Destructuring)

 

디스트럭처링은 배열이나 객체에서 필요한 요소만 추출하여 할당하고 싶은 경우에 사용할 수 있는 간편하고 유용한 문법입니다.

 

배열 디스트럭처링

 

배열 디스트럭처링을 하기 위해서 할당 연산자 왼쪽에는 배열 형태의 변수 리스트를 작성합니다.

각 변수가 배열 내에서 위치한 인덱스는 할당 연산자 오른쪽에 있는 배열의 인덱스와 짝지어져 그 인덱스에 해당하는 값을 추출하여 변수에 할당합니다.

 

const arr = [1, 2, 3];
const [one, two, three] = arr;
console.log(one, two, three); // 1 2 3

 

그런데 변수 리스트와 배열의 길이가 일치하지 않는 경우가 있을 수 있습니다.

할당 연산자 왼쪽에 있는 배열의 길이가 더 긴 경우 값을 할당받지 못하는 변수의 값은 undefined가 됩니다.

 

const arr = [1, 2, 3];
const [a, b, c, d] = arr;
console.log(a, b, c, d); // 1 2 3 undefined

 

변수 리스트 중 중간에 비어있는 인덱스가 있는 경우 그 인덱스는 건너뛰고 진행됩니다.

 

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

 

변수 리스트에는 각 변수의 default값을 지정하여 배열로부터 추출된 값이 없을 때 할당할 초기화 값을 지정할 수 있습니다.

 

const arr = [1, 0, 3];
const [a, b = 2, c = 3, d = 4] = arr;
console.log(a, b, c, d); // 1 0 3 4

 

위 예제에서

a는 default값이 없고 arr[0]의 값인 1이 할당됩니다.

b는 default값이 2이지만 arr[1]의 값이 있으므로 그 값인 0이 할당됩니다.

c도 b와 마찬가지로 default값 대신 arr[2]의 값인 3이 할당됩니다.

d는 arr[3]이 존재하지 않아 default값인 4가 할당됩니다.

 

 

 

객체 디스트럭처링

 

객체 디스트럭처링을 하기 위해서 할당 연산자 왼쪽에는 객체 리터럴 형태의 변수 리스트를 작성합니다.

객체 형태의 변수 리스트 안에 있는 각 변수는 할당 연산자 오른쪽에 있는 객체에서 같은 프로퍼티명을 가진 프로퍼티의 값을 추출해 할당받습니다.

 

const obj = { firstName: 'coder', lastName: 'co' };
const { firstName, middleName, lastName } = obj;
console.log(firstName, middleName, lastName); // coder undefined co

 

객체에 없는 프로퍼티를 변수 리스트에 변수명으로 써넣으면 그 변수는 undefined가 됩니다.

 

중첩 객체를 디스트럭처링 하려면 변수 리스트 안에 변수명을 적을 때 계층구조를 적어줘야 합니다.

 

const person = {
    name: "cocoder",
    address: {
        zipCode: "00000",
        city: "Seoul"
    }
};

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

 

위에서 person 객체의 address프로퍼티의 city 프로퍼티의 값을 할당받기 위해 변수 리스트 안에서는 중괄호를 중첩하여 사용했습니다.

 

 

 

 

 

7. Class

 

ES5에는 없는 클래스 문법이 도입되었습니다. 클래스를 정의하고 인스턴스를 생성 하는 방법은 다음과 같습니다.

 

* 예시 코드

class Person {
  // 생성자
  constructor(name) {
    this.name = name;
  }

  introduce() {
    console.log(`My name is ${this.name}.`);
  }
}

// 인스턴스 생성
const me = new Person("cocoder");
me.introduce(); // My name is cocoder.

console.log(me instanceof Person); // true

 

static 키워드를 사용한 메소드는 정적 메소드입니다. 정적 메소드는 인스턴스에서 호출할 수 없고 클래스에서 직접 호출할 수 있습니다. 그래서 정적 메소드 내부에서 this는 인스턴스가 아닌 클래스에 바인딩됩니다.

 

* 예시 코드

class Math {
  constructor(prop) {
    this.prop = prop;
  }

  static plus(a, b) {
    console.log(this); // 클래스에 바인딩된다.
    return a + b;
  }
}

console.log(Math.plus(3, 4)); // 7

const math = new Math();
console.log(math.plus()); // Uncaught TypeError: math.plus is not a function

 

클래스의 상속은 extends 키워드로 구현할 수 있습니다. 부모 클래스와 자식 클래스를 만들어 자식 클래스에 extends 키워드를 붙입니다.

 

* 예시 코드

// 부모 클래스
class Person {
  // 생성자
  constructor(name) {
    this.name = name;
  }

  introduce() {
    console.log(`My name is ${this.name}.`);
  }
}

// Person을 상속받은 자식 클래스
class Worker extends Person {
  constructor(name, job) {
    super(name);
    this.job = job;
  }

  // introduce 메소드는 부모 클래스의 메소드를 오버라이딩한다.
  introduce() {
    super.introduce(); // super키워드를 이용해 오버라이딩되지 않은 부모 클래스의 메소드를 사용한다.
    console.log(`My job is ${this.job}.`);
  }
}

// 인스턴스 생성
const me = new Worker("cocoder", "developer");

// 오버라이딩된 메소드 실행
me.introduce(); // My name is cocoder. My job is developer.

// me는 Worker 클래스의 인스턴스이다.
console.log(me instanceof Worker); // true
// me는 Person 클래스의 인스턴스이다.
console.log(me instanceof Person); // true

 

상속은 extends로 구현하고 오버라이딩은 부모 클래스에서 사용하는 메소드와 같은 메소드명을 사용함으로써 구현할 수 있습니다.

 

자식 클래스의 constructor 내부에서의 super메소드는 부모 클래스의 constructor를 호출합니다. 따라서 생성된 인스턴스는 자식 클래스의 인스턴스이기도 하지만 부모 클래스의 인스턴스이기도 합니다. 또한 super키워드는 부모 클래스의 필드나 메소드를 참조할 수 있습니다. 위 예제에서 자식 클래스의 introduce 메소드 안에 있는 super.introduce()는 부모 클래스의 introduce를 실행합니다.

 

 

 

8. Promise

 

Promise는 비동기 작업을 보기 편한 코드로 처리할 수 있는 기능입니다. 사용법은 다음과 같습니다.

Promise 생성자 함수의 첫번째 파라미터로 비동기적으로 실행되는 함수를 넣어서 인스턴스를 만듭니다. 그러면 비동기 코드가 실행된 결과를 가진 인스턴스가 생성됩니다. 이 인스턴스 객체는 then, catch메소드를 사용하여 반환된 값에 대한 후속 처리를 보기 편한 코드로 작성할 수 있습니다.

 

const promiseInstance = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = Math.random() > 0.5;
    
    if (success) {
      resolve("success");
    } else {
      reject("failure");
    }
  }, 1000);
});

promiseInstance.then(res => {
  console.log(res);
}).catch(err => {
  console.log(err);
});

 

비동기 코드를 사용하는 자바스크립트 라이브러리들은 대부분 프로미스로 구현되어 있습니다. 프로미스는 반환값을 사용하는 인터페이스가 편리합니다. resolve()로 넘겨준 파라미터는 프로미스의 then메소드의 파라미터로 받을 수 있고, reject()로 넘겨준 파라미터는 프로미스의 catch메소드의 파라미터로 받을 수 있습니다.

 

비동기 작업을 반복적으로 사용하기 위해 재사용가능한 구조로 만드려면 프로미스 생성자 함수를 반환하는 함수를 하나 만듭니다.

 

const promise = () => new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = Math.random() > 0.5;
    
    if (success) {
      resolve("success");
    } else {
      reject("failure");
    }
  }, 1000);
});

 

첫 예제와 다르게 이미 만들어진 인스턴스가 아니라 생성자 함수를 반복적으로 호출할 수 있습니다.

 

promise().then(res => {
  console.log(res);
}).catch(err => {
  console.log(err);
});

 

 

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기