ES6부터 새롭게 생겨난 반복문 for...of 에 대해 알아보고자 한다.

for...of 문반복가능한 객체(Array, Map, Set, String, TypedArray, arguments 객체 등)를 반복하는 문법이다.

 

▷ 구문

for (variable of iterable) {
    statement
}

variable: 각 반복에 서로 다른 속성값이 variable에 할당된다.

iterable : 반복되는 열거가능(enumerable)한 속성이 있는 객체.

 

예제를 통해 쉽게 접해보자.


▷ 예제1) Array에 대한 반복

let arr = [1, 2, 3];

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

 

▷ 예제2) String에 대한 반복

let str = 'mine';

for (let value of str) {
  console.log(value); //m, i, n, e
}

 

▷ 예제3) TypedArray에 대한 반복

let iterable = new Uint8Array([0x00, 0xff]);

for (let value of iterable) {
  console.log(value); // 0 , 255
}

 

▷ 예제4) Map에 대한 반복

let animals = new Map();
animals.set("dog", "woof")
       .set("cat", "meow")
       .set("elephant", "toot");

for (let name of animals.keys()) {
    console.log(name); // dog, cat, elephant
}

for (let howling of animals.values()) {
    console.log(howling); //woof, meow, toot
}

for (let [key, value] of animals) {// animals는 animals.entries()와 동일
    console.log(`${key} goes ${value}`);
    // dog goes woof , cat goes meow, elephant goes toot
}

 

▷ 예제5) Set에 대한 반복

let mineSet = new Set([0, 1, 2, 3]);

for (let key of mineSet.keys()) {
    console.log(key); // 0, 1, 2, 3
}

for (let value of mineSet.values()) {
    console.log(value); // 0, 1, 2, 3
}

for (let data of mineSet) {// mineSet mineSet.entries()와 동일
    console.log(data); // 0, 1, 2, 3
}

참고 : https://mine-it-record.tistory.com/473

 

[ES6] Map(), Set() 객체의 특징과 사용법

ES6에서 새롭게 등장한 자료구조인 맵(Map) 과 셋(Set) 객체에 대해 알아보자. 1. 맵(Map) - 맵(Map)은 키가 있는 데이터를 저장한다는 점에서 객체(obj)와 유사하다. - 객체는 키값으로 문자열만 사용 가

mine-it-record.tistory.com

참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/for...of

 

for...of - JavaScript | MDN

for...of 명령문은 반복가능한 객체 (Array, Map, Set, String, TypedArray, arguments 객체 등을 포함)에 대해서 반복하고 각 개별 속성값에 대해 실행되는 문이 있는 사용자 정의 반복 후크를 호출하는 루프를

developer.mozilla.org

반응형

ES6에서 새롭게 등장한 자료구조인 맵(Map) 과 셋(Set) 객체에 대해 알아보자.


1. 맵(Map)

- 맵(Map)은 키가 있는 데이터를 저장한다는 점에서 객체(obj)와 유사하다.

- 객체는 키값으로 문자열만 사용 가능하지만 맵(Map)은 다양한 자료형을 허한다.

- 객체는 삽입된 순서를 기억하지 못하지만 맵(Map)은 삽입된 순서를 기억해 반복문 사용시 삽입 순서대로 반된다.

 

1-1. 주요 메서드와 프로퍼티

맵(Map)에서 주요 사용되는 메서드와 프로퍼티는 다음과 같다.

  • new Map(iterable) : 맵 객체를 만든다. (매개변수로 [키, 값] 으로 이루어진 배열이나 이터러블 객체를 전달하면 그안의 값을 복사해 맵에 넣어준다.)
  • set(key, value) : key를 이용해 value를 저장한다.
  • get(key) : key에 해당하는 값을 반환한다. (key가 존재하지 않으면 undefined를 반환한다.)
  • has(key) : key가 존재하면 true, 존재하지 않으면 false를 반환한다.
  • delete(key) : key에 해당하는 값을 삭제한다. (key가 삭제되었으면 true, 키가 없는 등 삭제가 안됐으면 false 반환)
  • clear() : 맵(Map) 안의 모든 요소를 제거한다.
  • size : 맵 객체 안에 있는 요소의 개수를 반환한다.

▷ 예제 1 ) 주요 메서드와 프로퍼티 기본 사용법

let animals = new Map([
    ["wolf", "howling"]
]);

animals.set("dog", "woof");
// use set Chaining
animals.set("cat", "meow")
       .set("elephant", "toot");
       
console.log(animals);
// Map(4) {'wolf' => 'howling', 'dog' => 'woof', 'cat' => 'meow', 'elephant' => 'toot'}

animals.get("dog"); // woof
animals.get("fox"); // undefined

animals.size; // 4

animals.has("elephant"); // true
animals.has("bird"); // false

animals.delete("dog"); // true
animals.delete("bird"); // false

console.log(animals);
// Map(3) {'wolf' => 'howling', 'cat' => 'meow', 'elephant' => 'toot'}

animals.clear();
console.log(animals); // Map(0) {size: 0}

 

1-2. 맵의 요소 반복하기

맵에서는 세 가지 메서드를 통해 맵의 각 요소를 뽑아 반복 작업을 하거나, 배열처럼 내장 메서드인 forEach를 사용한다.

  • keys() : 각 요소의 를 모은 반복 가능한 iterable(이터러블) 객체를 반환한다.
  • values() : 각 요소의 을 모은 반복 가능한 iterable(이터러블) 객체를 반환한다.
  • entries() : 요소의 [키, 값]을 한 쌍으로 하는 iterable(이터러블) 객체를 반환한다.
  • forEach(callFuc(value, key, map)) : 배열과 유사하게 내장 메서드인 forEach 반복문을 지원한다.

▷ 예제 2 ) 반복을 위한 메서드의 기본 사용법

let animals = new Map();
animals.set("dog", "woof")
       .set("cat", "meow")
       .set("elephant", "toot");

for (let name of animals.keys()) {
    console.log(name); // dog, cat, elephant
}

for (let howling of animals.values()) {
    console.log(howling); //woof, meow, toot
}

for (let [key, value] of animals) {// animals는 animals.entries()와 동일
    console.log(`${key} goes ${value}`);
    // dog goes woof , cat goes meow, elephant goes toot
}

animals.forEach((value, key, map) => {
    console.log(`${key} : ${value}`);
    // dog : woof, cat : meow, elephant : toot
});

 

1-3. 객체를 맵으로, 맵을 객체로 변환하기

맵은 키가있는 데이터를 저장한다는 점에서 객체와 유사하다고 하였는데, 그래서 그런지 서로 변환이 가능하다.

 

▷ 예제 3 ) 객체를 맵으로 변환하기

let obj = {
    dog : "woof",
    cat : "meow",
    elephant : "toot"
}

let animals = new Map(Object.entries(obj));
console.log(animals);
// Map(3) {'dog' => 'woof', 'cat' => 'meow', 'elephant' => 'toot'}

 

▷ 예제 4 ) 맵을 객체로 변환하기

let animals = new Map();
animals.set("dog", "woof")
       .set("cat", "meow")
       .set("elephant", "toot");
       
let obj = Object.fromEntries(animals);
console.log(obj); //{dog: 'woof', cat: 'meow', elephant: 'toot'}

맵에서 객체로 변환할때는 모든 key값이 문자열로 바뀌니 주의해야한다.


2. 셋(Set)

- 셋(Set)은 중복을 허용하지 않는 값을 모아놓은 특별한 컬렉션이다.

- 셋(Set)은 키가 없는 값을 저장한다는 점에서 배열(array)과 유사하다.

2-1. 주요 메서드와 프로퍼티

셋(Set)에서 주요 사용되는 메서드와 프로퍼티는 다음과 같다.

  • new Set(iterable) : 셋 객체를 만든다. (배열이나 이터러블 객체를 전달하면 그 안의 값을 복사해 셋에 넣어준다.)
  • add(value) : 값을 추가한다.
  • delete(value) : 값을 제거한다. (삭제되었으면 true, 값이 없는 등 삭제가 안됐으면 false 반환)
  • has(value) : 셋(Set) 내에 값이 존재하면 true, 아니면 false를 반환한다.
  • clear() : 셋(Set) 안의 모든 요소를 제거한다.
  • size : 셋(Set) 객체 안에 있는 요소의 개수를 반환한다.

▷ 예제 1 ) 주요 메서드와 프로퍼티 기본 사용법

let mineSet = new Set([0]);

// use add Chaining
mineSet.add(1).add(5); // Set { 1, 5 }
mineSet.add(5); // Set { 1, 5 }
mineSet.add('some text'); // Set { 1, 5, 'some text' }
let o = {a: 1, b: 2};
mineSet.add(o);
mineSet.add({a: 1, b: 2}); // o와 다른 객체를 참조하므로 괜찮음
console.log(mineSet);
// Set(6) {0, 1, 5, 'some text', {a: 1, b: 2}, {a: 1, b: 2}}

mineSet.has(1); // true
mineSet.has(o); // true
mineSet.has(3); // false

mineSet.size; // 6

mineSet.delete(5); // true
mineSet.delete(3); // false

console.log(mineSet);
// Set(5) {0, 1, 'some text', {a: 1, b: 2}, {a: 1, b: 2}}

mineSet.clear();
// Set(0) {size: 0}

 

2-2. 셋(Set)의 요소 반복하기

셋 역시도 맵과 마찬가지로 세가지의 반복 가능한 요소롤 뽑아내는 메서드와 forEach 메서드가 존재한다.

  • keys() : 셋 내의 모든 값을 포함하는 반복 가능한 iterable(이터러블) 객체를 반환한다.
  • values() : keys()와 동일한 작업을 한다. (맵과의 호환성을 위해 만들어진 메서드다.)
  • entries() : 셋 내의 각 값을 이용해 만든 [value, value] 배열을 포함하는 이터러블 객체를 반환한다. (맵과의 호환성을 위해 만들어진 메서드다.)
  • forEach(callFuc(value, valueAgain, set)) : 내장 메서드인 forEach 반복문이다. (맵과의 호환성을 위해 콜백 변수가 3개이며, 두번째 valueAgain 역시 첫번째 value 와 동일한 값을 반환하다.)

▷ 예제 2 ) 반복을 위한 메서드의 기본 사용법

let mineSet = new Set([0, 1, 2, 3]);

for (let key of mineSet.keys()) {
    console.log(key); // 0, 1, 2, 3
}

for (let value of mineSet.values()) {
    console.log(value); // 0, 1, 2, 3
}

for (let data of mineSet) {// mineSet mineSet.entries()와 동일
    console.log(data); // 0, 1, 2, 3
}

mineSet.forEach((value, valueAgain, set) => {
    console.log(`${value} : ${valueAgain}`);
    // 0 : 0, 1 : 1, 2 : 2, 3 : 3
});

 

2-3. 배열을 셋으로, 셋을 배열로 변환하기

셋은 배열과 유사하다고 말했었는데, 서로 상호작용하는것에 대해 배워보자.

 

▷ 예제 3 ) 배열을 셋으로 변환하기

const array = [0, 1, 2, 3];

let mineSet = new Set(array);
console.log(mineSet); // Set(4) {0, 1, 2, 3}

 

▷ 예제 4 ) 셋을 배열로 변환하기

let mineSet = new Set([0, 1, 2, 3]);

// 1. 전개 연산자 사용
const arr1 = [...mineSet];
console.log(arr1); // [0, 1, 2, 3]

// 2. Array.from 사용
const arr2 = Array.from(mineSet);
console.log(arr2); // [0, 1, 2, 3]

참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Keyed_collections

 

키기반의 컬렉션 - JavaScript | MDN

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

developer.mozilla.org

참고 : https://ko.javascript.info/map-set

 

맵과 셋

 

ko.javascript.info

반응형

비동기 처리를 도와주는 async와 await에 대해 알아보자.

 

기존의 비동기 처리 방식인 콜백 함수와  프로미스(Promise)의 단점을 보완하고 개발자가 일기 좋은 코드를 작성할 수 있게 도와주는 문법인데, 제목에서 말한것처럼 Promise의 syntactic sugar라고 보면 된다.

 

기존에 없던게 아니고 그냥 좀더 Promise를 사용하기 쉽게 도와주는 문법이라 생각하면 될 것 같다.

(object와 class의 관계랄까...?)

 

코드를 통해 하나씩 알아가보자.


1. async 사용법

// 1.함수로 만들어진 기본 promise 문법 (async x)
function blog() {
    return new Promise((resolve, reject) => {
        resolve('mine-it-record');
    });
}

const bloger = blog();
bloger.then(result => console.log(result));

// 2. async를 사용하여 위 코드를 변경하기 (async o)
async function blog() {
    return 'mine-it-record';
}

const bloger = blog();
bloger.then(result => console.log(result));

 

위 예제를 보면 알겠지만 일반 Promise 문법을 사용했을때와 마찬가지로 아래 async로 이루어진 메서드 역시 동일한 방식으로 then을 통해 제어하는 것을 볼 수 있는데,

이는 본문 시작부분에서 얘기했던것처럼 async는 Promise의 syntactic sugar 이기 때문에 Promise 문법을 좀 더 사용하기 쉽게 도와주는 역할을 해주기 때문이다.

 

그래서 위 예제를 직접 실행해보면 2번 예제 역시 Promise를 반환해 주는것을 볼 수 있다.

 

2. await 사용법

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function getMine(){
    await delay(1000);
    return 'mine';
}

async function getIt(){
    await delay(1000);
    return 'it';
}

async function createBlogName(){
    const mine = await getMine();
    const it = await getIt();

    return `${mine}-${it}-record`;
}

createBlogName().then(result => {
    console.log(result);
});

await는 async와 꼭 같이 사용해야한다. (async 설정이 안되어있는 일반 함수에서는 사용이 불가능하다.)

 

await는 말 그대로 "기다려" 라는 의미인데,

await가 사용된 비동기 통신이 완료될때까지 기다린 다음에야 다음으로 넘어간다.

 

위 예제로 설명을 해보자면 각각 delay나 getMine, getIt에 await가 사용되었는데,

그 메서드들이 완전히 실행이 완료되어야 다음 줄이 실행된다는 의미이다.

 

await를 쓰는 가장 큰 이유는 콜백지옥 때문인데, then 역시도 남발하면 콜백지옥과 별다를게 없기 때문에 await를 사용해주면 가독성이 더 좋은 코드가 완성된다.

 

3. async & await 예외 처리

...

async function createBlogName(){
    try {
        const mine = await getMine();
        const it = await getIt();
    } catch (error) {
        console.log(erro);
    }

    return `${mine}-${it}-record`;
}

...

이런식으로 가볍게 try catch문을 사용해서 잡아주면 된다.


참고 : https://developer.mozilla.org/ko/docs/Learn/JavaScript/Asynchronous/Async_await

 

async와 await를 사용하여 비동기 프로그래밍을 쉽게 만들기 - Web 개발 학습하기 | MDN

Javascript에 대한 최신 추가 사항은 ECMAScript 2017 JavaScript 에디션의 일부인 async functions 그리고 await 키워드는 ECMAScript2017에 추가되었습니다. 이 기능들은 기본적으로 비동기 코드를 쓰고 Promise를

developer.mozilla.org

 

반응형

ES6부터 정식으로 지원해주기 시작한 Promise 객체에 대해 알아보자.

 

Promise란 말 그대로 '약속(프로미스)'을 의미하는데,

이는 최종 결과를 반환한다기보다는 미래의 어떤 시점에 결과를 제공하겠다는 의미로 보면 된다.

 

코드를 통해 하나씩 알아가보자.


1. Promise() 기본 구조

const promise = new Promise((resolve, reject) => {
    // ...비동기 처리할 영역
    // 보통 network 통신에 사용된다.
});

우선 Promise() 객체의 기본 구조에 대해 알아보자.

 

Promise 객체는 3가지 상태를 가진다.

  • 대기(pending) : 이행하지도, 거부하지도 않은 초기 상태
  • 이행(fulfilled) : 연산이 성공적으로 완료됨
  • 거부(rejected) : 연산이 실패함

 

그리고 이 3가지 상태와 Promise 객체의 콜백함수의 인자인 resolve, reject는 서로 밀접한 관계가 있는데,

const promise = new Promise((resolve, reject) => {
    console.log('pending');
    setTimeout(() => {
        resolve('fulfilled');
        // reject(new Error('rejected'));
    }, 2000);
});

이런식으로 resolve와 reject가 실행되지 않은 상태가 대기(pending) 상태이고,

resolve가 실행되면 이행(fulfilled) 상태, reject가 실행되면 거부(rejected) 상태가 된다.


2. Promise() 사용 (.then, .catch, .finally)

- Promise를 만들었다면 이걸 사용해야하는데, 사용할 때는 then, catch, finally를 통해 사용이 가능하다.

// 1. resolve
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('mine-it-record');
    }, 2000);
});

promise.then( result => {
    console.log(result);
});

// 처리결과
// mine-it-record

// 2. reject
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(new Error('error!'));
    }, 2000);
});

promise.then( result => {
    console.log(result);
})
.catch(error => {
    console.log(error);
}).finally(() => {
    console.log('finally');
});

// 처리결과
// error!
// finally

 

위 예제에서 then, catch, finally 를 다뤘는데 하나씩 알아보자.

 

resolvethen을 통해 결과값을 받아서 처리가 가능하고,

rejectcatch를 통해 결과값을 받아서 처리가 가능하다. 

 

마지막으로 finally는 Promise가 resolve를 사용하거나 reject를 사용해도 상관없이 무조건 실행되는 녀석이라 보면 된다.

 

Promise 흐름 이미지 (출처 : MDN Web Docs)


3. 여러 Promise 연결하기 (Promise Chaining)

const number = new Promise((resolve, reject) => {
    setTimeout(() => resolve(3), 1000);
});

number
 .then(num => num * 3) // 9
 .then(num => num * 3) // 27
 .then(num => {
     return new Promise((resolve, reject) => {
        setTimeout(() => resolve(num / 3), 1000);
     });
 })
 .then(num => console.log(num)); // 9

Promise의 특징 중 하나인 Chaining 인데, then을 나열함으로써 순차적으로 제어가 가능하다.

 

then을 사용하고나서 마지막에 return을 해준다음 다음 then에서 받아서 사용하는 방식인데,

여기서 비동기 통신 역시 포함되어 비동기 통신을 간단하게 순차적으로 제어할 수 있다는점이 큰 장점이다.


4. Promise 정적 메서드 (Promise.all / Promise.race)

Promise 자체에도 존재하는 메서드들이 있는데, 몇가지에 대해서만 다뤄볼까 한다. (Promise.all을 자주 사용했었다.)

// 1. Promise.all
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values); // expected output: Array [3, 42, "foo"]
});

// 2. Promise.race
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value); // expected output: "two"
  // Both resolve, but promise2 is faster
});

 

Promise.all 과 Promise.race에 대한 예제인데 예제 자체는 MDN에 있는걸 그대로 사용하였다.

 

Promise.all은 매개변수로 주어진 배열의 모든 Promise 이행된후 .then을 실행한다.

모든 Promise가 이행된다 하였는데, 이는 하나라도 실패(reject)된다면 그냥 에러가 발생하여 catch로 잡아주면 된다.

 

Promise.race는 매개변수로 주어진 배열중에 가장 먼저 실행이 완료된것(가장 빠른것)만 then으로 받고 끝이난다.


참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise

 

Promise - JavaScript | MDN

Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.

developer.mozilla.org

반응형

ES6부터는 자바스크립트 또한 Class문법을 사용할 수 있게 되었다.

 

자바스크립트에서의 Class는 함수라는 것을 알아두고 클래스(Class)문법에 대해 하나씩 알아가보자.


1. 클래스(Class) 정의

1-1. 클래스 선언

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

우선 클래스를 선언하는 방식은 매우 간단한데, 위 예제처럼 [ Class 클래스명 ] 구조로 선언해주면 된다.

 

위에서 클래스를 함수라고 설명했지만 예외가 있는게, 클래스는 호이스팅(Hoisting)이 불가능하기 때문에 반드시 먼저 선언하고 인스턴스를 생성하거나 호출해야한다.

 

1-2. 클래스 표현식

// unnamed
let Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// 출력: "Rectangle"

// named
let Rectangle = class Rectangle2 {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// 출력: "Rectangle2"

클래스 표현식은 클래스를 정의하는 또 다른 방법이다.

위 예제를 보면 알겠지만 클래스 표현식은 이름을 가질 수도 있고, 갖지 않을 수도 있다.

 


2. Class body와 메서드 정의

- Class body는 중괄호 {} 로 묶여 있는 안쪽 부분을 의미하며, 여기에 생성자(constructor)나 메서드 그리고 필드값을 정의하여 사용할 수 있다.

- 지금까지 스크립트 구문은 언제나 항상 다른 언어에 비해 느슨한 부분이 있었는데, 클래스의 본문엄격모드(strict mode)에서 실행된다. 즉, 여기에 적힌 코드는 성능 향상을 위해 더 엄격하게 관리하겠다는 의미이다.

 

2-1. 생성자(Constructor)

// 클래스 선언
class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

// 인스턴스 생성
const square = new Rectangle(10, 10);
console.log(square); // Rectangle {height: 10, width: 10}

생성자(Constructor)는 인스턴스를 생성하고 클래스 필드를 초기화하기 위한 특수한 메서드이다.

생성자는 클래스 내에 한개만 존재할 수 있으며, 인스턴스를 생성할 때 new 연산자와 함께 호출한 것이 생성자이다.

(자바를 기준으로 보자면 클래스명과 생성자 명은 동일한데 자바스크립트 문법에서는 생성자명이 생략되어 construcotr만 선언되어 있는것 같다.)

 

여기서 생성자는 생략이 가능하지만, 어차피 생략해봤자 필수 이기 때문에 자동으로 constructor() {}가 포함되어 있다.

 

그리고 나중에 설명할 부분이지만 클래스에서는 상속이 존재하는데,

클래스에서 상위 클래스의 생성자를 호출할 때 super 키워드를 사용한다. (나중에 상속과 같이 다뤄보자.)

 

2-2. 메서드(Method)와 정적 메서드(Static Method) 그리고 필드

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  
  // Static
  static calcAreaName1 = "custom";
  static calcArea1(height, width) {
    return (height + 100) * width;
  }
    
  calcAreaName2 = "normal"
  calcArea2() {
    return this.height * this.width;
  }
  
}

const square = new Rectangle(10, 10);
console.log(square.calcArea1); // undefined
console.log(square.calcAreaName1); // undefined
console.log(square.calcArea2()); // 100
console.log(square.calcAreaName2); // normal

console.log(Rectangle.calcArea1(10, 10)) // 1100
console.log(Rectangle.calcAreaName1) // custom

 

위 예제에서 메서드와 정적 메서드를 다루었다.

메서드 같은 경우에는 클래스의 인스턴스화를 통해 호출이 가능하며,

정적 메서드 같은 경우에는 클래스의 인스턴스화를 통해 호출이 불가능하고 클래스에 직접적으로 접근하여 호출할 수 있다.

 

* 설명은 메서드를 기준으로 작성하였지만 위 예제처럼 클래스 필드도 static에 대해서 동일하다.

 

2-3.  Public과 Private 필드

class Rectangle {
  height = 0;
  width;
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

필드를 먼저 선언함으로써 self-documenting 역할을 함과 동시에 public 멤버 변수의 역할을 수행한다.

public 필드는 다른 예제에서도 봤겠지만 생성자나 함수를 통해 동적으로 필드를 생성할 수 있다.

 

class Rectangle {
  #height = 0;
  #width;
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}

// Uncaught SyntaxError: Private field '#height' must be declared in an enclosing class
class Rectangle {
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}

위 예제처럼 # 을 사용하여 private필드를 선언할 수 있고,

클래스 내부에서만 읽고 쓰기가 가능해 외부에서 접근이 불가능하다.

 

private필드를 사용할 때 주의할 점
class body에서 무조건 맨 앞에 선언되어야 한다는 점과 생성자나 함수를 통해 나중에 동적으로 생성할 수 없다.

 

그리고 private 필드 역시 static과 합쳐서 사용이 가능하다.

 

* 여기서 잠깐 의문인점에 대해 잠깐 정리하고 가려고 한다.

왜 private과 public은 자바에서는 접근제한자라며 메서드나 변수에 전부 사용이 가능한데, ES6에서는 필드라고 정의한 것인지 의문이 들었다. 메서드에서는 사용이 불가능한 것일까?

class Calculator {
  a = 0;
  #b = 1;
  
  plus() {
    return this.#b;
  }
  
  minus() {
    return this.a;
  }
  
  #accumulator(x) {
    return x + x;
  }
  
  test(){
    return this.#accumulator(5);
  }
}

const calc = new Calculator();
console.log(calc); // Calculator {a: 0, #accumulator: ƒ, #b: 1}

간단하게 테스트를 해본 결과 메서드에서도 사용이 가능하지만, 뭔가 console.log()에 필드만 나오게 되는데 private method를 선언해준게 console.log()에 출력되는걸 보면 필드로 인식하나보다.


2-4.  getter 와 setter

자바에서는 private 접근제한자를 사용하기위해 getter와 setter를 사용하는데, 

ES6 클래스 문법에도 역시 getter와 setter가 존재하기는 한다.

class Rectangle {
  constructor(height, width) {
    this._height = height;
    this._width = width;
  }
  
  // Setter
  set width(elem) {
    this._width = elem;
  }
  
  set height(elem) {
    this._height = elem;
  }
  
  // Getter
  get area() {
    return this.calcArea();
  }
  
  // 메서드
  calcArea() {
    return this._height * this._width;
  }
}

const square = new Rectangle(10, 10);

console.log(square); // Rectangle {_height: 10, _width: 10}
console.log(square.area); // 100

square.width = 200;
square.height = 30;
console.log(square.width); // undefined
console.log(square.area); // 6000

 

getter 와 setter 각각 get 과 set 이라는 키워드를 메서드 이름 앞에 사용하여 정의한다.

예제 아래를 보면 알겠지만 getter와 setter로 사용된 메서드 이름은 클래스 필드처럼 호출해 사용할 수 있다.

 

즉, getter와 setter는 프로퍼티처럼 참조하는 형식으로 사용하며 참조시에 메서드가 호출되는 형식인 것이다.

getter는 무조건 return을 해줘야하며, setter는 무조건 하나의 파라미터가 있어야한다.

 

getter와 setter를 통해서 private 필드에 대해 제어하는것 역시 가능하다.

 


3. 클래스 상속

3-1. extends 와 super 키워드의 기본 사용법

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // super class 생성자를 호출하여 name 매개변수 전달
  }

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

let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.

위 예제에서 this.name을 보면 상위 클래스를 상속받아 하위클래스에서 사용된다.

그리고 상속의 특징 중 하나인 오버라이딩이 있는데, speak 메서드를 보면 상위 클래스의 메서드를 하위 클래스가 재정의하여 사용하고 있는것을 볼 수 있다.

 

하위 클래스(Dog)를 보면 생성자(constructor) 부분에서 super 키워드를 통해 상위 클래스로 값을 전달하였는데,

여기서 한가지 중요한 점은 클래스 상속을 진행하였으면 하위 클래스의 constructor에서 super()를 무조건 호출해줘야 한다는 점이다.

 

만약 호출하지 않으면 this에 대한 참조 에러가 발생한다.

class Animal {}

class Dog extends Animal {
  constructor() {}
}

let d = new Dog();
// Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

 

위에서 하위 클래스의 constructor에서 super()를 무조건 호출해줘야 한다고 하였는데, 만약 사용자가 constructor를 생략한다면 자동으로 super()가 실행된다.

 

super 키워드에 대해 조금 더 알아보자면 말 그대로 상속받은 상위 클래스에 접근하기 위함이다.

즉, 상위 클래스의 메서드나 필드에 접근하고자 할때 super 키워드를 사용하면 된다.

class Animal {
  constructor(name) {
    this.name = name
  }
  
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  speak() {
    super.speak();
    console.log(`${this.name} dog.`);
  }
}

let d = new Dog("happy");
d.speak();
// happy makes a noise.
// happy dog.

 

3-2. 정적 메서드 상속

class Animal {
  static speak() {
    return 'War!War!';
  }
}

class Dog extends Animal {
  static speak() {
    return `Dog ${super.speak()}`;
  }
  
  sickSpeak() {
    return `sick Dog ${super.speak()} ...`;
  }
}

console.log(Animal.speak()); // 'War!War!'
console.log(Dog.speak());  // 'Dog War!War!'
console.log(new Dog().sickSpeak());
// Uncaught TypeError: (intermediate value).speak is not a function

간단하게 설명하자면

하위 클래스의 정적 메서드에서는 상위 클래스의 정적 메서드를 참조할 수 있으나,

하위 클래스의 일반 메서드에서는 상위 클래스의 정적 메서드를 참조할 수 없다.

 

3-3. 함수 기반 상속

function Animal (name) {
  this.name = name;
}

Animal.prototype.speak = function () {
  console.log(`${this.name} makes a noise.`);
}

class Dog extends Animal {
  speak() {
    console.log(`${this.name} barks.`);
  }
}

let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.

 

3-4. 객체(Object) 기반 상속

const Animal = {
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
};

class Dog {
  constructor(name) {
    this.name = name;
  }
}

// 이 작업을 수행하지 않으면 speak를 호출할 때 TypeError가 발생합니다
Object.setPrototypeOf(Dog.prototype, Animal);

let d = new Dog('Mitzie');
d.speak(); // Mitzie makes a noise.

클래스 생성자가 없는 객체를 상속받기 위해서는 Object.setPrototypeOf() 메서드를 사용하면 가능하다.


참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes

 

Classes - JavaScript | MDN

Class는 객체를 생성하기 위한 템플릿입니다. 클래스는 데이터와 이를 조작하는 코드를 하나로 추상화합니다. 자바스크립트에서 클래스는 프로토타입을 이용해서 만들어졌지만 ES5의 클래스 의

developer.mozilla.org

 

참고 : https://poiemaweb.com/es6-class

 

Class | PoiemaWeb

자바스크립트는 프로토타입 기반(prototype-based) 객체지향형 언어다. 비록 다른 객체지향 언어들과의 차이점에 대한 논쟁들이 있긴 하지만, 자바스크립트는 강력한 객체지향 프로그래밍 능력들을

poiemaweb.com

반응형

ES6에서 객체 리터럴 프로퍼티 기능이 향상되었는데, 향상된 객체 릴터럴에 대해 알아보자.


1. 객체 프로퍼티 축약 표현

//ES5
var mine = 'Mine';
var it_record = 'ItRecord'
var fn = function(){return 'fn'};

var obj = {
  mine : mine,
  it_record : it_record,
  fn : fn
}

console.log(obj); // {mine: 'Mine', it_record: 'ItRecord', fn: ƒ}

//ES6
let mine = 'Mine';
let it_record = 'ItRecord'
let fn = () => 'fn';

const obj = {mine, it_record, fn}

console.log(obj); // {mine: 'Mine', it_record: 'ItRecord', fn: ƒ}

ES5에서는 프로퍼티 명과 프로퍼티 값을 하나하나 선언해주었는데,

ES6에서는 프로퍼티 값으로 변수를 사용하는 경우에는 프로퍼티 명을 생략할 수 있다.

이렇게 생략할 경우에는 프로퍼티 명은 해당 변수의 이름으로 자동 생성된다.

 

2. 객체 프로퍼티 키 계산식 사용 (키값 동적 생성)

//ES5
var it = 'IT';
var record = 'RECORD';
var obj = {};

obj[it + '-' + record] = 'it-record';

console.log(obj); // {IT-RECORD: 'it-record'}

//ES6
let it = 'IT';
let record = 'RECORD';

const obj = {
  [`${it}-${record}`] : 'it-record'
};

console.log(obj); // {IT-RECORD: 'it-record'}

ES5에서는 프로퍼티 키를 동적으로 생성하려면 객체 리터럴 외부에서 대괄호 표기법을 사용하여 생성하였다면,

ES6에서는 객체 리터럴 내부에서도 프로퍼티 키를 동적으로 생성할 수 있다.

 

3. 메서드 생성 방식 간소화 (축약 표현)

//ES5
var obj = {
  fn : function() {
    console.log('mine-it-record');
  }
};

obj.fn(); // mine-it-record

//ES6
const obj = {
  fn() {
    console.log('mine-it-record');
  }
}

obj.fn(); // mine-it-record

ES5에서는 메서드를 선언할때 함수 선언식을 사용하여 할당하는데,

ES6에서는 function 키워드를 생략한 축약 표현식을 사용하여 할당할 수 있다.

 

4. __proto__ 프로퍼티에 의한 상속

const chicken = {
  name : 'chicken',
  fn() {
    console.log(this.name);
  }
}

const chick = {
  name : 'Chick',
  __proto__ : chicken
}

console.log(chicken.fn()); // chicken
console.log(chick.fn()); // Chick

ES6에서는 객체 리터럴 내부에서 __proto__ 프로퍼티를 직접 설정할 수 있다.

이는 __proto__를 통해 다른 객체를 직접 바인딩하여 상속을 표현할 수 있음을 의미한다.


참고 : https://poiemaweb.com/es6-enhanced-object-property

 

Enhanced Object property | PoiemaWeb

ES6에서는 객체 리터럴 프로퍼티 기능을 확장하여 더욱 간편하고 동적인 객체 생성 기능을 제공한다.

poiemaweb.com

반응형