개인노트 nomad ES6의 정석 #13 ES2020
# 13.1 New ?? Operator
## 개요
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing
널 병합 연산자 (??) 는 왼쪽 피연산자가 null 또는 undefined일 때 오른쪽 피연산자를 반환하고, 그렇지 않으면 왼쪽 피연산자를 반환하는 논리 연산자이다.
이는 왼쪽 피연산자가 null 또는 undefined 뿐만 아니라 *falsy *값에 해당할 경우 오른쪽 피연산자를 반환하는 논리 연산자 OR (||)와는 대조된다. 다시 말해 만약 어떤 변수 foo에게 *falsy *값( '' 또는 0)을 포함한 값을 제공하기 위해 ||을 사용하는 것을 고려했다면 예기치 않는 동작이 발생할 수 있다. 하단에 더 많은 예제가 있다.
널 병합 연산자는 연산자 우선 순위가 다섯번째로 낮은데, || 의 바로 아래이며 조건부 (삼항) 연산자의 바로 위이다.
## 설명
const name = 0;
console.log("hello", name);
// hello 0
console.log("hello", name || "anonymous");
// hello anonymous
console.log("hello", name ?? "anonymous");
// hello 0
OR (||) 은 변수에 기본값을 줄 때 유용합니다.
하지만 weak point가 있습니다. 실제로 값이 할당되어 있어도 false로 판단합니다.
예를 들어 위 예시코드에서 name 에 0이 할당되어 있어도 OR (||) 는 이 0을 false로 판단합니다.
const name = "";
console.log("hello", name);
// hello
console.log("hello", name || "anonymous");
// hello anonymous
console.log("hello", name ?? "anonymous");
// hello
빈 문자열도 마찬가지로 OR (||)에서는 false로 판단합니다.
빈 문자열("")이나 0도 쓰이게 하고 싶다면 널 병합 연산자(??)를 사용합니다.
## falsy한 값
https://developer.mozilla.org/ko/docs/Glossary/Falsy
거짓 같은 값(Falsy, falsey로 쓰이기도 함) 값은 불리언 문맥에서 false로 평가되는 값입니다.
# 13.2 Optional Chaining
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Optional_chaining
optional chaining 연산자 (?.) 는 체인의 각 참조가 유효한지 명시적으로 검증하지 않고, 연결된 객체 체인 내에 깊숙이 위치한 속성 값을 읽을 수 있다.
?. 연산자는 . 체이닝 연산자와 유사하게 작동하지만, 만약 참조가 nullish (null 또는 undefined)이라면, 에러가 발생하는 것 대신에 표현식의 리턴 값은 undefined로 단락된다. 함수 호출에서 사용될 때, 만약 주어진 함수가 존재하지 않는다면, undefined를 리턴한다.
API 작업을 할 때 유용합니다. API를 통해서 가져온 데이터가
예상한 것을 (API 등이) 가지고 있는지 아닌지 확신할 수 없을 때 사용한다.
const me = {
name: "nico",
profile: {
email: "@something.com"
}
}
console.log(me.profile.email);
// @something.com
가져오려는 속성에 접근하여 해당 값을 읽어올 수 있습니다.
하지만, 만약 해당 속성이 없는 데이터가 왔다면?
const lynn = {
name: "lynn",
}
console.log(lynn.profile.email);
// error
에러가 발생합니다.
이 같은 경우를 위해서 optional chaining 연산자가 존재합니다.
console.log(lynn?.profile?.email);
optional chainging 연산자를 사용하지 않고, 표현한다면 아래처럼 매우 복잡하고 길게 작성해주어야합니다.
console.log(lynn && lynn.profile && lynn.profile.email);
정리한다면,
어떤 object가 우리가 예상한 것을 가지고 있는지 아닌지 확신할 수 없을 때 사용하면 좋습니다.
대부분 API를 사용하는 대부분의 작업에서 사용될 것입니다.
# 13.3 padStart and padEnd
1. padStart
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
padStart() 메서드는 현재 문자열의 시작을 다른 문자열로 채워, 주어진 길이를 만족하는 새로운 문자열을 반환합니다. 채워넣기는 대상 문자열의 시작(좌측)부터 적용됩니다.
2. padEnd
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
padEnd() 메서드는 현재 문자열에 다른 문자열을 채워, 주어진 길이를 만족하는 새로운 문자열을 반환합니다. 채워넣기는 대상 문자열의 끝(우측)부터 적용됩니다.
## 예시
let hours = 12;
let minutes = 3;
let seconds = 2;
console.log(`${hours}h:${minutes}m:${seconds}s`);
// 12h:3m:2s
// 원하는 출력
// 12h:03m:02s
minutes = String(minutes).padStart(2, "0");
console.log(`${hours}h:${minutes}m:${seconds}s`);
// 12h:03m:02s
"5".padStart(5, "xxxxx");
// "xxxx5"
"5".padEnd(5, "xxxxx");
// "5xxxx"
"1".padStart(2, "0").padEnd(3, "s");
// "01s"
참고로 결과를 반환할 뿐 원본 값은 변화시키지 않습니다.
# 13.4 trim, trimStart, trimEnd
trim : 비어있는 공간을 없애줍니다. (단어 사이의 공백 제외)
" hello".trimStart();
// "hello"
" hello ".trimEnd();
// " hello"
" hello ".trim();
// "hello"
.trim()은 원본 string 자체를 수정하지 않습니다.
# 13.5 Object entries, Object values,Object fromEntries
object는 javascript의 데이터 타입입니다.
object는 create, assign, freeze 같은 메소드를 가지고 있습니다.
참고 : Object.freeze() : object를 바꾸지 못하게 합니다.
## Object values(), Object entries()
values는 객체의 값들을 배열로 return합니다.
entries는 객체의 key와 값을 return합니다. 배열의 배열로 return합니다.
const person = {
name: "nico",
age: 12
}
// 객체의 값들을 알고 싶을 경우
Object.values(person);
// (2) ["nico", 12]
// entries
Object.entries(person);
// (2) [Array(2), Array(2)]
// 0: (2) ["name", "nico"]
// 1: (2) ["age", 12]
위처럼 key값의 이름과 값이 배열의 형태로 들어있습니다.
### 사용예
Object.entries(person).forEach(item => console.log(item[0], item[1]));
// 출력
// name nico
// age 12
## Object.fromEntries()
배열의 배열에서부터(Object.entries 결과물의 구조) Object를 만들어줍니다.
Object.fromEntries([["name", "nico"], ["age", 12], ["f", "k"], ["hello", true]]);
// {name: "nico", age: 12, f: "k", hello: true}
# 13.6 Array.flat
이 기능을 보면 정말 엄청나다고 생각할 수도 있습니다.
왜냐하면 이전에는 수작업으로 하거나 Underscore 라이브러리를 설치해서 .flatten() 이라는 함수를 이용해야했습니다.
const flatTest = [1, [2, 2, [3, 3, [4, 4, [5, 5]]]]];
console.log(flatTest.flat());
// (4) [1, 2, 2, Array(3)]
console.log(flatTest.flat(2));
// (6) [1, 2, 2, 3, 3, Array(3)]
console.log(flatTest.flat(3));
// (8) [1, 2, 2, 3, 3, 4, 4, Array(2)]
console.log(flatTest.flat(4));
// (9) [1, 2, 2, 3, 3, 4, 4, 5, 5]
console.log(flatTest.flat(5));
// (9) [1, 2, 2, 3, 3, 4, 4, 5, 5]
flat은 복잡한 depth의 배열안의 배열들을 전부 밖으로 빼줍니다.
flat은 기본적으로 깊이가 1로 설정되어 있어서 .flat()을 할 경우, 한 깊이 만큼을 낮춰줍니다.
# 13.7 Array.sort
배열을 정렬해줍니다.
새로운 배열로 반환하지 않고, 원 데이터를 변경합니다.
공식문서
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
compareFunction(a, b)가 0보다 작으면, a 가 b 보다 작다는 의미입니다. (a<b) 즉, a가 먼저옵니다.
a, b 가 같으면 0을 반환합니다.
0보다 크면 a가 b보다 큽니다. (a>b) b를 a보다 낮은 인덱스로 소트합니다.
참고로 비교 함수는 무조건 무엇이라도 반환해야 합니다.
const fruits = ["apple", "strawberry", "avocado"];
const sortFruitByLength = (fruitA, fruitB) => {
return fruitA.length - fruitB.length
// 예시
// strawberry글자수 - avocado글자수
// 10 - 7 = 3
}
console.log(fruits.sort(sortFruitByLength));
// ['apple', 'avocado', 'strawberry']
## 유용한 사용법
객체가 배열의 item인 경우..
const people = [
{
name: "nico",
age: 12
},
{
name: "lynn",
age: 22
}
];
const orderPeopleByAge = (personA, personB) => {
return personA.age - personB.age
}
console.log(people.sort(orderPeopleByAge));
// [ { name: 'nico', age: 12 }, { name: 'lynn', age: 22 } ]
# 13.8 Promise allSettled
promise.allSettled는 promise.all 과는 다릅니다.
promise.all은 모든 비동기 함수가 성공해야 resolve를 실행하고, 하나라도 실패하면 reject되지만,
promise.allSettled는 모든 promise가 성공할 필요는 없습니다.
promise.all
const p = Promise.all([
fetch("https://yts.mx/api/v2/list_movies.json"),
fetch("https://yts.mx/api/v2/list_movies.json"),
fetch("https://yts.mx/api/v2/list_movies.json"),
fetch("https://yts.mx/api/v2/list_movies.json"),
])
.then(response => console.log("success!", response))
.catch(e => console.log("my error:", e));
// 모두 성공시
// success! [ Response {}, Response {}, Response {}, Response {} ]
// 하나라도 실패시
// error: 에러메세지
promise.allSettled
const p = Promise.allSettled([
fetch("https://yts.mx/api/v2/list_movies.json"),
fetch("https://yts.mx/api/v2/list_movies.json"),
fetch("https://yts.mx/api/v2/list_movies.json"),
fetch("https://yts.mx/api/v2/list_movies.json"),
])
.then(response => console.log("success!", response))
.catch(e => console.log("my error:", e));
// 모든 비동기 처리가 완료되면
// success! [
// { status: 'fulfilled', value: Response {} },
// { status: 'fulfilled', value: Response {} },
// { status: 'fulfilled', value: Response {} },
// { status: 'fulfilled', value: Response {} } ]
// 실패한 비동기는 status를 'rejected'로 출력하고,
// value속성 대신 reason 속성과 에러내용이 추가됩니다.
// 실패한 비동기의 출력 객체 예시
// { status: 'rejected', reason: 에러메세지 }
성공시 출력값이 all 과는 다르게 Object로 출력됩니다.
이 출력값으로 유추해보면 promise.allSettled 은 모든 promise가 성공하지 않아도 되는 이유입니다. (에러가 있든 없든 resolve 됩니다.) 모두성공했는지는 상관없이 단지 모든 promise가 끝나기를 기다려서 resolve 하고, 에러가 발생한 비동기만 status의 값을 fulfilled가 아닌 rejected로 출력합니다.
## 정리
Promise.allSettled
모든 promise가 잘 작동하는지 확인할 필요가 없으면 all 대신에 allSettled 을 사용하면 됩니다.
어느것이 잘 동작하고 어느 것이 동작하지 않는지 확인할 때 사용하면 유용합니다.
Promise.all
모든 promise가 동시에 동작하는지 확인하는 것이 중요하다면 all을 사용합니다.