개인노트-인강

개인노트 nomad ES6의 정석 #13 ES2020

roroism 2023. 3. 2. 17:56

# 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을 사용합니다.