#swichEl;
#fontSelectEl;
constructor() {
this.#assignElement();
this.#addEvent();
}
#assignElement() {
this.#swichEl = document.getElementById("switch");
this.#fontSelectEl = document.getElementById("font");
}
위 코드는 document단에서 계속 element를 찾았다. switch나 font는 container의 형제 요소들 이기 때문에 container를 document단에서 찾고 그 찾은 내용을 querySelector로 선택할 수 있다. 리펙토링 하면 아래와 같다.
#swichEl;
#fontSelectEl;
#containerEl;
constructor() {
this.#assignElement();
this.#addEvent();
}
#assignElement() {
this.#containerEl = document.getElementById("container");
this.#swichEl = this.#containerEl.querySelector("#switch");
this.#fontSelectEl = this.#containerEl.querySelector("#font");
}
getElementById는 document단에서만 사용할 수 있는 메소드이다.
#addEvent() {
this.#swichEl.addEventListener("change", (event) => {
document.documentElement.setAttribute(
"theme",
event.target.checked ? "dark-mode" : ""
);
});
this.#fontSelectEl.addEventListener("change", (event) => {
document.body.style.fontFamily = event.target.value;
});
}
change이벤트가 발생하면 이벤트 헨들러가 동작하게끔 되어있다. 이벤트 헨들러의 관리를 유용하게 하기 위해 분리를 해서 관리한다. 아래에 정리된 코드이다.
#addEvent() {
this.#swichEl.addEventListener("change", this.#onChangeTheme);
this.#fontSelectEl.addEventListener("change", this.#onChangeFont);
}
#onChangeTheme(event) {
document.documentElement.setAttribute(
"theme",
event.target.checked ? "dark-mode" : ""
);
}
#onChangeFont(event) {
document.body.style.fontFamily = event.target.value;
}
}
#addEvent() {
// event.code가 keyA일 때, data-code가 keyA인 것이 있다면 active라는 클래스를 추가해준다.
if (this.#keyboardEl.querySelector(`[data-code=${event.code}]`)) {
this.#keyboardEl
.querySelector(`[data-code=${event.code}]`)
.classList.add("active");
}
});
}
해당 코드를 통해 찾아지지 않는 키코드에 대한 에러 발생을 잡아주었다.
IF문을 사용한 위 코드보다 더 간단한 처리 방식이 있다.
document.addEventListener("keydown", (event) => {
// event.code가 keyA일 때, data-code가 keyA인 것이 있다면 active라는 클래스를 추가해준다.
this.#keyboardEl
.querySelector(`[data-code=${event.code}]`)
?.classList.add("active");
});
?를 사용하였다. 이것을 Optional chaining이라고 한다.
Optional chaining
ES11(ECMAScript2020)에서 도입된 optional chaining연산자 ?.는 좌항의 피연산자가 null 또는 undefined인 경우 undefined를 반환하고, 그렇지 않으면 우항의 프로퍼티 참조를 이어간다. 위 코드와 같이 event.code로 발생한 코드가 data-code에 존재하지 않을 때 원래는 에러를 발생시켰지만 Optional chaining을 통해 에러를 방지하였다. 앞서 사용한 if문과 달리 간단한 코드가 탄생하였다.
예시로 살펴보자.
?.은 ?.'앞’의 평가 대상이 undefined나 null이면 평가를 멈추고 undefined를 반환한다.
옵셔널 체이닝을 사용해 user.address.street에 안전하게 접근해보자.
let user = {}; // 주소 정보가 없는 사용자
alert( user?.address?.street ); // undefined, 에러가 발생하지 않습니다.
user?.address로 주소를 읽으면 아래와 같이 user 객체가 존재하지 않더라도 에러가 발생하지 않는다.
let user = null;
alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
위 예시를 통해 우리는 ?.은 ?. ‘앞’ 평가 대상에만 동작되고, 확장은 되지 않는다는 사실을 알 수 있다.
참고로 위 예시에서 사용된 user?.는 user가 null이나 undefined인 경우만 처리할 수 있다.
user가 null이나 undefined가 아니고 실제 값이 존재하는 경우엔 반드시 user.address 프로퍼티는 있어야 한다.
var str = '';
var length = str?.length;
consolo.log(length); // 0
null 병합 연산자
ES11(ECMAScript2020)에서 도입된 null 병합 연산자 ??는 좌항의 피연산자가 null 또는 undefined인 경우 우항의 피연산자를 반환하고, 그렇지 않으면 좌항의 피연산자를 반환한다. null 병합 연산자는 변수에 기본 값을 설정할 때 유용하다.
let foo = null ?? 'default string';
console.log(foo); // 'default string'
null 병합 연산자 도입 이전에는 논리 연산자인 ||를 사용한 단축 평가를 통해 기본 값을 설정했다. 좌항의 피연산자가 false로 평가되는 Falsy값이면 우항의 피연산자를 반환한다. 만약 Falsy값인 0이나 ''도 기본 값으로서 유효하다면 예기치 않은 동작이 발생할 수 있다.
let foo = '' || 'default string';
console.log(foo); // 'default string'
하지만 null 병합 연산자는 좌항의 피연산자가 false로 평가되는 Falsy 값이라도 null 또는 undefined가 아니면 좌항의 피연산자를 그대로 반환한다.
Falsy값: false, undefined, null, 0, -0, NaN, 빈 문자열('')
let foo = '' ?? 'default string';
console.log(foo); // ""
'개발자의 공부 > JS' 카테고리의 다른 글
[JS]재귀 함수, 중첩 함수, 순수&비순수 함수 (0) | 2022.09.21 |
---|---|
[JS문법]객체(Object)란? (0) | 2022.09.13 |
[JS]함수 간단 정리(내용 업데이트 + 생성기 함수) (1) | 2022.09.05 |
[JS]LocalStorage와 SessionStorage(내용 업데이트) (0) | 2022.09.05 |
[JS문법]for...of와 for...in (0) | 2022.09.03 |