https://www.robinwieruch.de/javascript-variable-question-mark



JavaScript Variable with Question Mark


javascript question mark

If you are new to JavaScript, the question mark after a variable may be confusing to you. Let's shed some light into it. The question mark in JavaScript is commonly used as conditional operator -- called ternary operator when used with a colon (:) and a question mark (?) -- to assign a variable name conditionally.

const isBlack = false;
const text = isBlack ? 'Yes, black!' : 'No, something else.';
console.log(text);
// "No, something else."

Either the expression is true and returns the value after the question mark (?) or the expression is false and returns the value after the colon (:).

This kind of JavaScript variable declaration is used as a shorthand though. You can achieve the same with the "if-else"-statement in JavaScript as conditional operator in contrast to the ternary operator, but it turns out more verbose:

const isBlack = false;
let text;
if (isBlack) {
text = 'Yes, black!';
} else {
text = 'No, something else.';
}
console.log(text);
// "No, something else."

If this is not what you are looking for, then maybe you are searching for JavaScript's optional chaining feature. It is used to assign a variable conditionally:

const person = {
name: 'Robin Wieruch',
pet: {
name: 'Trixi',
},
};
const petName = person.pet?.name;
console.log(petName);
// "Trixi"

If the person has no pet, the output would be undefined without throwing a JavaScript exception.

const person = {
name: 'Robin Wieruch',
};
const petName = person.pet?.name;
console.log(petName);
// undefined

When this feature was not available in JavaScript, it was common to use the AND (&&) operator or the ternary operator (?:) from before to avoid any JavaScript exceptions:

const person = {
name: 'Robin Wieruch',
};
let petName = person.pet && person.pet.name;
console.log(petName);
// undefined
petName = person.pet ? person.pet.name : undefined;
console.log(petName);
// undefined

Most commonly you will find the question mark in JavaScript for these two use cases. Either it is used as shorthand conditional operator instead of the commonly used "if-else"-statement or as optional chaining operator to assign variables conditionally without hitting an exception.


https://m.blog.naver.com/PostView.nhn?blogId=int9708&logNo=220822786169&proxyReferer=https:%2F%2Fwww.google.com%2F
https://subokim.wordpress.com/2013/01/31/platform-story/

플랫폼이란 무엇인가?

‘플랫폼이 무엇인가’ 하는 질문에 대한 답은 대답하는 사람의 수만큼이나 무수히 많다. 플랫폼과 관련하여 출간된 책도 그 수가 적지 않다. 플랫폼 개념에 대한 다양한 내용과 정의 가운데 노규성 선문대학교 경영학부 교수의 저서 <플랫폼이란 무엇인가> 가운데서 ‘플랫폼’과 ‘비즈니스 생태계와 플랫폼’, 그리고 ‘플랫폼 비즈니스 모델’의 내용을 요약·정리한다.
 
급변하는 시장환경에서 최근 더욱 두각을 나타내고 있는 기업은 애플, 구글, 아마존, 페이스북 등 인터넷 혁명을 주도하는 ‘4인방(Gang of Four)’이다. 구글의 회장 에릭 슈밋은 이들의 성공 비결을 ‘자기만의 강력한 플랫폼’이라고 했다. 10년 전의 4인방은 마이크로 소프트(MS), 인텔, 시스코, 델이었지만, 플랫폼 경쟁에서 뒤진 바람에 플랫폼을 효과적으로 활용하고 있는 신4인방에게 자리를 내주고 고전을 면치 못하고 있다. 이처럼 플랫폼이 기업 성패의 핵심요인으로 등장하면서 플랫폼에 대한 관심이 고조되고 있다.

플랫폼의 구성하는 요건

아래의 논문에서 플랫폼을 구성하는 요건을 제시하고 있다. 플랫폼을 규정하는 가장 명확한 정의라고 생각한다.

첫째 상호연결을 필요로 하는 둘 이상의 구분되는 고객 , 군(two distinct group) 이 존재하여야 한다 (양면성). 둘째 한 면의 고객군은 다른 면의 고객군의 규모 가 클수록 더욱 높은 효용을 얻어야 한다 (교차 네트워크 외부성). 셋째 높은 거래비용으로 인하여 서로 다른 고객군들이 자체적인 노력으로 직접 거래하는 것 이 불가능하기 때문에23), 즉 교차 네트워크 외부성을 내부화하는 것이 불가능하기 때문에 플랫폼을 이용하여야 거래가 성립된다 (플랫폼을 통한 외부성의 내부화). 따라서 플랫폼은 거래비용을 최소화하면서 외부성을 해소하는 방안을 제 공하여 주는 실질적 가상적 공간 또는 제도적 장치로 생각할 수 있다.

[클릭] 양면시장(플랫폼)의 정의에 대하여

공급자와 수요자의 상생 생태계

플랫폼하면 기차를 타고 내리는 정거장이 먼저 떠오른다. 그렇다. 플랫폼은 본래 기차를 승하차하는 공간이나 강사, 음악 지휘자, 선수 등이 사용하는 무대 강단 등을 뜻했다. 플랫폼 용어는 16세기에 생성된 이후 일상생활이나 예술, 비즈니스 등의 분야에서 사용해왔다. 그러던 것이 오늘날에는 다양한 분야에 적응 가능한 보편적인 개념으로 확대되어 널리 사용되기에 이르렀다.
 
 
플랫폼의 대표격인 승강장이 어떤 역할을 하는지 살펴보면, 플랫폼의 진정한 의미를 파악할 수 있다. 승강장은 교통수단과 승객이 만나고 연결하는 공간으로, 사람이 많이 몰리는 이곳에서 다양한 형태의 비즈니스 모델로 부가적인 수익 창출을 할 수가 있다. 주요 수익모델인 승차요금 외에도 광고 등 부가적인 비즈니스 모델로 상당한 수익을 창출하고 있는 것이다. 그래서 승강장은 교통수단과 승객이 만날 수 있는 거점 역할을 하며, 교통과 물류의 중심이 되고, 거래가 발생하게 된다. 이것이 바로 플랫폼이다.
 
 
정리하면, 플랫폼이란 공급자와 수요자 등 복수그룹이 참여해 각 그룹이 얻고자 하는 가치를 공정한 거래를 통해 교환할 수 있도록 구축된 환경이다. 플랫폼 참여자들의 연결과 상호작용을 통해 모두에게 새로운 가치와 혜택을 제공해 줄 수 있는 상생의 생태계라고 말할 수 있다. 애플이 기존의 휴대폰 시장의 강자들을 물리치고 스마트폰 시장에서 새로운 강자로 부상할 수 있었던 것은 훌륭한 플랫폼을 기반으로 한 비즈니스 생태계를 구축했기 때문이다.

기능과 참여자에 따라 달라지는 경제적 가치

플랫폼의 가치는 기업의 비즈니스 구조나 시스템에서 차지하는 비중과 비례한다. 만약 플랫폼이 전체 시스템에서 필수적으로 요구되는 기능들을 최대한 많이 포함하고 있다면, 그 가치는 상당할 것이다. 플랫폼은 세 가지 측면에서 기업에 가치를 제공한다.
 
 
첫째는 플랫폼이 어떤 기능을 수행하느냐 하는 것이다. 플랫폼이 핵심 비즈니스를 수행한다면 그 가치는 대단히 크겠지만, 고객 서비스를 위한 플랫폼으로 기능한다면 상대적으로 그 가치는 적을 것이다. 둘째, 플랫폼의 반복적 사용과 공유에 따라 경제적 가치가 발생한다. 플랫폼을 반복적으로 사용할수록 비용절감의 효과를 갖게 해준다. 특히 각 참여자들이 플랫폼을 공유하면 비용이 대폭 절감된다. 셋째, 플랫폼은 네트워크 효과도 제공하여, 교류나 거래를 촉진한다. 즉, 수요자와 공급자, 개발자와 사용자, 프로슈머 등 참여자가 많아지면 많아질수록 네트워크 효과가 발생하고, 이로 인한 가치가 극대화한다.

서비스와 수익 모델의 결합 필요

비즈니스 모델이란 사업을 영위하고 이익을 창출하는 방식, 즉 어떤 상품을 어떤 방식으로 누구에게 판매할 것인가를 설계해보는 것이고, 플랫폼 비즈니스 모델이란 플랫폼 서비스의 모델을 통해 수익을 내는 모델을 의미한다. 서비스 모델에는 서비스의 제공방법, 서비스 모델 프레임워크 등이 포함되며, 수익 모델에는 수익을 창출하는 방법과 수익 모델 프레임워크가 필요하다. 결국 서비스 모델과 수익 모델의 결합을 통해서만 비즈니스 모델이 될 수 있다.
 
 
플랫폼 비즈니스 모델의 특징은 다음과 같다. 첫째, 충분한 규모의 구매자나 판매자를 확보하는 것으로, 플랫폼은 규모의 경제를 실현하기 좋은 창구이다. 에어비엔비와 같은 좋은 플랫폼이 있어도, 그 플랫폼에 참가하는 공급자와 수요자가 없다면 플랫폼은 의미가 없다. 신규 플렛폼이 구매자와 판매자를 확보하기 위해 사업초기에 파격적인 프로모션을 진행하는 것도 그러한 이유이다. 둘째, 해당 플랫폼만의 차별적인 가치가 창출되어 고객에게 제공된다. 셋째, 다수의 사람들이 모인 네트워크를 혁신에 활용한다. 애플의 개방된 플랫폼이 그렇고, 최근에는 트위터나 페이스북에 관심을 갖는 것도 이 때문이다.
 
 
비즈니스를 수행하는 플랫폼이 내포해야 할 요소와 특징으로는 첫째, 플랫폼 주도기업은 설계, 운영, 업그레이드를 담당하고, 둘째 모방이 어려운 해당 플랫폼의 차별화 기반 기술을 가지고 있어야 한다. 셋째, 플랫폼 주도 기업과 참여자, 또는 참여자 간 공동의 이익이 존재해야 한다. 넷째, 비즈니스 플랫폼은 참여자의 기반확대와 업그레이드, 인접분야 확장 등 지속적으로 진화되어야 한다.

플랫폼 비즈니스의 구분

  • 하드웨어 플랫폼
    하드웨어에도 “플랫폼”이 있습니다. “Platform Technology” 위키피디아에선 이렇게 설명하고 있습니다. “제품개발을 가능하게 하는 기술이나, 현재 또는 미래의 개발을 지원하는 프로세스”
     
    제조업은 하드웨어를 대량생산.판매함으로써 돈을 법니다. 그래서 “공산품화”가 중요하죠. 동일품질의 제품을 안정적으로 만듭니다. “프로세스Process”와 “자동화Automation”가 필수죠. 작업공의 기분에 따라 품질이 변하지 않게 해줍니다. 그러면 품질이 일정해지고, 원가가 안정되죠.
     
    하드웨어에선 이런 “프로세스”와 “물리적 장치”가 “플랫폼”입니다. “현대 소나타와 기아 K5가 플랫폼을 공유한다.” 이런 이야기를 들어보셨을 겁니다. 엔진, 조향장치가 똑같다는 뜻입니다. 여기엔 품질검수기준 및 부품규격 같은 것도 포함이 됩니다. 이게 함께 있어야 플랫폼 운영과 복제가 가능하기 때문입니다. 그게 가능해야 공장을 늘릴 수 있거든요.
       
    즉 하드웨어에서 플랫폼이란, 표준공정을 통해 다양한 제품을 만들어내는 기반과 도구를 지칭합니다.
       
    “소프트웨어”는 어떨까? “소프트웨어”도 하드웨어처럼 “공정”이 중요한 때가 있었습니다. 컴퓨터 안에 설치해서 팔았거든요. 그래서 소프트웨어도 그 PC 제품의 일부분이었습니다.
       
    이 때의 소프트웨어는 하드웨어 부품 중의 하나였습니다. 그래서 프로세스와 제작공정이 중요했죠. 초창기 SI 업체들은 소프트웨어 제작공정을 “플랫폼”으로 만들고 싶어했습니다. 공정화된 품질경영을 앞세웠죠. 훌륭한 제품을 반복생산할 수 있음을 증명하려했습니다. 그 증명서가 “ISO 9001” 인증입니다. 건설업 같은 곳에서 쓰는 국제인증서죠. 암튼, 당시엔 이런 “소프트웨어 생산공정”도 “플랫폼”이라고 불렀습니다.  
  • 소프트웨어 플랫폼
    하드웨어에 관계없이 소프트웨어가 단독역할을 하게되자, 소프트웨어에도 “플랫폼”이라는 개념이 본격적으로 만들어집니다.
       
    수십년 전에는 컴퓨터 종류마다 소프트웨어를 다르게 만들어야 했습니다. CPU와 OS등 실행환경이 달랐기 때문이었습니다. Java와 웹브라우저가 나오면서 이 문제가 극복됩니다. 하드웨어가 아닌 Java, 웹브라우저 위에서 프로그램이 작동하도록 만들어집니다. 그래서 윈도우(OS), 브라우저, 자바 등을 “플랫폼”으로 부르기 시작합니다. 다양한 기능들이 세트로 모여있는 무엇이었거든요.
       
    특정분야에 한정된 실행환경도 새로운 “플랫폼”으로 불려집니다. MAME는 옛날 오락실 게임을 PC에서 실행시켜주는 실행환경용 프로그램입니다.
    처음에 10개 정도일때는 그냥 게임기로 불리었는데, 게임숫자가 40~50개를 넘어가자 게임플랫폼이라고 불리게 됩니다. 일부러 MAME용으로 게임을 만드는 개발회사가 생겼거든요.
       
    “개발플랫폼”이라는 개념도 등장합니다. 개발하기 쉽게 여러가지 환경을 제공해주는 것을 말합니다. 개발플랫폼에는 코딩을 쉽게 해주는 라이브러리들과 코딩을 도와주는 여러가지 도구들이 있습니다.
       
    이렇게 소프트웨어에선 “여러가지 기능을 제공해주는 공통 실행환경”을 플랫폼이라고도 말하게도 되었습니다.
  • 서비스 플랫폼
    웹브라우저가 생기면서, 인터넷으로만 돈을 버는 “서비스사업”이 생겼습니다. “인터넷포털”이 돈을 벌게 된거죠. 산업분류로는 “정보통신제공업”, 업계말로는 “인터넷서비스”라고 부릅니다. 이 “인터넷서비스”가 진화를 하자 “서비스플랫폼”이라는 용어가 등장합니다.
     
    대표사례가 “트위터”입니다. API 를 오픈함으로써 자기네 기능을 자유롭게 쓰게 합니다. 개발자들은 API 를 연동해서 앱을 만들고 광고를 붙여 돈을 벌기시작한 겁니다. 그런 앱들이 많아지자 “트위터”에 쌓이는 컨텐츠가 급격하게 많아지기 시작합니다. 그래서 트위터는 수많은 뉴스정보를 갖게 되었고, 개발자들은 소액이지만 수익활동을 할 수 있게 된 겁니다.
     
    즉, “서비스플랫폼”이란 다른 서비스들이 내 서비스를 쉽게 활용할 수 있게 해도록 해주는 인터넷 기반의 기술 환경을 말합니다.

서비스 플랫폼 비즈니스 모델의 유형

플랫폼 비즈니스 모델은 가게(store)형, 사슬(chain)형, 상거래(commerce)형, 대리인(agent)형, 웹서비스(web service)형, 그리고 게임 비즈니스 모델로 분류할 수 있다.
 
 
가게형 모델은 수요자와 공급자들이 플랫폼을 근거로 필요한 가치를 교환하는 비즈니스로, 애플의 앱스토어가 이에 해당한다.
 
 
사슬형 모델은 비즈니스 수행을 위한 가치 사슬을 플랫폼에 형성해 두고 이를 기반으로 참여자들의 협업을 통해 가치 활동을 전개해 비즈니스를 완성하는 것으로 홍콩의 리엔펑이 대표적인 사례이다. 패션산업 강자로 알려진 리엔펑은 단 한 명의 재봉사도 없이 의류 생산업체와 고객을 연결해주는 플랫폼 역할에 충실할 뿐이다.
 
 
상거래형 모델은 다양한 상품 공급자와 수요자들이 만나는 상거래 모델과 사이트를 플랫폼으로 운영하는 비즈니스로, 단순한 정보의 중개자에서 거대한 커머스 플랫폼을 구축하는 것을 전략으로 하는 이베이가 이에 해당한다.
 
 
대리인형 모델은 플랫폼으로서 거래 사이트가 직접 매매를 하는 것이 아니라 공급자와 수요자들이 자유롭게 매매할 수 있도록 서비스하는 대리인 역할을 하는 비즈니스로 온라인 서점의 대표주자인 아마존이 소매점 전략에서 에이전트 전략으로 수 한 유통업에서 플랫폼을 통한 디지털 중개업자가 된 아마존의 행보는 플랫폼 전략의 미래를 보여주는 듯하다.
 
 
웹서비스 모델은 플랫폼 기반의 각종 원격 컴퓨팅 서비스를 비즈니스로 하는 모델로, 아마존 웹서비스(AWS)가 대표적이다. AWS는 클라우드 컴퓨팅 환경에서 다른 웹사이트나 클라이언트 측 응용프로그램에 대해 온라인 서비스를 제공하는데, 모두 플랫폼 기반에서 제공되고 있다.
 
 
게임 비즈니스 모델은 플랫폼 기반으로 게임에 관한 수요와 공급을 연결하는 비즈니스이다. 페이스북이 많은 플랫폼 기반의 게임 비즈니스 모델을 확보하고 있으며, 카카오톡 역시 2012년 7월30일부터 플랫폼을 기반으로 한 게임 비즈니스를 시작해 성공을 거두었다. (월간[CEO&] 2015.09월호 참조)


https://ko.javascript.info/try-catch

'try..catch'와 에러 핸들링

아무리 프로그래밍에 능한 사람이더라도 에러가 있는 스크립트를 작성할 수 있습니다. 원인은 아마도 실수, 예상치 못한 사용자 입력, 잘못된 서버 응답 등의 수천만 가지 이유 때문일 겁니다.

에러가 발생하면 스크립트는 ‘죽고’(즉시 중단되고), 콘솔에 에러가 출력됩니다.

그러나 try..catch 문법을 사용하면 스크립트가 죽는 걸 방지하고, 에러를 ‘잡아서(catch)’ 더 합당한 무언가를 할 수 있게 됩니다.

‘try…catch’ 문법

‘try…catch’ 문법은 'try’와 'catch’라는 두 개의 주요 블록으로 구성됩니다.

try {

  // 코드...

} catch (err) {

  // 에러 핸들링

}

동작 알고리즘은 다음과 같습니다.

  1. 먼저, try {...} 안의 코드가 실행됩니다.
  2. 에러가 없다면, try 안의 마지막 줄까지 실행되고, catch 블록은 건너뜁니다.
  3. 에러가 있다면, try의 실행이 중단되고, catch(err) 블록으로 제어 흐름이 넘어갑니다. 변수 err(아무 이름이나 사용 가능)은 무슨 일이 일어났는지에 대한 설명이 담긴 에러 객체를 포함합니다.

따라서 try {…} 블록 안에서 에러가 발생해도 catch에서 에러를 다루기 때문에 스크립트가 죽지 않습니다.

예제를 살펴봅시다.

  • 에러가 없는 예제 – (1)과 (2)를 alert 창에 보여줌

    
    
    
    
    
    
                                     
    
                                     try {
    
      alert('try 블록 시작');  // (1) <--
    
      // ...에러가 없습니다.
    
      alert('try 블록 끝');   // (2) <--
    
    } catch(err) {
    
      alert('에러가 없으므로, catch는 무시됩니다.'); // (3)
    
    }
  • 에러가 있는 예제 – (1)과 (3)을 보여줍니다.

    
    
    
    
    
    
    
    
    
    
                                      
    
    
    
    
    
    
                                     try {
    
      alert('try 블록 시작');  // (1) <--
    
      lalala; // 에러, 변수가 정의되지 않음!
    
      alert('try 블록 끝(절대 도달하지 않음)');  // (2)
    
    } catch(err) {
    
      alert(`에러가 발생했습니다!`); // (3) <--
    
    }
try..catch는 오직 런타임 에러에만 동작합니다.

try..catch는 실행 가능한(runnable) 코드에만 동작합니다. 실행 가능한 코드는 유효한 자바스크립트 코드를 의미합니다.

중괄호 짝이 안 맞는 것처럼 코드가 문법적으로 잘못된 경우엔 try..catch가 동작하지 않습니다.

try {
  {{{{{{{{{{{{
} catch(e) {
  alert("유효하지 않은 코드이기 때문에, 자바스크립트 엔진은 이 코드를 이해할 수 없습니다.");
}

자바스크립트 엔진은 코드를 읽고 난 후 코드를 실행합니다. 코드를 읽는 중에 발생하는 에러는 ‘parse-time’ 에러라고 부르는데, 엔진은 이 코드를 이해할 수 없기 때문에 parse-time 에러는 (코드 안에서) 복구가 불가능합니다.

try..catch는 유효한 코드에서 발생하는 에러만 처리할 수 있습니다. 이런 에러를 ‘런타임 에러(runtime error)’ 혹은 '예외(exception)'라고 부릅니다.

try..catch는 동기적으로 동작합니다.

setTimeout처럼 ‘스케줄 된(scheduled)’ 코드에서 발생한 예외는 try..catch에서 잡아낼 수 없습니다.

try {
  setTimeout(function() {
    noSuchVariable; // 스크립트는 여기서 죽습니다.
  }, 1000);
} catch (e) {
  alert( "작동 멈춤" );
}

setTimeout에 넘겨진 익명 함수는 엔진이 try..catch를 떠난 다음에서야 실행되기 때문입니다.

스케줄 된 함수 내부의 예외를 잡으려면, try..catch를 반드시 함수 내부에 구현해야 합니다.

setTimeout(function() {
  try {
    noSuchVariable; // 이제 try..catch에서 에러를 핸들링 할 수 있습니다!
  } catch {
    alert( "에러를 잡았습니다!" );
  }
}, 1000);

에러 객체

에러가 발생하면 자바스크립트는 에러 상세내용이 담긴 객체를 생성합니다. 그 후, catch 블록에 이 객체를 인수로 전달합니다.

try {
  // ...
} catch(err) { // <-- '에러 객체', err 대신 다른 이름으로도 쓸 수 있음
  // ...
}

모든 내장 에러를 포함한 에러 객체는 두 가지 주요 프로퍼티를 가집니다.

name
에러 이름. 정의되지 않은 변수 때문에 발생한 에러라면 "ReferenceError"가 이름이 됩니다.
message
에러 상세 내용을 담고 있는 문자 메시지

두 프로퍼티 이외에 표준은 아니지만, 대부분의 호스트 환경에서 지원하는 프로퍼티도 있습니다. stack은 가장 널리 사용되는 비표준 프로퍼티 중 하나입니다.

stack
현재 호출 스택. 에러를 유발한 중첩 호출들의 순서 정보를 가진 문자열로 디버깅 목적으로 사용됩니다.

예시:



try {
  lalala; // 에러, 변수가 정의되지 않음!
} catch(err) {
  alert(err.name); // ReferenceError
  alert(err.message); // lalala is not defined
  alert(err.stack); // ReferenceError: lalala is not defined at (호출 스택)

  // 에러 전체를 보여줄 수도 있습니다.
  // 이때, 에러 객체는 "name: message" 형태의 문자열로 변환됩니다.
  alert(err); // ReferenceError: lalala is not defined
}

선택적 ‘catch’ 바인딩

최근에 추가됨
스펙에 추가된 지 얼마 안 된 문법입니다. 구식 브라우저는 폴리필이 필요합니다.

에러에 대한 자세한 정보가 필요하지 않으면, catch에서 이를 생략할 수 있습니다.

try {
  // ...
} catch { // <-- (err) 없이 쓰임
  // ...
}

‘try…catch’ 사용하기

try..catch가 실무에서 어떻게 사용되는지 알아봅시다.

앞서 JSON으로 인코딩된 값을 읽을 수 있도록 해주는 JSON.parse(str) 메서드에 대해 배운 바 있습니다.

이 메서드는 주로 서버 등에서 네트워크를 통해 전달받은 데이터를 디코딩하는 데 사용합니다.

전달받은 데이터에 JSON.parse를 호출하는 식으로 사용되죠.




let json = '{"name":"John", "age": 30}'; // 서버로부터 전달받은 데이터

let user = JSON.parse(json); // 전달받은 문자열을 자바스크립트 객체로 변환

// 문자열 형태로 전달받은 user가 프로퍼티를 가진 객체가 됨
alert( user.name ); // John
alert( user.age );  // 30

JSON에 관한 자세한 정보는 JSON과 메서드 챕터에서 찾을 수 있습니다.

json의 형식이 잘못된 경우, JSON.parse가 에러를 만들기 때문에 스크립트가 ‘죽습니다’.

스크립트가 죽는 것에 만족해야 할까요? 당연히 아니죠!

서버에서 전달받은 데이터가 잘못되어 스크립트가 죽는 경우, 사용자는 (개발자 콘솔을 열지 않는 이상) 원인을 절대 알 수 없습니다. 그런데 사람들은 에러의 원인을 알려주는 메시지 없이 무언가가 '그냥 죽는 것’을 정말 싫어합니다.

try..catch를 사용해 이를 처리해 봅시다.


















let json = "{ bad json }";

try {

  let user = JSON.parse(json); // <-- 여기서 에러가 발생하므로
  alert( user.name ); // 이 코드는 동작하지 않습니다.

} catch (e) {
  // 에러가 발생하면 제어 흐름이 catch 문으로 넘어옵니다.
  alert( "데이터에 에러가 있어 재요청을 시도합니다." );
  alert( e.name );
  alert( e.message );
}

위 예시에선 에러가 발생했다는 걸 단순히 보여주기 위해 catch 블록을 사용했지만, catch 블록 안에서 새로운 네트워크 요청 보내기, 사용자에게 대안 제안하기, 로깅 장치에 에러 정보 보내기 등과 같은 일을 할 수 있습니다. 스크립트가 죽도록 놔두는 것보다 훨씬 나은 대응이죠.

자체 에러 던지기

json이 문법적으로 잘못되진 않았지만, 필수 프로퍼티 name을 가지고 있지 않다면 무슨 일이 생길까요?

다음처럼 말이죠.







let json = '{ "age": 30 }'; // 불완전한 데이터

try {

  let user = JSON.parse(json); // <-- 에러 없음
  alert( user.name ); // 이름이 없습니다!

} catch (e) {
  alert( "실행되지 않습니다." );
}

위 예시에서 JSON.parse는 정상적으로 실행되었지만 name이 없는 건 에러를 유발하는 상황입니다.

이제 throw 연산자를 사용해 에러 처리를 통합해 보도록 하겠습니다.

‘Throw’ 연산자

throw 연산자는 에러를 생성합니다.

문법은 다음과 같습니다.

throw <error object>

이론적으로는 숫자, 문자열 같은 원시 타입을 포함한 어떤 것이든 에러 객체로 사용할 수 있습니다. 하지만 되도록 (내장 에러와의 호환을 위해) name과 message 프로퍼티가 있는 객체를 에러 객체로 사용하는 것을 권장합니다.

자바스크립트는 ErrorSyntaxErrorReferenceErrorTypeError등의 표준 에러 객체를 만들어주는 내장 생성자를 지원합니다. 이 생성자들을 이용해 에러 객체를 만들 수도 있습니다.

아래와 같이 말이죠.

let error = new Error(message);
// or
let error = new SyntaxError(message);
let error = new ReferenceError(message);
// ...

일반 객체가 아닌 내장 생성자를 사용해 만든 내장 에러 객체의 name 프로퍼티는 생성자의 이름과 동일한 값을 갖습니다. 프로퍼티 message의 값은 인수에서 가져옵니다.

예시:

let error = new Error("이상한 일이 발생했습니다. o_O");

alert(error.name); // Error
alert(error.message); // 이상한 일이 발생했습니다. o_O

잘못된 데이터를 받았을 때, JSON.parse가 어떤 종류의 에러를 만들어내는지 아래 코드를 통해 살펴봅시다.





try {
  JSON.parse("{ 잘못된 형식의 json o_O }");
} catch(e) {
  alert(e.name); // SyntaxError
  alert(e.message); // Unexpected token b in JSON at position 2
}

SyntaxError가 발생하네요.

사용자를 나타내는 객체에 name 프로퍼티는 반드시 있어야 하므로, 우리 예시에선 name이 없으면 에러가 발생한 것으로 간주합시다.

throw 연산자를 사용해 에러를 던져보겠습니다.









let json = '{ "age": 30 }'; // 불완전한 데이터

try {

  let user = JSON.parse(json); // <-- 에러 없음

  if (!user.name) {
    throw new SyntaxError("불완전한 데이터: 이름 없음"); // (*)
  }

  alert( user.name );

} catch(e) {
  alert( "JSON Error: " + e.message ); // JSON Error: 불완전한 데이터: 이름 없음
}

(*)로 표시한 줄에서 throw 연산자는 message를 이용해 자바스크립트가 자체적으로 에러를 생성하는 방식과 동일한 방식으로 SyntaxError를 생성합니다. 에러가 발생했으므로 try의 실행은 즉시 중단되고 제어 흐름은 catch로 넘어갑니다.

이제 JSON.parse에서 에러가 발생한 경우를 포함한 모든 에러를 catch 블록 안에서 처리할 수 있게 되었습니다.

에러 다시 던지기

위 예제에선 try..catch를 사용해 불완전한 데이터를 처리하였습니다. 그런데 또 다른 예기치 않은 에러가 try {...} 블록 안에서 발생 할 수도 있습니다. 정의하지 않은 변수 등의 프로그래밍 에러가 발생할 가능성은 항상 있습니다.

예시:

let json = '{ "age": 30 }'; // 불완전한 데이터

try {
  user = JSON.parse(json); // <-- user 앞에 let을 쓰지 않음

  // ...
} catch(err) {
  alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined
  // (실제론 JSON Error가 아닙니다.)
}

어떤 에러든 발생할 수 있습니다! 끔찍한 해킹으로 이어질 수 있는 엄청난 버그가 몇십 년간 몇백만 명이 사용한 오픈소스 유틸리티에서도 조차 발견됩니다.

위에선 '불완전한 데이터’를 다루려는 목적으로 try..catch를 썼습니다. 그런데 catch는 근본적으로 try 블록에서 발생한 모든 에러들을 잡습니다. 위 코드에서 catch는 예상치 못한 에러를 잡아냈긴 했지만, 에러의 종류와 관계없이 “JSON Error” 메시지를 보여줍니다. 이렇게 에러 종류와 관계없이 동일한 방식으로 에러를 처리하는 방식은 디버깅을 어렵게 만들기 때문에 좋지 않습니다.

다행히도, name 프로퍼티를 사용하면 에러의 종류를 알아낼 수 있습니다.





try {
  user = { /*...*/ };
} catch(e) {
  alert(e.name); // 존재하지 않는 변수에 접근하려 했으므로 "ReferenceError"가 발생
}

에러 처리 규칙은 간단합니다.

catch에선 타입을 알고 있는 에러만 처리하고, 나머지 에러는 모두 ‘다시 던집니다(rethrow)’.

‘다시 던지기’ 기술을 자세히 설명해보겠습니다.

  1. catch에서 모든 에러를 받습니다.
  2. catch(err) {...} 블록에서 에러 객체, err을 분석합니다.
  3. 에러를 어떻게 처리할지 모르는 경우는 throw err로 처리합니다.

다시 던지기를 사용하여 catch 블록에선 SyntaxError만 처리하도록 해보겠습니다.































let json = '{ "age": 30 }'; // 불완전한 데이터
try {

  let user = JSON.parse(json);

  if (!user.name) {
    throw new SyntaxError("불완전한 데이터: 이름 없음");
  }

  blabla(); // 예상치 못한 에러

  alert( user.name );

} catch(e) {

  if (e.name == "SyntaxError") {
    alert( "JSON Error: " + e.message );
  } else {
    throw e; // 에러 다시 던지기 (*)
  }

}

catch 블록 안의 (*)로 표시된 줄에서 다시 던져진(rethrow) 에러는 try..catch ‘밖으로 던져집니다’. 이때 바깥에 try..catch가 있다면 여기서 에러를 잡습니다. 아니라면 스크립트가 죽겠죠.

따라서 catch 블록은 어떻게 다룰지 알고 있는 에러만 처리하고, 알 수 없는 에러는 ‘건너뜁니다’.

아래 예시에선 try..catch를 한 레벨 더 만들어, 예상치 못한 에러를 어떻게 처리하는지 보여줍니다.



































function readData() {
  let json = '{ "age": 30 }';

  try {
    // ...
    blabla(); // 에러!
  } catch (e) {
    // ...
    if (e.name != 'SyntaxError') {
      throw e; // 알 수 없는 에러 다시 던지기
    }
  }
}

try {
  readData();
} catch (e) {
  alert( "External catch got: " + e ); // 에러를 잡음
}

readData는 SyntaxError 처리 방법만 알고 있는 반면, 함수 바깥의 try..catch는 예상치 못한 에러 처리 방법도 알고 있습니다.

try…catch…finally

여기서 끝이 아닙니다.

try..catch는 finally라는 코드 절을 하나 더 가질 수 있습니다.

finally안의 코드는 아래 상황에서 실행됩니다.

  • 에러가 없는 경우, try 실행이 끝난 후
  • 에러가 있는 경우, catch 실행이 끝난 후

finally를 사용해 try..catch를 확장하면 다음과 같습니다.





         

          try {
   ... 코드를 실행 ...
} catch(e) {
   ... 에러 핸들링 ...
} finally {
   ... 항상 실행 ...
}

아래 코드를 실행해보세요.

try {
  alert( 'try' );
  if (confirm('에러를 만드시겠습니까?')) 이상한_코드();
} catch (e) {
  alert( 'catch' );
} finally {
  alert( 'finally' );
}

위 코드는 두 가지 경로로 실행됩니다.

  1. "에러를 만드시겠습니까"에 "OK"로 답한 경우: try -> catch -> finally
  2. "No"로 답한 경우: try -> finally

finally 절은 무언가를 실행하고, 결과에 상관없이 실행을 완료하고 싶을 경우 사용됩니다.

피보나치 함수 fib(n)의 연산 시간을 측정하고 싶다고 해 봅시다. 함수 실행 전에 측정을 시작해서 실행이 끝난 후 측정을 종료하면 되겠죠. 그런데 함수 실행 도중 에러가 발생하면 어떻게 될까요? 아래에서 구현한 함수 fib(n)는 음수나 정수가 아닌 수가 입력될 경우 에러가 반환됩니다.

finally 절은 무슨 일이 일어났든 관계없이 연산 시간 측정을 종료하기 적절한 곳입니다.

fib 함수가 에러 없이 정상적으로 실행되든 에러를 발생하든 상관없이, finally를 사용하면 연산 시간을 제대로 측정할 수 있습니다.





















let num = +prompt("양의 정수를 입력해주세요.", 35)

let diff, result;

function fib(n) {
  if (n < 0 || Math.trunc(n) != n) {
    throw new Error("음수나 정수가 아닌 값은 처리할 수 없습니다.");
  }
  return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}

let start = Date.now();

try {
  result = fib(num);
} catch (e) {
  result = 0;
} finally {
  diff = Date.now() - start;
}

alert(result || "에러 발생");

alert( `연산 시간: ${diff}ms` );

코드를 실행하고 35를 프롬프트 대화상자에 입력하면 try 다음에 finally가 정상적으로 실행되면서 연산 시간을 확인할 수 있습니다. -1을 입력하면 에러가 발생하고, 연산 시간은 0ms가 됩니다. 두 경우 모두 연산 시간이 정상적으로 측정되었습니다.

함수는 return 이나 throw를 만나면 종료되는데, finally 절은 이용하면 두 경우를 모두 실행됩니다.

try..catch..finally 안의 변수는 지역 변수입니다.

위 예시에서 변수 diff와 result는 try..catch  에 선언되었다는 점에 주의해 주세요.

try 블록 안에서 변수를 선언했다면, 블록 안에서만 유효한 지역 변수가 됩니다.

finally 와 return

finally 절은 try..catch절을 빠져나가는 어떤 경우에도 실행됩니다. return을 사용해 명시적으로 빠져나가려는 경우도 마찬가지 입니다.

아래 예시의 try 블록 안엔 return이 있습니다. 이 경우엔 값이 바깥 코드로 반환되기 전에 finally가 실행됩니다.














function func() {

  try {
    return 1;

  } catch (e) {
    /* ... */
  } finally {
    alert( 'finally' );
  }
}

alert( func() ); // finally 안의 alert가 실행되고 난 후, 실행됨
try..finally

catch 절이 없는 try..finally 구문도 상황에 따라 유용하게 쓸 수 있습니다. try..finally 안에선 에러를 처리하고 싶지 않지만, 시작한 프로세스가 마무리되었는지 확실히 하고 싶은 경우에 사용합니다.

function func() {
  // 무언가를 측정하는 경우와 같이 끝맺음이 있어야 하는 프로세스
  try {
    // ...
  } finally {
    // 스크립트가 죽더라도 완료됨
  }
}

위 코드엔 catch가 없기 때문에 try 안에서 발생한 에러는 항상 밖으로 떨어져 나옵니다. 그러나 실행 흐름이 함수를 떠나기 전에 finally가 실행됩니다.

전역 catch

호스트 환경을 확인하세요

이 절은 코어 자바스크립트가 아닙니다.

try..catch 바깥에서 치명적인 에러가 발생해 스크립트가 죽었다고 상상해봅시다.

대처 방법은 무엇이 있을까요? 에러를 기록하거나 사용자에게 무언가를 보여주는 것 등을 할 수 있을 겁니다.

자바스크립트 명세에는 이런 치명적인 에러에 대응하는 방법이 적혀있지 않습니다. 하지만 try..catch 밖의 에러를 잡는 것은 아주 중요하기 때문에, 자바스크립트 호스트 환경 대다수는 에러 처리 기능을 제공합니다. Node.js의 process.on("uncaughtException")이 그 예입니다. 브라우저 환경에선 window.onerror를 이용해 에러를 처리합니다. window.onerror프로퍼티에 함수를 할당하면, 예상치 못한 에러가 발생했을 때 이 함수가 실행됩니다.

문법:

window.onerror = function(message, url, line, col, error) {
  // ...
};
message
에러 메시지
url
에러가 발생한 스크립트의 URL
linecol
에러가 발생한 곳의 줄과 열 번호
error
에러 객체

예시:





<script>
  window.onerror = function(message, url, line, col, error) {
    alert(`${message}\n At ${line}:${col} of ${url}`);
  };

  function readData() {
    badFunc(); // 에러가 발생한 장소
  }

  readData();
</script>

전역 핸들러 window.onerror를 죽어버린 스크립트를 복구하려는 목적으론 잘 사용하지 않습니다. 프로그래밍 에러가 발생한 경우 window.onerror만으로 스크립트를 복구하는 건 사실상 불가능 합니다. window.onerror는 개발자에게 에러 메시지를 보내는 용도로 사용합니다.

window.onerror말고 https://errorception.com 나 http://www.muscula.com같은 에러 로깅과 관련된 여러 가지 상용 서비스가 있습니다.

이런 서비스들은 다음과 같은 프로세스로 동작합니다.

  1. 서비스를 가입하면 자바스크립트 파일(혹은 스크립트 url)을 받는데, 개발자는 이 파일을 페이지에 삽입합니다.
  2. 받은 파일은 커스텀 window.onerror 함수를 설정합니다.
  3. 에러가 발생하면, 이 커스텀 함수가 에러에 관한 내용을 담아 서비스에 네트워크 요청을 보냅니다.
  4. 서비스 사이트에 로그인하고 기록된 에러를 봅니다.

요약

try..catch를 이용하면 런타임 에러를 처리할 수 있습니다. try에선 코드를 실행하고, 에러가 발생하면 catch에서 잡아냅니다.

문법은 다음과 같습니다.

try {
  // 이곳의 코드를 실행
} catch(err) {
  // 에러가 발생하면, 여기부터 실행됨
  // err는 에러 객체
} finally {
  // 에러 발생 여부와 상관없이 try/catch 이후에 실행됨
}

catch 나 finally를 따로 다루는 절이 없는 걸 봐서, try..catch와 try..finally도 유효한 문법이란 걸 유추할 수 있습니다.

에러 객체엔 다음 프로퍼티가 있습니다.

  • message – 사람이 읽을 수 있는 형태의 에러 메시지
  • name – 에러 이름을 담은 문자열 (에러 생성자 이름)
  • stack (표준이 아니지만 대부분의 호스트 환경이 지원함) – 에러가 발생한 순간의 스택

에러 객체가 필요 없으면 이를 생략하여 catch(err) { 대신 catch {를 쓸 수 있습니다.

throw 연산자를 사용하면 에러를 직접 만들 수 있습니다. 이론상으론, throw의 인수에 모든 것이 가능하지만, 대게 내장 Error 클래스를 상속받은 에러 객체가 인수에 들어갑니다. 확장 에러에 대해선 다음 챕터에서 다루도록 하겠습니다.

다시 던지기는 에러 처리 시 사용되는 중요한 패턴입니다. catch 블록에선 대게, 예상하였거나 어떻게 다룰지 알고 있는 에러를 다루고, 예상치 못한 에러는 다시 던지기 합니다.

try..catch가 없어도 대부분의 호스트 환경이 ‘전역’ 에러 핸들러를 지원해주기 때문에 ‘떨어져 나온’ 에러를 잡을 수 있습니다. 브라우저의 전역 에러 핸들러는 window.onerror입니다.

과제

중요도: 5

두 코드 조각을 비교해보세요.

  1. 첫 번째 코드 조각은 try..catch 이후에 코드를 실행하기 위해 finally를 사용하였습니다.

    
    
    
    
    
    
    try {
      work work
    } catch (e) {
      handle errors
    } finally {
      작업 내역 삭제
    }
  2. 두 번째 코드 조각에선 try..catch 바로 아래에 작업 내역을 삭제하는 코드를 놓았습니다.

    
    
    
    
    
    
    
    try {
      work work
    } catch (e) {
      handle errors
    }
    
    작업 내역 삭제

에러의 유무와 상관없이, 작업 후에는 초기화가 필요합니다.

finally를 사용하면 이점이 있을까요? 아니면 두 코드 조각은 동일하게 동작할까요? 만약 이점이 있다면, 이점이 드러나는 예시를 제시해 주세요.


https://dev.to/sidthesloth92/understanding-html-form-encoding-url-encoded-and-multipart-forms-3lpa


Understanding HTML Form Encoding: URL Encoded and Multipart Forms

   ・5 min read 

The other day I was trying to write a REST endpoint in Go, which uploads the contents of a form submitted in a browser to another REST endpoint, in other words,

Form in Browser ----> My GO Rest API ----> Another REST API

While doing that I ended up learning some fundamentals of how HTML forms work. So thought it might be a good thing to share what I learned and hence the post.. :)

The encoding type of a form is determined by the attribute enctype. It can have three values,

  • application/x-www-form-urlencoded - Represents an URL encoded form. This is the default value if enctype attribute is not set to anything.

  • multipart/form-data - Represents a Multipart form. This type of form is used when the user wants to upload files

  • text/plain - A new form type introduced in HTML5, that as the name suggests, simply sends the data without any encoding

Now, let us look at each form type with an example to understand them better.

URL Encoded Form

As the name suggests, the data that is submitted using this type of form is URL endcoded. Take the following form,

<form action="/urlencoded?firstname=sid&lastname=sloth" method="POST" enctype="application/x-www-form-urlencoded">
    <input type="text" name="username" value="sidthesloth"/>
    <input type="text" name="password" value="slothsecret"/>
    <input type="submit" value="Submit" />
</form>

Here, you can see that the form is submitted to the server using a POST request, this means that it has a body. But how is the body formatted? It is URL encoded. Basically, a long string of (name, value) pairs are created. Each (name, value) pair is separated from one another by a & (ampersand) sign, and for each (name, value) pair, the name is separated from the value by an = (equals) sign, like say,

key1=value1&key2=value2

For the above form, it would be,
username=sidthesloth&password=slothsecret

Also, notice that we have some query parameters passed in the action URL, /urlencoded?firstname=sid&lastname=sloth.

Don't the URL encoded body and the query parameters passed in the action URL look awfully similar? It's because they are similar. They share the same format discussed above.

Try creating an HTML file with the above code and see how it's submitted in the dev tools. Here is a snap,

URL Encoded Snapshot

The things to notice here are the Content-Type header which says application/x-www-form-urlencoded, the query string and the form fields are transferred to the server in the format as discussed above.

Note: Don't get confused by the term Form Data in the screen shot. It's just how Google Chrome represents form fields.

All is fine, but there is a little more to the encoding process. Let's introduce some spaces in the submitted values, take the below form which is the same as the previous one but has the firstname value changed from sid to sid slayer and username value changed from sidthesloth to sid the sloth.

<form action="/urlencoded?firstname=sid slayer&lastname=sloth" method="POST" enctype="application/x-www-form-urlencoded">
    <input type="text" name="username" value="sid the sloth"/>
    <input type="text" name="password" value="slothsecret"/>
    <input type="submit" value="Submit" />
</form>

Now try to submit the form and see how the form fields are transferred in the dev tools. Here is a dev tools snap in Chrome.

URL Encoded snapshot with space

Clearly, you can see that the spaces are replaced by either '%20' or '+'. This is done for both the query parameters and the form body.

Read this to understand when + and %20 can be used. This encompasses the URL encoding process.

Multipart Forms

Multipart forms are generally used in contexts where the user needs files to be uploaded to the server. However, we'll just focus on simple text field based forms, as is it enough to understand how they work.

To convert the above form into a multipart form all you have to do is to change the enctype attribute of the form tag from application/x-www-form-urlencoded to multipart/form-data.

<form action="/multipart?firstname=sid slayer&lastname=sloth" method="POST" enctype="multipart/form-data">
    <input type="text" name="username" value="sid the sloth"/>
    <input type="text" name="password" value="slothsecret"/>
    <input type="submit" value="Submit" />
</form>

Let's go ahead and submit it and see how it appears in the dev tools.

URL Encoded snapshot with space

There are the two things to notice here, the Content-Type header and the payload of the form request. Let's go through them one by one.

Content-Type Header

The value of the Content-Type header is obviously multipart/form-data. But it also has another value, boundary. The value for this in the example above is generated by the browser, but the user can very well define it as well, say for example, boundary=sidtheslothboundary. We'll get to see how it's useful in the next section.

Request Body

The request payload contains the form fields themselves. Each (name, value) pair is converted into a MIME message part of the following format,

--<<boundary_value>>
Content-Disposition: form-data; name="<<field_name>>"

<<field_value>>

The above format is repeated for each (name, value) pair.

Finally, the entire payload is terminated by the boundary value suffixed with a --. So the entire request looks like,

--<<boundary_value>>
Content-Disposition: form-data; name="<<field_name>>"

<<field_value>>
--<<boundary_value>>
Content-Disposition: form-data; name="<<field_name>>"

<<field_value>>
--<<boundary_value>>--

Now, we see how the boundary value is used.

In the case of an application/x-www-form-urlencoded form, the & ampersand kind of acts as a delimiter between each (name, value) pair, enabling the server to understand when and where a parameter value starts and ends.

username=sidthelsloth&password=slothsecret

In the case of a multipart/form-data form, the boundary value serves this purpose. Say if the boundary value was XXX, the request payload would look like,

--XXX
Content-Disposition: form-data; name="username"

sidthesloth
--XXX
Content-Disposition: form-data; name="password"

slothsecret
--XXX--

The hyphens themselves are not part of the boundary value but rather needed as part of the request format. The Content-Type header for the above request would be,

Content-Type: multipart/form-data; boundary=XXX

This allows the browser to understand, when and where each field starts and ends.

Text/plain Forms

These forms are pretty much the same as the URL encoded forms, except that the form fields are not URL encoded when sent to the server. These are not used widely in general, but they have been introduced as a part of the HTML 5 specification.

Avoid using them as they meant for human understanding and for machines.

As quoted from the spec,

Payloads using the text/plain format are intended to be human readable. They are not reliably interpretable by computer, as the format is ambiguous (for example, there is no way to distinguish a literal newline in a value from the newline at
the end of the value).

Hope, I was clear in explaining what I learnt..See you in the next one guys..Peace.. :)



https://jaeyeophan.github.io/2017/05/16/Everything-about-babel/

[Tool] (번역) Everything you need to know about BabelJS

원본 : http://kleopetrov.me/2016/03/18/everything-about-babel/
이 글은 위 글을 기반으로 하여 나름 최신으로 업데이트하며 작성되었습니다 :) 물론, 본 저자의 동의하에 작성되었습니다.

babel

Babel은 아시다시피 ES6/ES7 코드를 ECMAScript5 코드로 transpiling 하기 위한 도구입니다. Babel은 다양한 작은 모듈들로 구성되어 있습니다. Babel 다양한 모듈을 담는 일종의 상자 역할을 하며 코드를 컴파일 하기 위해 작은 모듈들(ex. presets)을 사용합니다.

Set up

babel 학습을 위한 디렉토리를 구축합니다.

1
2
3
4
$ mkdir babel-tutorial && cd babel-tutorial
$ npm init
$ mkdir src && touch src/example.js
# Write some code of ES6 syntax in example.js

bael-cli

bael-cli는 command line을 통해 코드를 transpile 할 수 있는 도구입니다.

1
2
3
$ npm install --save-dev babel-cli
# or
$ yarn add -D babel-cli

-g 옵션을 통해서 bael-cli를 전역에 설치할 수도 있지만 --save-dev 옵션으로 설치하는 이유는 하나의 컴퓨터에 존재하는 다른 프로젝트들이 각각 다른 버전의 babel에 의존성을 갖고 있을 수 있습니다. --save-dev 옵션을 통해서 이를 해결할 수 있습니다.

설치한 후 터미널에서 다음 명령을 실행할 수 있습니다.

1
$ babel example.js --out-file compiled.js

이 명령어는 다음과 같은 의미를 담고 있습니다.

  • babel - babel을 호출합니다.
  • example.js - transpile 하고자하는 ES6/ES7의 자바스크립트 파일입니다.
  • --out-file - babel에게 전달할 옵션을 명시합니다. 파일로 output을 지정하는 옵션입니다.
    cf1> shortcut으로 -o 옵션을 제공합니다.
    cf2> 이 이외에도 --out-dir or -d 옵션을 전달할 수 있습니다.
  • compiled.js - 출력 파일의 이름을 명시합니다.

npm script를 사용하여 해당 프로세스를 자동화 할 수 있습니다.

package.json
1
2
3
4
5
6
7
{
...
"scripts": {
"build": "babel ./src -d ./lib -w"
}
...
}

and

1
$ npm run build

src 디렉토리 밑에 있는 파일을 transpile하여 lib 디렉토리 밑으로 output을 출력합니다. 이 때는 동일한 파일명이 사용됩니다. -w 옵션을 통해서 src 디렉토리 밑의 파일들이 변경될 때마다 자동으로 transpile 하도록 할 수 있습니다.

babel-register

babel-register는 각각의 모듈을 결합할 때 사용되는 후크(Hook) 모듈입니다. require 메소드를 바인드하여 자바스크립트 코드를 transpile 시킵니다. babel-register 모듈은 production을 위한 모듈은 아닙니다. 예를 들어 mocha 기반의 ES6로 작성된 테스트 코드를 실행시키기 위해서는 다음과 같은 스크립트를 사용할 수 있습니다.

package.json
1
2
3
"script": {
"test": "mocha --require babel-register"
}

Configuring Babel

처음에도 말했듯이, babel에게 어떠한 정보를 전달해주지 않는 한 babel은 아무 작업도 수행하지 않는 ‘상자’에 불과합니다. 방금 전에 살펴봤던 예제에서 아무 옵션없이 babel을 실행시키면 src 디렉토리에 있는 파일을 lib 디렉토리에 옮기는 작업만 수행하게 됩니다. 그렇기 때문에 babel에게 설정 정보를 전달해줘야 합니다. 이 정보는 .babelrc파일을 통해서 전달할 수 있습니다.

.babelrc

.babelrc파일은 babel을 설정하기 위한 파일입니다. 다음과 같이 구성되어 있습니다.

.babelrc
1
2
3
4
{
"presets": [],
"plugins": []
}

presets를 추가하기 위해서는 npm 설치가 필요합니다.

1
$ npm install --save-dev babel-preset-es2015

만약 React code를 transpile해야 한다면 다음과 같이 설치해줍니다.

1
$ npm install --save-dev babel-preset-react

그리고 .babelrc파일을 수정해줍니다.

.babelrc
1
2
3
4
{
"presets": ["es2015", "react"],
"plugins": []
}

자바스크립트 스펙으로 아직 확정되지 않은 proposal 스펙들이 존재합니다. 이들은 5개의 stage로 구분됩니다. babel에서는 각각의 stage에 대해서 preset을 제공합니다.

  • babel-preset-stage-0
  • babel-preset-stage-1
  • babel-preset-stage-2
  • babel-preset-stage-3

babel-preset-stage-4는 babel-preset-es2015를 의미합니다. 각각의 stage에 대해서도 위와 같은 방법으로 설치하고 .babelrc파일을 수정하여 사용할 수 있습니다. 하지만 babel에서 이들을 모두 한번에 사용할 수 있도록 해주는 preset을 하나 제공했는데요, 바로 babel-preset-env입니다. 이 preset으로 모든 stage를 대체할 수 있습니다.

babel-polyfill

babel-polyfill은 ES6 환경을 제공해줍니다.
polyfill이 없는 경우를 예제를 통해 살펴봅니다.

ES6
1
2
function allAdd() {
return Array.from(arguments).map(a => a + 2);

위 코드는 babel에 의해 다음과 같이 transpile됩니다.

ES5
1
2
3
4
5
function allAdd() {
return Array.from(argument).map(function(a) {
return a + 2;
});
}

Array.from()은 ES6 syntax이므로 지원하지 않는 브라우저가 존재하기 때문에 위 코드는 transpile은 되었지만 모든 브라우저에서 작동하지 않습니다. 이 문제를 해결하기 위해서 polyfill을 사용해야 합니다. polyfill이란 code 조각으로 런타임에 존재하지 않는 native API의 복사본을 말합니다.

babel-polyfill 사용을 위해서 다음과 같이 npm을 설치해줍니다.

1
$ npm install --save-dev babel-polyfill

그리고 해당 polyfill이 필요한 곳에서 import해줍니다.

1
import 'babel-polyfill';

만약 webpack과 함께 사용한다면 entry point에 babel-polyfill을 추가해줍니다.

webpack.config.js
1
2
3
4
module.exports = {
entry: ['babel-polyfill', '...']
...
};

babel-plugins

아직 공식 스펙에서 지원하지 않은 기능들을 transform-plugin을 추가하여 사용할 수 있습니다. 여러 플러그인은 babel 공식 홈페이지에서 확인실 수 있습니다. 추가로 설치한 플러그인은 plugins 옵션으로 추가할 수 있습니다. .babelrc파일에서 설정해줄 수도 있고 Webpack이란 도구에서도 설정해줄 수 있습니다.

주로 Webpack이라는 도구와 함께 사용하는 babel에 대해서 알아봤는데요, webpack과 함께 사용하는 것에 대해서는 Webpack2 입문 가이드 포스팅에 자세히 나와있습니다 :)

end

Reference


'C Lang > JS Program Diary' 카테고리의 다른 글

Babel이란?  (0) 2020.02.18
New features of ES6, ES7, ES8, ES9 and ES10  (0) 2020.02.12
JavaScript 버전별 표준  (1) 2019.09.06
javascript program diary  (0) 2018.05.13

https://medium.com/@ljs0705/babel-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-a1d0e6bd021a
 
react로 개발을 하고 있다면 거의 대부분 babel도 함께 사용하고 있을 것이다.create-react-app에도 기본으로 들어있고 기타 react 관련된 튜토리얼을 따라 하다 보면 자연스럽게 babel을 설치하게 된다. 굳이 react가 아니더라도 자바스크립트를 많이 사용한다면 babel을 함께 사용할 것을 추천한다.

babel은 자바스크립트 컴파일러다. 입력은 자바스크립트 코드고 출력도 자바스크립트 코드다. 최신 버전의 자바스크립트 문법은 브라우저가 이해하지 못하기 때문에 babel이 브라우저가 이해할 수 있는 문법으로 변환해준다. ES6, ES7 등의 최신 문법을 사용해서 코딩을 할 수 있기 때문에 생산성이 향상된다.

ES6 문법

특히 ES6에는 상당히유용한 문법이 많다. 간단히 몇 가지만 살펴보자

arrow function

arrow function을 사용하면 함수 선언이 간단해진다. arrow function의 this는 자신을 포함하는 가장 근접한 일반 함수의 this를 참조한다. 따라서 arrow function의 this는 arrow function이 호출되는 시점과는 무관하게 선언되는 시점에 결정된다. 개인적으로 이 점이 상당히 편리해서 잘 쓰고 있다.

dataList.forEach(item => console.log(item.value))

classes

생성자, 상속 등의 클래스 기능을 사용할 수 있다.

enhanced object literals

object 생성이 편리해졌다.

// 예전 방식  
const obj = {  
foo: foo,  
bar: 42  
}  
// es6  
const obj = {  
foo,  
bar: 42  
}

template strings

여러 개의 문자열을 +로 붙이는 작업은 상당히 성가시다. 아래의 방식으로 자연스럽게 표현이 가능해졌다.

const msg = `Hello ${name}, current time is ${time}`

destructuring

object에서 필요한 부분만 뽑아낼 때 편리하다. 특히 함수에서 매개변수의 일부만 입력으로 받고 싶을때 유용하다.

const {foo} = obj;  
function savePerson({name, age, onSuccess, onFail}) {...}savePerson({name: "berry", onSuccess: () => alert("person saved")})

let & const

수정 가능한 변수는 let, 수정 불가능한 변수는 const를 사용한다. let은 var와 유사하지만 block-scope라는 차이점이 있다. var는 block-scope가 아니라서 실수하기가 쉽다. 또한 let과 const를 사용하면 코드가 좀 더 읽기 쉬워진다.

babel-polyfill

babel을 사용한다고 자바스크립트 최신 함수를 사용할 수 있는 건 아니다. 초기에 babel만 믿고 최신 함수를 사용했다가 브라우저에서 동작하지 않는 것을 보고 당황했었다. babel은 문법을 변환해주는 역할만 할 뿐이다. polyfill은 프로그램이 처음에 시작될 때 현재 브라우저에서 지원하지 않는 함수를 검사해서 각 object의 prototype에 붙여주는 역할을 한다. 즉, babel은 컴파일-타임에 실행되고 babel-polyfill은 런-타임에 실행된다.

babel-polyfill을 사용하고 싶다면 별도로설정해줘야 한다.

.babelrc

.babelrc파일을 프로젝트 root 폴더에 생성하자. plugins와 presets 속성이 중요하다. 위에서 설명했던 각 문법이 하나의 plugin이라고 생각하면 된다. 그리고 preset은 plugin 여러 개가 묶여있는 개념이다. 대표적으로 ES6 문법을 모아놓은 es2015 preset과 react 문법을 모아놓은 react preset이 있다. 사용할 preset을 presets에 추가하고 presets에 속해있는 plugin 외에 추가로 사용하고 싶은 plugin은 plugins에 넣자.

babel-cli

보통은 webpack을 사용해서 빌드하겠지만 직접 cli로 빌드할 수도 있다. 자세한 사용법은여기를 참고하자.

여기에서 babel의 출력을 테스트해볼 수 있다. 아래의 코드를 입력해보자.

const test1 = (a, b) => a + b

Arrow function

사용법

//기존의 function sample
var double = function(x){
    return x*2;
}
//arrow function sample
const double = (x) => {
    return x*2;
}
//매개변수가 1개인 경우 소괄호 생략 가능
const double = x => {return x*2}
//함수가 한줄로 표현가능하면 중괄호 생략 가능하고 자동으로 return됨
const double = x => x * 2
//매개변수가 두 개 이상일 때
const add = (x,y) => x + y
//매개변수가 없을 때
() => {    ... }
//객체변환할 때 중괄호 사용
() => {return {a:1};}
//return 바로할땐 소괄호 사용
() => ({a:1})

function 키워드 대신 화살표(=>)를 사용하여 함수를 선언할 수 있다.

  • 미리 말하자면 arrow function이 기존의 function을 완전히 대신할 수 없다.
  • 콜백 함수에서 사용하면 아주 간결하게 표현이 가능한 장점이 있다.

함수 호출

//const pow = (x) => {return x * x;}
const pow = x => x * x;
console.log(pow(10)); // 100
//콜백에 arrow function 사용해서 간략화 시키기
const arr = [1,2,3];
const pow = arr.map(x => x * x);
console.log(pow);

변경점

  1. 매개변수에 default 값 적용 가능
const f1 = (msg ='jeongpro') => {
    alert(msg);
}
f1(); // 'jeongpro'

기존에는 매개변수를 전달하지 않으면 undefined가 나오지만 ES6에서는 실수로 매개변수를 전달하지 않았을 때 처리를 할 수 있다.

  1. arguments 대신 args 사용
var foo = function(){
    console.log(arguments);
}
foo(1,2); // { '0':1, '1':2 }

매개변수를 지정하지 않아도 arguments 라는 프로퍼티가 함수에 자동으로 생성되어 사용가능 했었으나 화살표 함수에는 arguments가 없어짐. 대신 args가 생겼다.

// ES5
function sum(){
  var arr = Array.prototype.slice.call(arguments);
  return arr.reduce(function(pre, cur){
    return pre + cur;
  });
}
console.log(sum(1,2,3,4));

// ES6
const sum1 = (...args) => {
  return args.reduce((pre, cur) => pre + cur);
}
console.log(sum1(1,2,3,4));

매개변수부분에 rest 파라미터(...)를 사용하여 가변인자 함수 내부에 배열로 전달할 수 있음.

  1. this 변경점 (핵심)
  • arrow function은 자신을 포함하는 외부 scope에서 this를 계승받는다.
    즉, arrow function은 자신만의 this를 생성하지 않는다. (Lexical this)
function Prefixer(prefix) {
  this.prefix = prefix;
}

Prefixer.prototype.prefixArray = function (arr) {
  console.log(this.prefix);// (A)
  return arr.map(function (x) {
    return this.prefix + ' ' + x; // (B)
  });
};

var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim']));

this.prefix가 (A)에서는 'Hi' 를 나타내지만 (B)에서는 undefined가 찍힌다.
지난 포스트에서 자바스크립트의 this에서 봤듯이 내부 함수호출에서는 this가 window객체였다.

function Prefixer(prefix) {
  this.prefix = prefix;
}

Prefixer.prototype.prefixArray = function (arr) {
  return arr.map(x => `${this.prefix}  ${x}`);
};

const pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim']));

arrow function에서는 내부 함수여도 this가 arrow function이 선언된 곳에서 상위의 context를 가리키므로 Prefixer객체 pre를 가리키므로 this.prefix가 Hi를 가리켜 개발자가 예상한대로 작동한다.

arrow function을 사용하면 안되는 경우1

const obj1 = {
  name : 'Lee',
  sayName : () => {console.log(`hi + ${this.name}`);}
};
obj1.sayName();
Colored by Color Scripter

위와 같이하면 객체의 this를 바인딩하지 않고 전역 객체가 바인딩된다.

즉, 객체의 메소드를 정의할 때는 사용하면 안된다.

const obj = {
  name: 'Lee',
  sayHi() { // === sayHi: function() {
    console.log(`Hi ${this.name}`);
  }
};

obj.sayHi(); // Hi Lee

위 처럼 ES6에서 사용하는 객체의 메소드 정의방법으로 정의하는 것이 옳다. 마찬가지로 prototype에 메소드를 할당할 때도 똑같이 화살표함수를 사용하면 안된다.

일반 함수 function(){ } 을 이용하도록 한다.

arrow function을 사용하면 안되는 경우2

arrow function을 생성자 함수로 사용할 수 없음.

arrow function을 사용하면 안되는 경우3

addEventListener 함수의 콜백 함수에서 사용하면 this가 상위 컨텍스트를 가리킴

var button = document.getElementById('myButton');  
button.addEventListener('click', () => {  
  console.log(this === window); // => true
  this.innerHTML = 'Clicked button';
});

var button = document.getElementById('myButton');  
button.addEventListener('click', function() {  
  console.log(this === button); // => true
  this.innerHTML = 'Clicked button';
});
Colored by Color Scripter

https://programming.vip/docs/new-features-of-es6-es7-es8-es9-and-es10.html

New features of ES6, ES7, ES8, ES9 and ES10

1. New features of ES6 (2015)

  • class
  • Modularization
  • Arrow function
  • Function parameter defaults
  • Template string
  • Destructuring assignment
  • Extension operator
  • Object attribute shorthand
  • Promise
  • Let and Const

1.1 module

ES5 does not support native modularity, and modules are added as an important part of ES6. The function of the module is mainly composed of export and import. Each module has its own scope. The mutual calling relationship between modules is to specify the exposed interfaces of modules through export, and to reference the interfaces provided by other modules through import. At the same time, it also creates a namespace for the module to prevent function naming conflicts.

1.1.1 export


//Derived variables
export var name = 'Rainbow'
 
var name = 'Rainbow';
var age = '24';
export {name, age};
 
 
//Derived constant
export const sqrt = Math.sqrt;
 
//derived function
export function myModule(someArg) {
  return someArg;

1.1.2 import

import {myModule} from 'myModule';//Structure assignment is used
import {name,age} from 'test';
 
//An import statement can import default functions and other variables at the same time.
import defaultMethod, { otherMethod } from 'xxx.js';

1.2 Arrow function

This is one of the most exciting features of ES6. =>It's not just a shorthand for the keyword function, it brings other benefits. Arrow function shares the same this with the surrounding code, which can help you to solve the problem of this pointing. Experienced JavaScript developers are familiar with patterns such as var self = this; or var that = this that refer to peripheral this. But with = > this mode is not needed.

1.2.1 structure of arrow function

Arrow function's arrow = > before is an empty bracket, a single parameter name, or multiple parameter names enclosed in brackets, and after the arrow can be an expression (as the return value of the function), or the function body enclosed in curly brackets (you need to return the value by yourself, otherwise it is undefined).

// Examples of arrow functions
()=>1
v=>v+1
(a,b)=>a+b
()=>{
    alert("foo");
}
e=>{
    if (e == 0){
        return 0;
    }
    return 1000/e;

The original said:

"Whether it's an arrow function or a bind function, it returns a new function reference every time it's executed, so if you need a function reference to do something else (such as uninstalling the listener), you have to save the reference yourself."

//Wrong practice
class PauseMenu extends React.Component{
    componentWillMount(){
        AppStateIOS.addEventListener('change', this.onAppPaused.bind(this));
    }
    componentWillUnmount(){
        AppStateIOS.removeEventListener('change', this.onAppPaused.bind(this));
    }
    onAppPaused(event){
    }
}
 
//Correct approach
class PauseMenu extends React.Component{
    constructor(props){
        super(props);
        this._onAppPaused = this.onAppPaused.bind(this);
    }
    componentWillMount(){
        AppStateIOS.addEventListener('change', this._onAppPaused);
    }
    componentWillUnmount(){
        AppStateIOS.removeEventListener('change', this._onAppPaused);
    }
    onAppPaused(event){
    }
}
 
//The right way to simplify
class PauseMenu extends React.Component{
    componentWillMount(){
        AppStateIOS.addEventListener('change', this.onAppPaused);
    }
    componentWillUnmount(){
        AppStateIOS.removeEventListener('change', this.onAppPaused);
    }
    onAppPaused = (event) => {
        //Define the function directly as an arrow function attribute, and bind this pointer when initializing
    }
}
 
 
//It should be noted that no matter bind or arrow function is executed, a new function reference is returned every time,
//So if you need a function reference to do something else (such as uninstalling the listener), you have to save the reference yourself.

1.3 default parameters

const test = (a='a',b='b',c='c')=>{
    return a+b+c
}
 
console.log(test('A','B','C')) //ABC
console.log(test('A','B'))     //ABc
console.log(test('A'))         //Abc

1.4 template string

//Do not use template string:
 
var name = 'Your name is ' + first + ' ' + last + '.'
 
//Use template string:
 
var name = `Your name is ${first} ${last}.`

1.5. Structure assignment

1.5.1 structure assignment of array


var foo = ["one", "two", "three", "four"];
 
var [one, two, three] = foo;
console.log(one); // "one"
console.log(two); // "two"
console.log(three); // "three"
 
//If you want to ignore some values, you can get the values you want as follows
var [first, , , last] = foo;
console.log(first); // "one"
console.log(last); // "four"
 
//You can write like this
var a, b; //Declare variables first
 
[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2

Use structure assignment to exchange the values of two variables.

var a = 1;
var b = 3;
 
[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1

1.5.2 structure assignment of objects

const student = {
  name:'Ming',
  age:'18',
  city:'Shanghai'  
};
 
const {name,age,city} = student;
console.log(name); // "Ming"
console.log(age); // "18"
console.log(city); // "Shanghai"

1.6 spread operator

Expand an array or object.

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
var arr3 = [...arr1, ...arr2];// Append all elements in arr2 after arr1 and return
//Equate to
var arr4 = arr1.concat(arr2);
 
 
 
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
 
var clonedObj = { ...obj1 };
// Cloned object: {foo: 'bar', X: 42}
 
var mergedObj = { ...obj1, ...obj2 };
// Merged object: {foo: 'Baz', X: 42, Y: 13}

Application in react

const params = {
    name: 'Jine',
    age: 21
}
<CustomComponent {...params} />
 
 
var params = {
    name: '123',
    title: '456',
    type: 'aaa'
}
 
var { type, ...other } = params;
 
<CustomComponent type='normal' number={2} {...other} />
//Equate to
<CustomComponent type='normal' number={2} name='123' title='456' />

1.7Promise


var test = (a,b)=>{
    return new Promise((reslove,reject)=>{
        //Asynchronous operation
        //...
        
        reslove(resoult)//Return correct results
        
        //...
        reject(err)    //Results on error
    })
}
 
 
//Use
 
test(a,b).then(res=>{
    //... promise reslove() returns the correct result and executes here
}).catch(err=>{
    //After the previous reject (err), the code will execute into
})
 
//perhaps
 
try{
    var resoult = await test(a,b)
    //...
}catch(er){
    //...
}

2 new features of ES7 (2016)

  • The array includes() method is used to determine whether an array contains a specified value. If it does, it returns true, otherwise it returns false.
  • a ** b index operator, which is the same as Math.pow(a, b).

    2.1 includes()

The includes() function is used to determine whether an array contains a specified value. If it does, it returns true. Otherwise, it returns false.

let arr = ['react', 'angular', 'vue'];
 
if (arr.includes('react'))
{
    console.log('react existence');
}

2.2 index operator**

In ES7, the exponential operator is introduced, and the result is equivalent to Math.pow(..)

console.log(2**10);// Output 1024
console.log(Math.pow(2, 10)) // Output 1024

3 new features of es8 (2017)

  • async/await
  • Object.values()
  • Object.entries()
  • String padding: padStart() and padEnd(), the padding string reaches the current length
  • Comma is allowed at the end of function parameter list
  • Object.getOwnPropertyDescriptors()
  • ShareArrayBuffer and Atomics objects for reading and writing from shared memory locations

    3.1 async/await

  async function init() {
    console.log('start')
    await this.testSync()
    console.log('End')
  }
  this.init()
  async function testSync() {
    const response = await new Promise(resolve => {
      setTimeout(() => {
          resolve("async await test...");
        }, 1000);
    });
    console.log(response);
  }

3.2 Object.keys()

var obj = { foo: "bar", baz: 42 };
Object.keys(obj)
// ["foo", "baz"]

3.3 Object.values()

var obj = { foo: "bar", baz: 42 };
Object.values(obj)
// ["bar", 42]

3.4 Object.entries()

The Object.entries method returns an array of key value pairs of all enumerable properties of the parameter object itself (excluding inheritance).

var obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]


const obj1 = {a: 1, b: 2, c: 3}
for(let [key,value] of Object.entries(obj1)){
    console.log(`key: ${key} value:${value}`)
}
//key:a value:1
//key:b value:2
//key:c value:3

One use of the Object.entries method is to turn an object into a real Map structure.

var obj = { foo: 'bar', baz: 42 };
var map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }

Object object keys(), values() entries()

3.5 String padding

String.padStart(targetLength,[padString])

String.padEnd(targetLength,padString])

  • targetLength: the target length to which the current string needs to be populated. If the value is less than the length of the current string, the current string itself is returned.
  • padString: (optional) fills the string. If the string is too long, so that the length of the filled string exceeds the target length, only the leftmost part will be retained, and the rest will be truncated. The default value of this parameter is' '.

console.log('0.0'.padStart(4,'*'))
console.log('0.0'.padStart(20))
console.log('0.0'.padEnd(4,'*')) 
console.log('0.0'.padEnd(10,'*'))
 
/*
*0.0
                 0.0
0.0*
0.0*******
*/

3.6 Object.getOwnPropertyDescriptors()

The Object.getOwnPropertyDescriptor method returns an object (descriptor). ES6 introduces the Object.getOwnPropertyDescriptors method, which returns the description object of all its own properties (non inherited properties) of the specified object.

const obj = {
  foo: 123,
  get bar() { return 'abc' }
};
 
Object.getOwnPropertyDescriptors(obj)
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

In the above code, the Object.getOwnProperDescriptors method returns an object. The property names of all the original objects are the property names of the object, and the corresponding property values are the property description objects.
The main purpose of this method is to solve the problem that Object.assign() can't copy get and set attributes correctly.

const source = {
  set foo(value) {
    console.log(value);
  }
};
 
const target1 = {};
Object.assign(target1, source);
 
Object.getOwnPropertyDescriptor(target1, 'foo')
// { value: undefined,
//   writable: true,
//   enumerable: true,
//   configurable: true }

In the above code, the value of the foo attribute of the source object is an assignment function. The Object.assign method copies this attribute to the target1 object. As a result, the value of this attribute becomes undefined. This is because the object. Assign method always copies the value of an attribute, not the assignment method or value method behind it.
ES6 object extension - Object.getOwnPropertyDescriptors()

3.7 SharedArrayBuffer object

With SharedArrayBuffer, multiple web workers can read and write the same block of memory at the same time. You no longer need postMessage communication with delay. Multiple web workers have no delay in data access

The SharedArrayBuffer object is used to represent a general, fixed length raw binary data buffer, similar to the ArrayBuffer object, which can be used to create views on shared memory. Unlike ArrayBuffer, SharedArrayBuffer cannot be separated.

/**
 * 
 * @param {*} length The size of the array buffer created, in bytes.  
 * @returns {SharedArrayBuffer} A new SharedArrayBuffer object of the specified size. Its contents are initialized to 0.
 */
new SharedArrayBuffer(length)

Illustration of ArrayBuffers and shared ArrayBuffers

3.8 Atomics objects

The Atomics object provides a set of static methods for atomic manipulation of the SharedArrayBuffer object.

These atomic operations belong to the Atomics module. Unlike general global objects, Atomics is not a constructor, so it cannot be called with the new operator or directly as a function. All properties and methods of Atomics are static (like the Math object).

Multiple threads sharing memory can read and write data at the same location at the same time. Atomic operation ensures that the value of the data being read or written meets the expectation, that is, the next atomic operation will not start until the end of the previous atomic operation, and its operation process will not be interrupted.

Atomics.add()
//Adds an array element at the specified location to the given value and returns the value of the element before it is added.
 
 
Atomics.and()
//Compares the array element at the specified location with the given value, and returns the value of the element before the operation.
 
 
Atomics.compareExchange()
//If the element specified in the array is equal to the given value, it is updated to the new value and the original value of the element is returned.
 
 
Atomics.exchange()
//Updates the element specified in the array to the given value and returns the value before the element is updated.
 
 
Atomics.load()
//Returns the value of the specified element in the array.
 
 
Atomics.or()
//Compares the array element at the specified location with the given value, and returns the value of the element before the or operation.
 
 
Atomics.store()
//Sets the element specified in the array to the given value and returns the value.
 
 
Atomics.sub()
//Subtracts an array element at the specified location from the given value and returns the value of the element before subtraction.
 
 
Atomics.xor()
//The array element at the specified location is different from the given value or, and the value of the element before the exclusive or operation is returned.
 
//The wait() and wake() methods use the futexes model on Linux (fast user space mutex,
//Fast user space mutex), which allows processes to wait until a specific condition is true, is mainly used to implement blocking.
 
Atomics.wait()
//Checks whether the value at a specified location in the array is still the given value, and if it is, holds until it wakes up or times out.
//The return value is "ok", "not equal" or "time out". When called, an exception is thrown if the current thread does not allow blocking
//Most browsers do not allow wait() to be called in the main thread.
 
 
Atomics.wake()
//Wakes up the thread in the waiting queue waiting on the element at the specified position in the array. The return value is the number of threads successfully woken.
 
 
Atomics.isLockFree(size)
//It can be used to detect whether the current system supports hardware level atomic operation. For arrays of a specified size, if the current system supports hardware level atomic operations,
//Returns true; otherwise, it means that for the array, each atomic operation in the Atomics object can only be implemented with a lock. This function is for technical experts.

4 new features of es9 (2018)

  • Asynchronous iterator
  • Template string for non escape sequence
  • Regular expression reverse (look behind) assertion (this article)
  • Regular expression Unicode escape
  • Regular expression s/dotAll mode
  • Regular expression named capture group
  • Object expansion operator
  • Promise.prototype.finally

4.1 asynchronous iterator

ES2018 introduces asynchronous iterators, just like regular iterators, except that the next() method returns a Promise. Therefore, await can be used with the for...of loop to run asynchronous operations in a serial manner. For example:

async function process(array) {
  for await (let i of array) {
    doSomething(i);
  }
}

4.2 template string of non escape sequence

Tags allow you to parse Template Strings with functions. The first parameter of the tag function contains an array of string values. The rest of the parameters are expression dependent. Finally, your function can return the processed string (or it can return something completely different).

function foo(str) {
    return str[0].toUpperCase();
}

foo`justjavac`; // Output JUSTJAVAC
foo`Xyz`; // Output XYZ

New features of ES2018: template string of non escape sequence

4.3 regular expression backward assertion (this article)

Assertion is a test of characters before or after the current matching position. It does not actually consume any characters, so assertion is also called "non consuming matching" or "non obtaining matching".

There are four forms of regular expression assertions:

  • (? = pattern) zero width positive lookahead assertion
  • (?! pattern) zero width negative lookahead assertion
  • (? < pattern) zero width positive look behind assertion
  • (? <! Pattern) zero width negative look behind assertion

The pattern in this is a regular expression.

lookahead and lookbehind are translated into:

  • Positive negative
  • Forward backward
  • Forward and reverse
  • Forward looking and backward looking
  • ......

This document uses forward and reverse.
Look behind assertion

4.4 regular expression Unicode escape

Generally speaking, the number character interpretation [0-9], the word character is [0-9a-zA-Z], and the blank character includes spaces, carriage returns and other characters, but this is the case in ASCII encoding, not in Unicode encoding.

                                    . Therefore, if you specify that regular expressions use Unicode mode in Python 2 (the simplest way to interpret is to specify the pattern modifier (? u) at the beginning of regular expressions), \ d \ w \ s can match all corner numbers, Chinese characters, and all corner spaces. In this case, it is called the Unicode matching rule in this book; correspondingly, the matching in the previous ASCII encoding is called the ASCII matching rule.
Regular expression -- Unicode matching rule

4.5 regular expression s/dotAll mode

Regular expression s/dotAll mode

4.6 regular expression named capture group

const
  reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
  match  = reDate.exec('2018-04-30'),
  year   = match.groups.year,  // 2018
  month  = match.groups.month, // 04
  day    = match.groups.day;   // 30
 
const
  reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
  d      = '2018-04-30',
  usDate = d.replace(reDate, '$<month>-$<day>-$<year>');

4.7 object expansion operator

let a = [1,2,3];
let b = [0, ...a, 4]; // [0,1,2,3,4]
 
let obj = { a: 1, b: 2 };
let obj2 = { ...obj, c: 3 }; // { a:1, b:2, c:3 }
let obj3 = { ...obj, a: 3 }; // { a:3, b:2 }
let object = {
  a: '01', b: '02'
};
 
let newObject = {
  c: '03',
  ...object
};
 
console.log(newObject); //{c: "03", a: "01", b: "02"}

4.8 Promise.finally()

A Promise call chain either successfully reaches the last. then(), or fails to trigger. catch(). In some cases, you want to run the same code no matter whether Promise runs successfully or fails, such as clearing, deleting conversations, closing database connections, etc.

function doSomething() {
  doSomething1()
  .then(doSomething2)
  .then(doSomething3)
  .catch(err => {
    console.log(err);
  })
  .finally(() => {
    // finish here!
  });
}

5 new features of ES10 (2019)

  • Added the flat() method and flatMap() method of Array
  • Added trimStart() method and trimEnd() method of String
  • Object.fromEntries()
  • Symbol.prototype.description
  • String.prototype.matchAll
  • Function.prototype.toString() now returns exact characters, including spaces and comments
  • JSON⊂ECMAScript
  • Simplify try {} catch {} and modify the catch binding
  • New basic data type BigInt
  • globalThis
  • import()
  • Legacy RegEx
  • Private instance methods and accessors

5.1 add the flat() method and flatMap() method of Array

flat() and flatMap() are essentially operations of reduce and concat.
The flat() method recursively traverses the array at a specified depth, and merges all elements and elements in the traversed sub array into a new array to return.

  • The basic function of flat() method is to reduce the dimension of array
  • Secondly, we can use the characteristics of the flat() method to remove the empty items of the array

5.1.1 flat()

var arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]
 
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]
 
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
 
//Using Infinity as the depth, expand the nested array of any depth
arr3.flat(Infinity); 
// [1, 2, 3, 4, 5, 6]
 
 
//Remove null items
var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]

5.1.2 flatMap()

var arr1 = [1, 2, 3, 4];

arr1.map(x => [x * 2]); 
// [[2], [4], [6], [8]]

arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]

// Only "flatten" the array returned by the function in flatMap
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]

5.2 added trimStart() method and trimEnd() method of String

String.trimStart() can be used to remove whitespace from the beginning of a string. String.trimEnd() can be used to remove whitespace from the end of a string.

let  greeting =  "    Hello World";
console.log(greeting.trimStart());// "Hello World"

let greeting = "Hello World    ";
console.log(greeting.trimEnd());// "Hello World"

5.3 Object.fromEntries()

The function of the Object.entries() method is to return an array of key value pairs of enumerable properties of a given object, which is arranged in the same order as when the for...in loop is used to traverse the object (the difference is that the for in loop also enumerates properties in the prototype chain).

Object.fromEntries() is the inversion of Object.entries().

The Object.fromEntries() function passes in a list of key value pairs and returns a new object with these key value pairs. This iteration parameter should be an object that can implement the @ iterator method and return an iterator object. It generates an array like object with two elements, the first being the value to be used as the attribute key, and the second being the value associated with the attribute key.

  • Through Object.fromEntries, Map can be converted to Object:
const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }
  • Through Object.fromEntries, you can convert Array to Object:
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }

5.4 Symbol.prototype.description

Read only description property, which is a string that returns an optional description of a Symbol object.

let mySymbol = `My Symbol`;

let symObj = Symbol(mySymbol);

console.log(symObj) // Symbol(mySymbol);

console.log(String(symObj) === `Symbol(${mySymbol})`); // true

console.log(symObj.description); // "My Symbol"

5.5 String.protype.matchAll()

The matchAll() method returns an iterator of all the results of matching a string with a regular expression, including the capture group.

const string = 'Hexadecimal number:DEADBEEF CAFE'
const regex = '\b\p{ASCII_Hex_digit}+\b/gu'

for(const match of string.match(regex)) {
  console.log(Math)
}
/*
  output
  DEADBEEF
  CAFE
*/
// 2.string.matchAll gives more detailed information about each match
const string = 'Hexadecimal number:DEADBEEF CAFE'
const regex = '\b\p{ASCII_Hex_digit}+\b/gu'

for(const match of string.matchAll(regex)) {
  console.log(Math)
}
/*
 output
 ["DEADBEEF", index: 8, input: "Hexadecimal number: DEADBEEF CAFE", groups: undefind]
 ["CAFE", index: 17, input: "Hexadecimal number: DEADBEEF CAFE", groups: undefind] 
*/

5.6 Function.prototype.toString() now returns exact characters, including spaces and comments

The toString() method returns a string representing the source code of the function. In ES6, when toString is called on a function, it returns the string representation of the function according to the ECMAScript engine. If possible, it will return the source code, otherwise - a standardized placeholder.

this.fruits = []
function buyFruits(fruit) {
  this.fruits = [...this.fruits, fruit]
}
console.log(buyFruits.toString())

/*
function buyFruits(fruit) {
  this.fruits = [...this.fruits, fruit]
}
*/

5.7 JSON⊂ECMAScript

In versions prior to ES10, non escaped line separator U+2028 and paragraph separator U+2029 are not accepted.

U+2028 is the paragraph separator.
U+2029 is the line separator.

let LS = ""
const PS = eval("'\u2029'")

5.8 simplify try {} catch {} and modify catch binding

The optional catch binding allows developers to use try/catch in catch blocks without using the error parameter.

// Use before ES2019
try {
  // some code
}catch (err) {
  // error handling code
}

// Now use try / catch like ES2019:
try  {
  // some code
}catch {
  // error handling code
}

5.9 new basic data type BigInt

BigInt is the seventh primitive type, which is an integer of arbitrary precision. Not just the maximum at 900719925474092.

13 new features of ES10

Keywords: Javascript Attribute React ascii encoding

'C Lang > JS Program Diary' 카테고리의 다른 글

바벨에 필요한 모든 것 : 실행방법, 주요 함수, 사용법  (0) 2020.02.18
Babel이란?  (0) 2020.02.18
JavaScript 버전별 표준  (1) 2019.09.06
javascript program diary  (0) 2018.05.13

+ Recent posts