2023. 3. 8. 16:50ㆍ개인노트-인강
ECMA2021:
- Logical Assignment Operators
- 1. Logical OR Assignment (||=)
- 2. Logical AND Assignment (&&=)
- 3. Logical NULLISH Assignment (??=)
- Numeric Separators
- Promise.any
- String.prototype.replaceAll
ECMA2022:
- .at()
- Object.hasOwn(obj, propKey)
- error.cause
- Classes:
- 1. Class field declarations
- 2. Private Methods & fields
- 3. Static class fields & private static methods
# 14.1 Logical OR Assignment
Logical Assignment Operators를 사용하면 우리들의 코드량을 줄여줍니다. 일종의 shortcut 역할을 해줍니다.
const name = prompt("what is your name");
console.log(`Hello ${name}`);
위 코드를 실행해서 입력창에 값을 입력하면 콘솔에 출력될것입니다.
하지만 만약에 user가 cancel을 누르거나 아무것도 입력하지 않으면 null이 출력됩니다.
이 null 대신에 다른 값을 출력하고 싶다면?
let name = prompt("what is your name");
if(!name) {
name = "anonymous";
}
console.log(`Hello ${name}`);
위처럼 작성해 줄 수도 있겠지만,
대신에 logical or operator를 사용하여 표현할 수도 있습니다.
let name = prompt("what is your name");
// if(!name) {
// name = "anonymous";
// }
name ||= "anonymous";
console.log(`Hello ${name}`);
logical or operator는 변수가 falsy일 때 오른쪽의 값을 변수에 값을 넣을 수 있게 해줍니다.
변수에 텍스트, Array, Object가 오면 일어나지 않습니다.
* falsy : undefined, false, 빈 문자열, 0, null, NaN, -0
# 14.2 Logical AND Assignment
Logical AND Assignment는 OR 와 완전히 반대입니다.
변수에 value가 truthy라면 변수를 수정합니다. (덮어씁니다.)
예시 코드
const user = {
username: 'nico',
password: 123
};
console.log(user);
// { username: 'nico', password: 123 }
위 코드에서 password의 value가 존재할 때,
콘솔에 공개적으로 password가 보이지 않게 보호하고 싶다면?
const user = {
username: 'nico',
password: 123
};
if(user.password) {
user.password = "[secret]";
}
console.log(user);
// { username: 'nico', password: '[secret]' }
Logical AND Assignment (&&=) 를 사용해서 바꾸면..
const user = {
username: 'nico',
password: 123
};
// if(user.password) {
// user.password = "[secret]";
// }
user.password &&= "[secret]";
console.log(user);
// { username: 'nico', password: '[secret]' }
추가 예시
const user = {
username: 'nico',
password: 123
};
user.password &&= "[secret]";
user.name &&= "nico"; // user 객체에 name이 없기 때문에 동작하지 않습니다.
console.log(user);
// { username: 'nico', password: '[secret]' }
user.name ||= "nico"; // user 객체에 name이 없기 때문에 동작합니다.
console.log(user);
// { username: 'nico', password: '[secret]', name: 'nico' }
이처럼 적절한 상황에서 사용한다면 if나 else보다 훨씬 보기 좋습니다.
# 14.3 Logical NULLISH Assignment
Logical NULLISH Assignment (??=) 은 Logical OR Assignment (||=) 하고 굉장히 비슷합니다.
const user = {
username: "nico",
password: 123,
isAdmin: ""
};
user.isAdmin ||= true;
user.isAdmin ??= true;
// 변수가 null이거나 undefined 일 때 true를 넣습니다.
// isAdmin이 undefined 일 경우 isAdmin은 true
// isAdmin이 null 일 경우 isAdmin은 true
// isAdmin이 "" 일 경우 isAdmin은 ""
// isAdmin이 0 일 경우 isAdmin은 0
// isAdmin이 false 일 경우 isAdmin은 false
console.log(user);
isAdmin이 falsy한 값(undefined, null, 0, "", false, NaN, -0) 이라면 ||=에 의하여 isAdmin에는 true가 출력될 것입니다.
||= 를 ??=로 바꾸면 ??=는 변수가 falsy인지 확인하는 것이 아니라. 오직 null이거나 undefined인 경우만 확인합니다.
즉, 오직 null이거나 undefined인 경우만 값을 할당합니다.
||=는 falsy 일 때 동작하고,
&&=는 truthy 일 때 동작하고,
??=는 null 또는 undefined 일 때 동작합니다.
# 14.4 Numeric Separators
단위 수를 알아보기 쉽게 _ 를 사용하여 구분하여 표현할 수 있습니다.
가끔 진짜 큰 숫자를 쓸 때 유용합니다.
const allTheMoney = 11_000_000_000_000_000;
// _ 를 사용해서 숫자를 나누면 훨씬 쉽게 셀 수 있습니다.
console.log(allTheMoney);
// 출력
// 11000000000000000
// 숫자를 읽기 쉽게 바꿔줄뿐 값을 변경시키지는 않습니다.
작성자 입장에서 편리한 기능입니다. 실제로 값에 영향을 주지는 않습니다.
float에서도 동작합니다.(소수점)
작성자가 원하는 곳 아무대나 넣어서 사용할 수 있습니다. (예 : 1_1_0_0_0_1.345)
단, 연속으로 _ 를 2개 이상 사용하면 안됩니다.
# 14.5 Promise.any
Promise.any는 매우 흥미롭습니다. 그 전에
먼저, Promise.all 을 먼저 알아봅니다.
Promise.all([p1, p2, p3]).then();
위 코드에서 Promise.all은 p1, p2, p3 가 모두 다 끝날 때까지 기다립니다.
p1, p2, p3가 끝난 뒤에야 .then으로 가서 Promise.all이 끝납니다.
Promise.any는 다릅니다.
Promise.any([p1, p2, p3]).then();
Promise.any는 p1, p2, p3 중 어느 하나가 끝나기를 기다립니다.
이것들 중 어느 것이라도 끝이 나면 다음으로 넘어갑니다. 예를 들어 p2가 먼저 끝났다면, p1과 p3를 기다리지 않고 바로 then으로 넘어갑니다.
만약, 이들 중 아무것도 끝나지 않았다면 aggregate error가 발생합니다.
const p1 = new Promise((resolve, reject) => {
setTimeout(reject, 1000, "quick");
});
const p2 = new Promise((resolve, reject) => {
setTimeout(reject, 5000, "quick");
});
Promise.any([p1, p2]).then(console.log);
// then은 실행되지 않습니다. 그 대신 에러가 발생합니다.
// Problems : All Promises were rejected
이번에는 catch로 error를 잡아봅니다.
const p1 = new Promise((resolve, reject) => {
setTimeout(reject, 1000, "quick");
});
const p2 = new Promise((resolve, reject) => {
setTimeout(reject, 5000, "quick");
});
Promise.any([p1, p2]).then(console.log).catch(console.log);
// 2개가 다 실행되어 reject가 된 후, catch가 실행됩니다.
// AggregateError : All promises were rejected
위 에러 메세지는 전체 promise 포함하여 하나의 에러 메세지로 나타냈지만,
error 메세지를 직접 출력해보면 각각의 Promise 별로 에러상태를 배열로 구분하여 출력해줍니다.
Promise.any([p1, p2])
.then(console.log)
.catch(e => {
console.log(e.errors);
});
// ["quick", "slow"]
all은 모두가 끝나길 기다리고,
any는 하나만 끝나길 기다리고 나머지는 신경쓰지 않습니다.
# 14.6 replaceAll
replaceAll은 string 안에 있는 모든 표적을 대체할 수 있습니다.
replaceAll은 원래 값을 변화시키지 않고 새로운 string을 return합니다.
첫번째 argument는 바꾸고 싶은 것, 두번째 argument는 어떤 걸로 바꿀지 넣어주면 됩니다.
const name = "Nicolaso";
const newName = name.replaceAll("o", "❤");
console.log(name, newName);
// Nicolaso Nic❤las❤
# 14.7 at()
const arr = ["a", "b", "c", "d"];
console.log(arr.at(2)); // c
console.log(arr[2]); // c
// 여기까지는 기존의 arr[2] 나 arr.at(2) 는 차이가 없습니다.
기존의 arr[2] 나 arr.at(2) 는 차이가 없습니다.
만약 배열에서 0부터 시작하는 인덱스의 항목을 찾고 싶다면, 이 둘은 다른점이 없습니다.
하지만, 끝에서부터 item을 찾고 싶다면 at 메서드가 빛을 발합니다.
at() 은 끝에서부터도 item을 찾을 수 있게 해줍니다.
console.log(arr.at(-1)); // d
console.log(arr[-1]); // undefined
console.log(arr.at(-3)); // b
at()은 음수 인덱스를 사용해서 끝에서부터 찾을 수 있게 해줍니다.
Javascript에서는 일반적인 방법으로는 끝에서부터 찾을 수 없습니다. at()이 이것을 가능하게 해줍니다.
# 14.8 Object hasOwn
Object.hasOwn은 object가 property를 가지고 있는지 확인합니다.
Object.hasOwn 이전에 기존에 사용한 Object.hasOwnProperty에 대해 먼저 확인해 봅니다.
const user = {
name: "nico",
isAdmin: "hi"
};
console.log(user.hasOwnProperty("isAdmin")); // true
기존의 hasOwnProperty는 조사하려는 객체에서 hasOwnProperty메서드를 호출하여 인자로 조사하려는 속성이름을 넘겼지만,
Object.hasOwn 은 Object.hasOwn(객체, "확인하려는 속성 이름"); 으로 나타냅니다.
Object.hasOwn(obj, propKey)
const user = {
name: "nico",
isAdmin: "hi"
};
console.log(user.hasOwnProperty("isAdmin")); // true
console.log(Object.hasOwn(user, "isAdmin")); // true
공식문서 중..
Object.hasOwn() is intended as a replacement for Object.prototype.hasOwnProperty().
Object.hasOwn()은 Object.prototype.hasOwnProperty()를 대체하기 위한 것입니다.
가능하다면 hasOwnProperty() 보다 Object.hasOwn() 을 사용하는 것을 추천합니다.
이유 공식문서 확인 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn
## object에 property가 존재하는지 확인하는 작은 트릭
console.log("isAdmin" in user); // true
# 14.9 Error cause
개발자의 DX를 위해 만들어졌습니다. (DX : 개발자 경험)
많은 라이브러리나 오픈소스에서 이걸 사용하고 있을 거라고 생각됩니다.
error.cause를 사용하지 않고 기존의 에러 처리를 보면,
try {
2+2;
throw new Error("DB Connection Failed.");
} catch(e) {
console.log(e.message);
}
// 출력
// DB Connection Failed.
error.cause는 에러에 메세지를 추가할 수 있을 뿐만 아니라, 또 다른 정보를 추가하는 기능도 있습니다.
try {
2+2;
throw new Error("DB Connection Failed.", {
cause: "Password is incorrect"
});
} catch(e) {
console.log(e.message, e.cause);
}
// 출력
// DB Connection Failed. Password is incorrect
위처럼 에러에 대한 추가적인 정보를 제공할 수 있습니다.
그리고 cause가 꼭 문자열일 필요는 없습니다. 객체로 넘겨서 더 많은 정보를 남길 수 있습니다.
try {
2+2;
throw new Error("DB Connection Failed.", {
cause: {
error: "Password is incorrect.",
value: 1234,
message: ["too short", "only number not ok."]
}
});
} catch(e) {
console.log(e.cause);
}
// 출력
// { error: "Password is incorrect.", value: 1234, message: Array(2) }
# 14.10 Class Field Declarations
JavaScript는 진짜 객체 지향 프로그래밍 언어들이 지원하는 모든 특징을 가지고 있지는 않습니다.
하지만 ECMA2022로 오면서 점점 가까워지고 있어서, 이전에 사용하지 못했던 객체 지향 프로그래밍 언어들이 가진 특징들을 더 많이 쓸 수 있게 됐습니다.
이번에는 Class에서의 필드 선언에 대한 것부터 알아보겠습니다.
먼저 예시코드로 Counter class를 만들어보겠습니다.
class Counter {
constructor() {
this.count = 0;
}
plus() {
this.count++;
}
}
위에서처럼 변수를 초기화 하려면 constructor를 작성해야 했습니다.
Class Field Declarations를 사용하면 constructor를 작성하지 않고도 바로 필드에서 초기화 할 수 있습니다.
class Counter {
count = 0;
// constructor() {
// this.count = 0;
// }
plus() {
this.count++;
}
}
코드는 더 깔끔해졌고, 결과는 똑같이 나옵니다.
# 14.11 Private Methods and Fields
Private 메소드와 필드에 대해 알아보겠습니다.
이는 마치 Java나 C#, TypeScript 에서 코딩하는 느낌을 줄 것입니다.
지금까지의 JavaScript에서는 Private Methods and Fields는 없었습니다.
Private 메소드와 필드를 쓰는 이유는 'Class를 보호하기 위해서' 입니다.
#를 앞에 붙여서 필드나 속성, 메소드를 private로 만들 수 있습니다.
## Private Fields
class Counter {
count = 0;
plus() {
this.count++;
}
}
const c = new Counter();
c.plus(); // 1증가
c.plus(); // 1증가
c.count = 100000; // 누군가 악의적으로 값을 바꾼다면?
console.log(c.count); // 100000
위 코드처럼 원하지않는 상황이 발생할 수도 있습니다. count 값을 보호해야 하는 상황입니다.
이 때, private하게 바꾸어 클래스 내부에서만 count 값을 수정할수 있고, 클래스 밖에서는 수정할 수 없게 합니다.
count를 private 필드로 만드려면 변수명 앞에 #을 붙입니다.
class Counter {
#count = 0;
plus() {
this.#count++;
}
}
const c = new Counter();
c.plus(); // 1증가
c.plus(); // 1증가
// c.#count = 100000; // error 발생
console.log(c.#count); // 2
## Private Methods
Private 메소드도 만들 수 있습니다.
class Counter {
#count = 0;
plus() {
if(this.#count === 5) {
this.#reset();
} else {
this.#count++;
}
}
#reset() {
this.#count = 0;
}
get count() {
return this.#count;
}
}
const c = new Counter();
c.plus(); // 1증가
c.plus(); // 1증가
c.plus(); // 1증가
c.plus(); // 1증가
c.plus(); // 1증가
// c.#reset(); // private 메소드 이므로 error 발생.
console.log(c.count); // 5
c.plus(); // 1증가
console.log(c.count); // 0
Private Fields와 마찬가지로 Private Methods도 오직 클래스안에서만 접근할 수 있습니다.
위 코드에서 reset 함수는 외부로 부터 보호됩니다.
클래스 안에서만 사용하게 되는 메소드는 private하게 만들어주고, 클래스 외부에서 값을 불러올 수 있게 public으로 getter 함수를 만들어줍니다.
참고로 메소드 앞에 get키워드를 사용하면 javascript에게 getter함수로 인지하게 할 수 있습니다.
get 키워드를 사용할 경우.
// ...
#count = 0;
// ...
get count() {
return this.#count;
}
// ...
console.log(c.count);
get 키워드를 사용하지 않을 경우.
// ...
#count = 0;
// ...
count() {
return this.#count;
}
// ...
console.log(c.count());
# 14.12 Static Fields and Methods
1. static 메소드와 필드.
2. private static 메소드와 필드.
## static 메소드와 필드
static메소드는 인스턴스에 고정되지 않은 메소드입니다.
class Counter {
#count = 0;
static description = "Count up to five.";
static isMyChild(instance){
return instance instanceof Counter;
};
plus() {
if(this.#count === 5) {
this.#reset();
} else {
this.#count++;
}
}
#reset() {
this.#count = 0;
}
get count() {
return this.#count;
}
}
const c = new Counter();
// c.description // 동작하지 않습니다.
console.log(c.description); // undefined
console.log(Counter.description); // Count up to five.
// c라는 인스턴스가 Counter클래스의 인스턴스인지 확인합니다.
console.log(Counter.isMyChild(c)); // true
static 메소드와 필드는 class 그 자체에 붙어있습니다. 클래스의 인스턴스로 접근할 수 없습니다.
## private static 메소드와 필드
위에서 만든 static 필드와 메소드에 #을 붙여 private하게 만들수 있습니다.
class Counter {
#count = 0;
static #description = "Count up to five.";
static #isMyChild(instance){
return instance instanceof Counter;
};
plus() {
if(this.#count === 5) {
this.#reset();
} else {
this.#count++;
}
}
#reset() {
this.#count = 0;
}
get count() {
return this.#count;
}
}
const c = new Counter();
console.log(c.description); // undefined
// console.log(Counter.#description); // error
// c라는 인스턴스가 Counter클래스의 인스턴스인지 확인합니다.
// console.log(Counter.isMyChild(c)); // error
'개인노트-인강' 카테고리의 다른 글
| 개인노트 정리 Node.js 백엔드 part 1-2 (0) | 2023.03.20 |
|---|---|
| 개인노트 정리 Node.js 백엔드 part 1-1 (0) | 2023.03.15 |
| 개인노트 nomad ES6의 정석 #13 ES2020 (0) | 2023.03.02 |
| 개인노트 nomad ES6의 정석 #12 Generators and Maps (0) | 2023.02.26 |
| 개인노트 nomad ES6의 정석 #11 Symbol, Set and Map (0) | 2023.02.22 |