[ES6] Class 사용하기(2) - get,set, 클래스에서 generator구현, bind(),화살표함수 , iterable 클래스

권준혁·2020년 11월 1일
0

javascript

목록 보기
14/19
post-thumbnail
post-custom-banner

안녕하세요 이번 포스팅의 내용입니다.

  • class에서 get과 set사용하기

  • class에 generator 함수를 이용해 이터러블 구현하기

  • class에서의 bind()와 화살표함수

지난 게시물의 소스코드를 사용합니다. (Ctrl + Click)

1. class에서 get과 set사용하기

get과 set은 함수를 속성처럼 보이게 해서 복잡성을 숨기는 방법입니다.

class Coupon {
    constructor (price, expiration) {
        this.price = price;
        this.expiration = expiration || '2주'
    }
    get getPriceText () {
        return `$${this.price}`
    }
    get getExpirationMessage () {
        return `이 쿠폰은 ${this.expiration}후에 만료됩니다.`
    }
    set halfPrice(price) {
        this.price = price / 2 
    }
}
const coupon = new Coupon(5)
coupon.halfPrice = 20;
console.log(coupon.price)
console.log(coupon.getPriceText)    // $5

get함수로 getPriceText() 와 getExpirationMessage() 를 설정했고,
set함수로 halfPrice() 를 설정했습니다.

set함수에는 변수처럼 값을 할당할 수 있습니다.

get의 경우 coupon.getPriceText로 접근해 뒤에 괄호를 붙이지않아 속성처럼 보이게 할 수 있습니다.(마지막 줄)


get,set함수가 속성명과 곂칠경우 무한루프에 빠질 수 있습니다.

class Coupon {
    constructor (price, expiration) {
        this.price = price;
        this.expiration = expiration || '2주'
    }
    get price() {
        return this.price;
    }
    set price(price) {
        this.price = `$ ${price}`
    }
}
const coupon = new Coupon(5)
// result
RangeError: Maximum call stack size exceeded

인스턴스 생성과 동시에 에러가 발생합니다.

이럴 경우 코딩컨벤션에 따라 속성명 앞에 밑줄 _ 을 입력해 속성이 비공개라는 점을 표시합니다.

class Coupon {
    constructor (price, expiration) {
        this._price = price;
        this.expiration = expiration || '2주'
    }
    get price() {
        return this._price;
    }
    set price(price) {
        const newPrice = price.toString().replace(/[^\d]/g,'')
        this._price = parseInt(newPrice, 10);
    }
}
const coupon = new Coupon(5)
coupon.price = '15a'
console.log(coupon.price)   // 15

set price()함수는 다음의 과정을 거칩니다.

  1. 인수를 toString()으로 문자열형태로 바꾼다.
  2. 정규표현식에 따라 숫자가 아닌값을 전체검색해서 공백으로 바꾼다.
    대괄호는 문자열표현, ^은 부정식 , \은 숫자, 뒤에 g는 전역검색입니다.
  3. parseInt로 정수로 바꿔 속성값을 변경한다.

2. class에 generator 함수를 이용해 이터러블속성 생성하기

이터러블 속성을 생성하면 컬렉션을 순회할 수 있게됩니다.

class FamilyTree {
    constructor () {
        this.family = {
            name : 'Doris',
            child : {
                name : 'Martha',
                child : {
                    name : 'Dyan',
                    child : {
                        name : 'Bea'
                    }
                }
            }
        }
    }
    getMembers () {
        const family = []
        let node = this.family
        while(node){
            family.push(node.name)
            node = node.child
        }
        return family
    }
}
const family = new FamilyTree()
console.log(family.getMembers())
// result
[ 'Doris', 'Martha', 'Dyan', 'Bea' ]

FamilyTree클래스는 중첩구조의 속성 family를 가지고있습니다. 가계도입니다.
제너레이터를 사용하면 getMember 함수가 필요없어집니다.

class FamilyTree {
...
    생성자 생략
...
    * [Symbol.iterator]() {
        let node = this.family
        while (node) {
            yield node.name
            node = node.child
        }
    }

getMember함수를 없애고 iterator를 넣었습니다.
이제 이 클래스는 iterable입니다.
generator함수를 이용하면 이터레이터를 쉽게 구현할 수 있습니다.
펼침연산자를 이용해 순회해보겠습니다.

const family = new FamilyTree()
console.log([...family])
// result
[ 'Doris', 'Martha', 'Dyan', 'Bea' ]

잠시 짧게 iterator, iterable에 대해서 간단히 보겠습니다.

iterable

  • iterable은 순회가 가능하다는걸 말합니다.
  • iterable하기 위해서는 @@iterator메소드를 구현해야합니다.
    • 이 말은 [Symbol.iterator] key 의 속성을 가져야 한다는 것을 의미합니다
    • @@iterator 메소드가 iterator 객체를 반환하지 않는다면 그것은 잘 정의되지 못한 iterable이라고 할 수 있습니다.
  • built-in iterables 객체에는 Map, Array가 있습니다.

iterator

  • value들을 순회하는 것, next()메소드를 가지고 있고 그 메소드는 value,와 done을 반환하는 object를 가지고 있는 인수없는 함수여야 합니다.
  • done값은 반복을 마쳤을경우 true, 아닐 경우 false입니다.
  • done 속성 자체를 특정짓지 않으면 false
  • value 순회하는 각각의 값입니다 done이 false이면 생략 될 수 있습니다.

정리하면 iterator속성을 제대로 구현했다면 해당 객체는 iterable입니다.
generator은 iterator을 만드는 방법중 가장 쉬운 방법입니다.
generator을 사용하지 않고도 iterator을 [Symbol.iterator] key를 만들어서 구현할 수 있지만 조금 더 복잡합니다.

이제 순회 가능한 클래스가 만들어졌습니다.


3. class에서의 bind()와 화살표함수

이름을 인수로 받아 인사메세지를 반환하는 함수입니다.

class Greeting {
    constructor () {
        this.message = '님 안녕하세요?'
    }
    setMessage(name) {
        return `${name}${this.message}`
    }
    setMessages (...names) {
        return names.map(this.setMessage)
    }
}

const greeting = new Greeting()
const names = ['a','b','c']
console.log(greeting.setMessage('a'))
// a님 안녕하세요?
console.log(greeting.setMessages(...names))
// TypeError: Cannot read property 'message' of undefined

배열메서드를 사용하는 경우 에러를 반환합니다.

지난 포스팅에서 객체리터럴과 마찬가지로 배열메서드 map()이 새로운 문맥을 만들어 this바인딩을 생성하기 때문에 에러가 발생합니다.

1. 화살표함수 사용한 해결

class Greeting {
    constructor () {
        this.message = '님 안녕하세요?'
    }
    setMessage = (name) => `${name} ${this.message}`
    setMessages (...names) {
        return names.map(this.setMessage)
    }
}
const greeting = new Greeting()
const names = ['a','b','c']
console.log(greeting.setMessages(...names))
// result
[ 'a 님 안녕하세요?', 'b 님 안녕하세요?', 'c 님 안녕하세요?' ]

2. bind() 메서드 사용한 해결

2-1 메서드에서 연결

class Greeting {
    constructor () {
        this.message = '님 안녕하세요?'
    }
    setMessage (name) {
        return `${name}${this.message}`
    }
    setMessages (...names) {
        return names.map(this.setMessage.bind(this))
    }
}

메서드에서 연결할 때의 단점은 다른메서드에서 setMessage를 사용하려면 다시 bind()로 묶여줘야 한다는 겁니다.

2-2 생성자에서 연결

class Greeting {
    constructor () {
        this.message = '님 안녕하세요?'
        this.setMessage = this.setMessage.bind(this)
    }
    setMessage (name) {
        return `${name}${this.message}`
    }
    setMessages (...names) {
        return names.map(this.setMessage)
    }
}

생성자에서 연결할 때의 단점은 생성자가 비대해집니다


profile
웹 프론트엔드, RN앱 개발자입니다.
post-custom-banner

0개의 댓글