Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 문자열
- toCharArray
- 스프링 빈
- Kotlin
- 개발자 회고록
- 백준 javascript
- 프로그래머스
- 고잉버스
- 명령어
- 자바
- JavaScript
- 백준 java
- java 백준 1차원 배열
- map
- 반복문
- GoingBus
- 자바스크립트 코딩의 기술
- 리눅스마스터 3과목
- 월간코드챌린지
- Java
- 카카오
- 코테
- 스프링 컨테이너
- 연습문제
- 코딩테스트
- 리눅스마스터1급
- Memoir
- 리눅스
- 리눅스마스터 1급 정리
- Linux
Archives
- Today
- Total
hoon's bLog
Javascript 자바스크립트 코딩의 기술 8장 클래스로 인터페이스를 간결하게 유지하라 (2) 본문
반응형
8장 클래스로 인터페이스를 간결하게 유지하라
tip40. get과 set으로 인터페이스를 단순하게 만들어라
이전 팁에서 사용한 코드를 활용하여, 클래스는 아래 코드처럼 속성에 접근하여 변경도 가능하다.
class Coupon {
constructor(price, expiration) {
this.price = price;
this.expiration = expiration || '2주';
}
getPriceText() {
return `$ ${this.price}`;
}
getExpirationMessage() {
return `이 쿠폰은 ${this.expiration} 후에 만료됩니다.`;
}
}
const coupon = new Coupon(5);
coupon.price = '$10';
coupon.getPriceText(); // '$ $10'
export default Coupon;
- line 16 : Coupon 인스턴스 생성
- line 17 : line 16에서 설정된 price의 값 5의 속성을 변경하여 '$10'으로 설정!!
- line 20 : Coupon 클래스를 모듈에서 내보내는 문장으로 이는 이 파일이 다른 파일에서 Coupon 클래스를 가져와 사용할 수 있다!
class Coupon {
constructor(price, expiration) {
this.price = price;
this.expiration = expiration || '2주';
}
get priceText() {
return `$ ${this.price}`;
}
get expirationMessage() {
return `이 쿠폰은 ${this.expiration} 후에 만료됩니다.`;
}
}
const coupon = new Coupon(5);
coupon.price = 10;
coupon.priceText;
// '$ 10'
coupon.expirationMessage
// "이 쿠폰은 2주 후에 만료됩니다."
get
: JavaScript의 클래스 내에서 게터(getter) 메서드를 정의할 때 사용한다.
getter 메서드는 클래스의 속성에 접근할 때 사용되며, 해당 속성을 읽을 때 특정한 동작을 수행하도록 정의한다- 따라서 get 키워드를 사용하여 정의된 priceText는 클래스의 속성처럼 동작하지만, 사실상 메서드이며, 호출하면 내부적으로 ${this.price}를 반환하는 메서드가 실행한다.
- get을 사용할 경우 뒤에는 동작이 아니고 명사형태로 네이밍을 변경하면 아주 좋다.
- 코딩 컨벤션으로 메서드나 함수는 동사로!! 속성은 명사로 하는것이 아주 좋다.
- 특이한 점은 점 표기법으로 접근이 가능하다.
- 객체의 속성에 접근하는 방식은 객체의 특정 속성을 읽거나 수정하는 데 더 적합하며, 이로써 코드가 객체 지향적인 설계를 보다 잘 반영했다.
class Coupon {
constructor(price, expiration) {
this.price = price;
}
set halfPrice(price) {
this.price = price / 2;
}
}
const coupon = new Coupon(5);
coupon.price; // 5
coupon.halfPrice = 20;
coupon.price; // 10
coupon.halfPrice // undefined
coupon.halfPrice = 50;
coupon.price; // 25
set
: JavaScript의 설정자(setter)를 정의할 때 사용되는 키워드로 setter는 객체의 속성에 값을 할당할 때 실행되는 함수로, 해당 속성의 값을 설정하거나 가공하는 역할을 한다.- set 키워드를 사용하여 setter를 정의할 때, 해당 setter는 객체의 속성처럼 동작하지만, 실제로는 메서드!!
- setter의 이름은 해당 속성의 이름으로 사용되며, setter가 호출될 때는 속성에 값을 할당할 때와 동일한 방식으로 호출됩니다.
- setter는 속성 이름 뒤에 오는 등호(=) 연산자와 함께 사용
- setter에 대응하는 getter가 없으므로 coupon.halfPrice 값이 undefined로 나온다.
- 항상 getter와 setter는 쌍을 이루는것이 좋다. 하지만, 속성까지 같은 이름은 지양한다.(호출 스택이 무한히 쌓인다.)
- 컨벤션에 맞춰
_
(하이픈)을 사용해 비공개라는걸 알려준다. - 아래 코드와 같이 _price는 클래스 내에서 가교 역할로 사용하고 외부에서는 세터 price를 사용한다.
class Coupon {
constructor(price, expiration) {
this._price = price;
this.expiration = expiration || '2주';
}
get priceText() {
return `$${this._price}`;
}
get price() {
return this._price;
}
set price(price) {
const newPrice = price
.toString()
.replace(/[^\d]/g, ''); // 정수만 남기는 정규화식
this._price = parseInt(newPrice, 10);
}
get expirationMessage() {
return `이 쿠폰은 ${this.expiration} 후에 만료됩니다.`;
}
}
const coupon = new Coupon(5);
coupon.price; // 5
coupon.price = '$10';
coupon.price; // 10
coupon.priceText; // '$10'
- 속성 이름을 내부적으로만 사용하고 외부에서는 접근할 수 없도록 하기 위해 _price와 같이 밑줄을 사용하여 속성을 명명한다!
- 장점은 복잡도를 숨길 수 있다.
- 단점은 자바스크립트의 모든 객체는 외부에서 접근이 허용되기 때문에 위의 예시처럼 실제로 은닉이 되지는 않는다. 때문에 다른 개발자가 이 클래스를 사용할때 실제로는 메서드를 호출하지만 속성을 설정한다고 생각할 수 있는 소통의 오류가 생길 수 있다.
tip41. 제너레이터로 이터러블 속성을 생성하라
- 이터러블(Iterable) :
Collection
을 순회가능하게 한다.(객체의 일부를 배열로 변경하여 순회 가능) Generator
: 함수가 호출 되었을때 끝까지 실행하지 않고 중간에 빠져나갔다가 다시 돌아올 수 있는 함수- 호출될 때마다 iterator를 반환하며, iterator를 통해 함수의 실행을 제어할 수 있다.
- Generator 함수는
funcion*()
이렇게 사용 next()
라는 메서드는 함수의 일부를 반환(value, done가 있는 객체를 반환)yield
: 선언한 항목이 value, done은 남은 항목이 없다는 정보를 반환한다.(일종의 return)
function* getCairoTrilogy() {
yield '궁전 샛길';
yield '욕망의 궁전';
yield '설탕 거리';
}
const trilogy = getCairoTrilogy();
trilogy.next(); // { value: '궁전 샛길', done: false }
trilogy.next(); // { value: '욕망의 궁전', done: false }
trilogy.next(); // { value: '설탕 거리', done: false }
trilogy.next(); // { value: undefined, done: true }
- getCairoTrilogy Generator 함수는 세 개의 값을 순차적으로 반환하는데, 이 값들은 yield 키워드를 사용하여 반환
- Generator 함수가 호출될 때마다 yield 키워드를 만나면 함수의 실행이 일시 중지되고 해당 값을 반환한다.
- 이후에
next()
메서드가 호출되면 다음yield
키워드로 이동하여 함수가 다시 실행한다. trilogy.next()
를 호출하여 Generator 함수를 실행하고, 첫 번째 yield 문인 '궁전 샛길'을 반환한다.- 만약 이후에 다시 trilogy.next()를 호출하면 '욕망의 궁전'을 반환하고, 그 다음에는 '설탕 거리'를 반환한다.
- 마지막으로 호출되면
value
속성의 값은 undefined,done
속성이 true인 객체를 반환하여 Generator 함수의 실행이 종료되었음을 나타낸다. - Generator 함수 뿐만 아니라 iterable 속성을 사용할 때 next() 메서드가 사용 가능하다.(펼침연산자, for...of 등)
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();
family.getMembers();
// ['Doris', 'Martha', 'Dyan', 'Bea'];
- 위 코드는 한 가족의 가계도인데 자식이 자식을 낳고, 또 그 자식이 자식을 낳았는데 이름을 배열로 구하는 코드이다.
- getter와 setter처럼 클래스에 단순한 인터페이스를 제공할 수 있다.
- 복잡한 데이터 구조를 다루는 클래스를 만들때, 단순한 배열을 다루는 것처럼 데이터에 접근할 수 있게 한다.
- 위 코드를 Generator를 사용하면, 아래와 같이 배열에 담지 않고 데이터를 바로 반환이 가능하다.
class FamilyTree {
constructor() {
this.family = {
name: 'Doris',
child: {
name: 'Martha',
child: {
name: 'Dyan',
child: {
name: 'Bea',
},
},
},
};
}
* [Symbol.iterator]() {
let node = this.family;
while (node) {
yield node.name;
node = node.child;
}
}
}
const family = new FamilyTree();
[...family];
// ['Doris', 'Martha', 'Dyan', 'Bea'];
- gemtMemebers() 대신
* [Symbol.iterator]()
로 변경 : 클래스의 iterable에 Generator로 연결 - 이렇게 하면, iterator를 통해 함수의 실행을 제어할 수 있습니다.
[Symbol.iterator]
는 while 루프를 사용하여 가계도를 따라가며 각 노드의 이름을 반환하는데, 먼저 현재 노드의 이름을 yield 키워드를 사용하여 반환하고, 다음 자식 노드로 이동합니다. 이 과정을 모든 노드가 탐색될 때까지 반복한다.- map 객체가 map iterator를 가지고 있는것과 비슷하다.
tip42. bind( )로 문맥 문제를 해결하라
- 저번 tip.36 화살표 함수로 문맥 혼동을 피하라에서 대략적으로 살펴봤는데 여기서 다시 자세히 살펴보자.
class Validator {
constructor() {
this.message = '가 유효하지 않습니다.';
}
// 입력값 하나다 유효하지 않는 경우 메세지 반환
setInvalidMessage(field) {
return `${field}${this.message}`;
}
// 모든 메세지를 담긴 배열을 순회하면서 유효하지 않는 메세지들 반환
setInvalidMessages(...fields) {
return fields.map(this.setInvalidMessage);
}
}
const validator = new Validator();
validator.setInvalidMessage('도시'); // "도시가 유효하지 않습니다."
validator.setInvalidMessages('도시'); // Uncaught TypeError: Cannot read property 'message' of undefined
- setInvalidMessages 메서드에서 map안에 this.setInvalidMessage는 class 내부에 바인딩외어
- setInvalidMessage을 호출하는데 여기서 this는 map() 메서드 콜백함수이므로 새로운 문맥(window)에 바인딩 된다.
- 해결 방법은 먼저 메서드를 화살표 함수로 변경하는 방법이다.
class Validator {
constructor() {
this.message = '가 유효하지 않습니다.';
this.setInvalidMessage = field => `${field}${this.message}`;
}
setInvalidMessages(...fields) {
return fields.map(this.setInvalidMessage);
}
}
- 메서드가 많아지면 생성자함수는 너무 커진다.
- 그래서 두번째 해결 방법으로는 명시적으로 bind()를 사용하는 방법이 있다.
function sayMessage() {
return this.message;
}
const alert = {
message: '위험해!',
};
const sayAlert = sayMessage.bind(alert);
sayAlert(); // '위험해!'
위에 코드인 Validator를 bind()를 사용해 보자.
class Validator {
constructor() {
this.message = '가 유효하지 않습니다.';
}
setInvalidMessage(field) {
return `${field}${this.message}`;
}
setInvalidMessages(...fields) {
return fields.map(this.setInvalidMessage.bind(this));
}
}
- 잘 동작하지만, 다른 곳에서 사용할때마다 this.setInvalidMessage.bind(this)를 계속 입력해야 한다.
- 생성자 함수 화살표 함수처럼
bind()
사용이 가능하다. - 직관적이긴하나 역시 함수가 많아지면 생성자 함수가 비대해진다.(화살표 함수에 단점이라고 책에서 말해 놓은 상태이다.)
- 그래서 위의 코드를 this 바인딩을 이용하여 개선해보면 아래와 같다.
class Validator {
constructor() {
this.message = '가 유효하지 않습니다.';
this.setInvalidMessage = this.setInvalidMessage.bind(this);
}
setInvalidMessage(field) {
return `${field}${this.message}`;
}
setInvalidMessages(...fields) {
return fields.map(this.setInvalidMessage);
}
}
- 변경 전 코드에서는 setInvalidMessage 메서드를 사용할 때마다 bind 메서드를 호출하여 명시적으로 this를 설정해야 하지만, 위 코드에서는 이미 생성자 내에서 this를 바인딩하여 별도의 바인딩이 필요하지 않다.
Reference
자바스크립트 코딩의 기술 요약 / 정리
728x90
반응형
'IT > Javascript' 카테고리의 다른 글
Javascript 자바스크립트 코딩의 기술 9장 외부 데이터에 접근하라 (2) (10) | 2024.03.11 |
---|---|
Javascript 자바스크립트 코딩의 기술 9장 외부 데이터에 접근하라 (1) (20) | 2024.03.08 |
Javascript 자바스크립트 코딩의 기술 8장 클래스로 인터페이스를 간결하게 유지하라 (1) (2) | 2024.02.28 |
Javascript 자바스크립트 코딩의 기술 7장 유연한 함수를 만들어라 (2) (0) | 2024.02.23 |
Javascript 자바스크립트 코딩의 기술 7장 유연한 함수를 만들어라 (1) (2) | 2024.02.21 |