서두에
ES2020(즉 ES11) 는 지난주 (2020 년 6 월) 에 정식으로 릴리스되었으며, 그 이전에 Stage 4 에 들어간 10 건의 제안은 모두 사양에 포함되었고, JavaScript 언어의 새 특성이 되었습니다
一.특성 일람
ES Module 은 몇 가지 강화를 맞이했습니다:
-
import(): 동적 모듈 식별자로 모듈을 비동기로 도입할 수 있는 구문
-
import.meta: 모듈 관련 메타 정보를 운반하기 위한 오브젝트
-
export * as ns from "mod";: 새로운 집약 수출 구문
안전한 체인 조작을 정식으로 서포트:
-
Optional chaining: 새 연산자
?.는 속성 액세스, 메서드 호출 전에 그 존재를 체크 가능 -
Nullish coalescing Operator: 디폴트 값을 제공하기 위한 새 연산자
??
큰 수치 연산의 네이티브 서포트를 제공:
- BigInt – arbitrary precision integers: 임의 정밀도의 정수 연산을 서포트하는 새로운 기초 수치 타입
몇 가지 기초 API 도 새로운 변화가 있었습니다:
-
Promise.allSettled: 새로운 Promise 콤바이너.
all,race와 같은 쇼트컷 특성을 갖지 않음 -
String.prototype.matchAll: 이터레이터 형식으로 글로벌 매치 모드 하의 정규 표현식 매치 결과 (
index,groups등) 를 모두 반환 -
globalThis: 글로벌 스코프
this에 액세스하는 통용 방법 -
for-in mechanics:
for-in루프의 특정 동작을 규범
二.ES Module 강화
동적 import
[ES Module](/articles/module-es6 노트 13/) 은정적인 모듈 시스템이라는 것을 알고 있습니다:
The existing syntactic forms for importing modules are static declarations.
정적인 것은:
They accept a string literal as the module specifier, and introduce bindings into the local scope via a pre-runtime "linking" process.
-
정적 로드:
import/export선언은 탑레벨 스코프에만 출현 가능.按需 로드, 지연 로드를 서포트하지 않음 -
정적 식별자: 모듈 식별자는 문자열 리터럴만.런타임에서 동적으로 계산된 모듈 이름을 서포트하지 않음
예를 들어:
if (Math.random()) {
import 'foo'; // SyntaxError
}
// You can't even nest `import` and `export`
// inside a simple block:
{
import 'foo'; // SyntaxError
}
이러한 엄격한 정적 모듈 메커니즘은 소스 코드 기반의 정적 분석, 컴파일 최적화에 더 큰 발휘 공간을 줍니다:
This is a great design for the 90% case, and supports important use cases such as static analysis, bundling tools, and tree shaking.
그러나 다른 몇 가지 씬에는 그다지 우호적이지 않습니다. 예를 들어:
-
첫 스크린 퍼포먼스를 苛求하는 씬:
import선언으로 인용된 모든 모듈 (초기화 시에 일시적으로 사용하지 않는 모듈을 포함) 은 모두 초기화 단계에서 사전 로드되어, 첫 스크린 퍼포먼스에 영향 -
목표 모듈 식별자를 사전에 확정하기 어려운 씬: 예를 들어 사용자의 언어 옵션에 따라 다른 모듈을 동적으로 로드 (
module-en,module-zh등) -
특수한 상황에서만 특정 모듈을 로드할 필요가 있는 ���: 예를 들어 이상 상황에서 다운그레이드 모듈을 로드
이러한 동적으로 모듈을 로드할 필요가 있는 씬을 만족시키기 위해, ES2020 은 동적 import 특성 (import()) 을推出했습니다:
import(specifier)
import()「함수」는 모듈 식별자 specifier(그 해석 룰은 import 선언과 같음) 를 입력하고, Promise 를 출력. 예를 들어:
// 목표 모듈 ./lib/my-math.js
function times(a, b) {
return a * b;
}
export function square(x) {
return times(x, x);
}
export const LIGHTSPEED = 299792458;
// 현재의 모듈 index.js
const dir = './lib/';
const moduleSpecifier = dir + 'my-math.mjs';
async function loadConstant() {
const myMath = await import(moduleSpecifier);
const result = myMath.LIGHTSPEED;
assert.equal(result, 299792458);
return result;
}
// 또는 async & await 를 사용하지 않음
function loadConstant() {
return import(moduleSpecifier)
.then(myMath => {
const result = myMath.LIGHTSPEED;
assert.equal(result, 299792458);
return result;
});
}
import 선언과 비교하여, import() 의 특징은 다음과 같습니다:
-
함수, 브랜치 등의 비탑레벨 스코프에서 사용 가능.按需 로드, 지연 로드는 문제가 아님
-
모듈 식별자는 변수传入을 서포트. 동적으로 계산하여 모듈 식별자를 확정 가능
-
module에 한정되지 않고,普通のscript중에서도 사용 가능
주의, 함수처럼 보이지만, import() 은 실제로는 연산자입니다. 왜냐하면 연산자는 현재의 모듈 관련 정보를 운반할 수 있기 때문 (모듈 표시를 해석하기 위해). 그러나 함수는 할 수 없음:
Even though it works much like a function,
import()is an operator: in order to resolve module specifiers relatively to the current module, it needs to know from which module it is invoked. A normal function cannot receive this information as implicitly as an operator can. It would need, for example, a parameter.
import.meta
또 하나의 ES Module 새 특성은 import.meta 로, 모듈 특정의 메타 정보를 透出하기 위해 사용:
import.meta, a host-populated object available in Modules that may contain contextual information about the Module.
예를 들어:
-
모듈의 URL 또는 파일 이름: 예를 들어 Node.js 중의
__dirname,__filename -
所在하는
script태그: 예를 들어 브라우저가 서포트하는document.currentScript -
엔트리 모듈: 예를 들어 Node.js 중의
process.mainModule
이러한 메타 정보는 모두 import.meta 속성에挂载 가능. 예를 들어:
// 모듈의 URL(브라우저 환경)
import.meta.url
// 현재의 모듈이 所在하는 script 태그
import.meta.scriptElement
그러나주의가 필요한 것은, 사양은 구체적인 속성名과 의미를 명확히 정의하지 않았다는 것. 모두 구체적인 실장에 의존. 따라서 특성 제안 중의 브라우저에 서포트해주길 바라는 이들 2 개의 속성은 장래 서포트될 수도 있고, 안 될 수도 있음
P.S. import.meta 자체는 오브젝트로, 프로토타입은 null
export-ns-from
3 번째 ES Module 관련의 새 특성은 또 하나의 종류 모듈 수출 구문:
export * as ns from "mod";
export ... from ... 형식의 집약 수출 에 동속. 작용 상은 이하와 유사:
import * as ns from "mod";
export {ns};
그러나 현재의 모듈 스코프에 목표 모듈의 각 API 변수를 도입하지 않음
P.S. import * as ns from "mod"; 구문과 대조하면, ES6 모듈 설계 중의 排列組合의 1 개의疏漏처럼 보입니다;)
三.체인 조작 서포트
Optional Chaining
매우 실용적인 특성으로, 이러한 冗長한 안전 체인 조작을 대체하기 위해 사용:
const street = user && user.address && user.address.street;
새 특성 (?.) 으로 환용 가능:
const street = user?.address?.street;
구문 형식은 다음과 같습니다:
obj?.prop // 옵션의 정적 속성에 액세스
// 等价于
(obj !== undefined && obj !== null) ? obj.prop : undefined
obj?.[?expr?] // 옵션의 동적 속성에 액세스
// 等价于
(obj !== undefined && obj !== null) ? obj[?expr?] : undefined
func?.(?arg0?, ?arg1?) // 옵션의 함수 또는 메서드를 호출
// 等价于
(func !== undefined && func !== null) ? func(arg0, arg1) : undefined
P.S.주의 연산자는 ?. 이지 단 ? 가 아님. 함수 호출 중에서 조금 이상함 alert?.(). 이는 삼항 연산자 중의 ? 와 구분하기 위함
메커니즘은 매우 간단. 물음표 앞의 값이 undefined 또는 null 이 아닌 경우에만, 물음표 뒤의 조작을 실행.否则 undefined 를 반환
마찬가지로 쇼트컷 특성을 가짐:
// .b?.m 에서 쇼트컷하여 undefined 를 반환하고, alert 'here' 하지 않음
({a: 1})?.a?.b?.m?.(alert('here'))
&& 와 비교하여, 새로운 ?. 연산자는 안전하게 체인 조작을 수행하는 씬에 더욱 적합. 왜냐하면:
-
시맨틱스가 더욱 명확:
?.는 속성/메서드가 존재하지 않으면undefined를 반환.&&와 같이 왼쪽의 값을 반환하는 것이 아님 (거의 쓸모없음) -
존재성 판단이 더욱 정확:
?.는null과undefined만을 대상.&&는 임의의 偽値 를 만나면 반환. 때로 수요를 만족시킬 수 없음
예를 들어常用的인 정규 표현식으로 목표 문자열을 추출. 구문 기술은 매우 간결:
'string'.match(/(sing)/)?.[1] // undefined
// 이전에는 이렇게 할 필요
('string'.match(/(sing)/) || [])[1] // undefined
Nullish coalescing Operator 특성과 배합하여 디폴트 값을 충전 가능:
'string'.match(/(sing)/)?.[1] ?? '' // ''
// 이전에는 이렇게 할 필요
('string'.match(/(sing)/) || [])[1] || '' // ''
// 또는
('string'.match(/(sing)/) || [, ''])[1] // ''
Nullish coalescing Operator
마찬가지로 새로운 구문 구조 (??) 를 도입:
actualValue ?? defaultValue
// 等价于
actualValue !== undefined && actualValue !== null ? actualValue : defaultValue
디폴트 값을 제공하기 위해 사용. 왼쪽의 actualValue 가 undefined 또는 null 인 경우, 오른쪽의 defaultValue 를 반환.否则 왼쪽의 actualValue 를 반환
|| 와 유사. 주요 차이는 ?? 는 null 과 undefined 만을 대상. || 는 임의의 偽値를 만나면 오른쪽의 디폴트 값을 반환
四.큰 수치 연산
BigInt 라고 불리는 새로운 기초 타입을 추가. 큰 정수 연산 서포트를 제공:
BigInt is a new primitive that provides a way to represent whole numbers larger than 2^53, which is the largest number Javascript can reliably represent with the Number primitive.
BigInt
JavaScript 중 Number 타입이 정확하게 표현할 수 있는 최대의 정수는 2^53. 더 큰 수에 대한 연산을 서포트하지 않음:
const x = Number.MAX_SAFE_INTEGER;
// 9007199254740991 즉 2^53 - 1
const y = x + 1;
// 9007199254740992 正确
const z = x + 2
// 9007199254740992 错了、변하지 않음
P.S. 왜 2 의 53 승인지에 대해서는, JS 중의 수치는 모두 64 비트 부동 소수점 형식으로存放. 1 개의 부호 비트, 11 개의 지수 비트 (과학 계수법 중의 지수) 를 제외하고, 나머지 52 비트로 수치를存放. 2 의 53 승에 대응하는 이 52 비트는 모두 0. 표현할 수 있는 다음 수는 2^53 + 2. 중간 의 2^53 + 1 은 표현 불가:
[caption id="attachment_2213" align="alignnone" width="625"]
JavaScript Max Safe Integer[/caption]
구체적인 설명은 BigInts in JavaScript: A case study in TC39 참조
BigInt 타입의 출현은 바로此类의 문제를 해결하기 위함:
9007199254740991n + 2n
// 9007199254740993n 正确
도입된 새로운 것은 이하를 포함:
-
큰 정수 리터럴: 숫자에 후缀
n을 붙여 큰 정수를 표. 예를 들어9007199254740993n,0xFFn(이진법, 팔진법, 십진법, 십육진법 리터럴 모두 후缀n을 붙여BigInt로 변환 가능) -
bigint기초 타입:typeof 1n === 'bigint' -
타입 구축 함수:
BigInt -
수학 연산자 (가감 승제 등) 를 오버로드: 큰 정수 연산을 서포트
예를 들어:
// BigInt 를 생성
9007199254740993n
// 또는
BigInt(9007199254740993)
// 승산 연산
9007199254740993n * 2n
// べき 연산
9007199254740993n ** 2n
// 비교 연산
0n === 0 // false
0n === 0n // true
// toString
123n.toString() === '123'
P.S. BigInt API 상세에 관한 더 많은 정보는, ECMAScript feature: BigInt – arbitrary precision integers 참조
주의가 필요한 것은 BigInt 는 Number 와 혼용하여 연산할 수 없음:
9007199254740993n * 2
// 오류 Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
또 BigInt 는 정수만을 표할 수 있으므로, 제산은 직접 절사 (Math.trunc() 에 상당):
3n / 2n === 1n
五.기초 API
기초 API 에도 몇 가지 새로운 변화가 있으며, Promise, 문자열 정규 표현식 매치, for-in 루프 등을 포함
Promise.allSettled
[Promise.all](/articles/完全理解 promise/#articleHeader12), [Promise.race](/articles/完全理解 promise/#articleHeader13) 에 이어, Promise 는 새로운 정적 메서드 allSettled 를 추가:
// 传入된 모든 promise 가 결과 (pending 상태에서 fulfilled 또는 rejected 가 됨) 를 가진 후, onFulfilled 을 트리거
Promise.allSettled([promise1, promise2]).then(onFulfilled);
P.S. 另外, any 도 路上. 현재 (2020/6/21) 는 Stage 3
all 과 유사. 그러나 某些項 rejected 로 쇼트컷하지 않음. 즉, allSettled 는 모든 項이 결과 (성공 실패 불문) 를 가진 후에야 Promise 체인의 다음 環으로 들어감 (따라서 반드시 Fulfilled 상태가 됨):
A common use case for this combinator is wanting to take an action after multiple requests have completed, regardless of their success or failure.
예를 들어:
Promise.allSettled([Promise.reject('No way'), Promise.resolve('Here')])
.then(results => {
console.log(results);
// [
// {status: "rejected", reason: "No way"},
// {status: "fulfilled", value: "Here"}
// ]
}, error => {
// No error can get here!
})
String.prototype.matchAll
문자열 처리의 일반적인 씬은 문자열 중의 모든 목표 부분 문자열을 매치하고 싶은 것. 예를 들어:
const str = 'es2015/es6 es2016/es7 es2020/es11';
str.match(/(es\d+)\/es(\d+)/g)
// 순조롭게 ["es2015/es6", "es2016/es7", "es2020/es11"] 를 얻
match() 메서드 중에서, 정규 표현식이 매치한 여러 개의 결과는 배열에 팩되어 반환. 그러나각 매치의 결과 외의 관련 정보를得知할 수 없음. 예를 들어 캡처된 부분 문자열, 매치한 index 위치 등:
This is a bit of a messy way to obtain the desired information on all matches.
此时는 가장 강력한 exec 에 求助할 수밖에 없음:
const str = 'es2015/es6 es2016/es7 es2020/es11';
const reg = /(es\d+)\/es(\d+)/g;
let matched;
let formatted = [];
while (matched = reg.exec(str)) {
formatted.push(`${matched[1]} alias v${matched[2]}`);
}
console.log(formatted);
// ["es2015 alias v6", "es2016 alias v7", "es2020 alias v11"] 를 얻
ES2020 이新增한 matchAll() 메서드는 바로此类의 씬에 대한 보충:
const results = 'es2015/es6 es2016/es7 es2020/es11'.matchAll(/(es\d+)\/es(\d+)/g);
// 배열로 변환하여 처리
Array.from(results).map(r => `${r[1]} alias v${r[2]}`);
// 또는 이터레이터에서 꺼내어 직접 처리
// for (const matched of results) {}
// 결과는 위와 같음
주의, matchAll() 은 match() 와 같이 배열을 반환하는 것이 아니라, [이터레이터](/articles/generator(生成器)-es6 노트 2/#articleHeader2) 를 반환.大数据量의 씬에 더욱 우호적
for-in 주사 메커니즘
JavaScript 중에서 for-in 으로 오브젝트를 주사할 때 key 의 순서는 불확정. 왜냐하면 사양은 명확히 정의하지 않았고,かつ 프로토타입 속성을 주사할 수 있어 for-in 의 실장 메커니즘은 매우 복잡해지며, 다른 JavaScript 엔진은 각자根深은 다른 실장을 가지며, 통일하는 것이 매우 어려움
따라서 ES2020 은 속성 주사 순서를 통일하는 것을 요구하지 않고, 주사 프로세스 중의 몇 가지 특수한 Case 에 대해 명확히 몇 가지 룰을 정의:
-
Symbol 타입의 속성을 주사할 수 없음
-
주사 프로세스 중, 목표 오브젝트의 속성은 삭제 가능. 아직 주사되지 않고 이미 삭제된 속성을 무시
-
주사 프로세스 중,新增 속성이 있는 경우, 새로운 속성이 당회의 주사로 처리되는 것을 보증하지 않음
-
속성名은 중복하여 출현하지 않음 (1 개의 속성名은最多 1 회 출현)
-
목표 오브젝트整条 프로토타입 체인 상의 속성은 모두 주사 가능
상세는 13.7.5.15 EnumerateObjectProperties 참조
globalThis
마지막 새 특성은 globalThis. 브라우저, Node.js 등 다른 환경 중에서, 글로벌 오브젝트名称이 통일되어 있지 않고, 글로벌 오브젝트를 획득하는 것이麻烦한 문제를 해결하기 위해 사용:
var getGlobal = function () {
// the only reliable means to get the global object is
// `Function('return this')()`
// However, this causes CSP violations in Chrome apps.
if (typeof self !== 'undefined') { return self; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
throw new Error('unable to locate global object');
};
globalThis 는 통일된 글로벌 오브젝트 액세스 방식으로서, 항상 글로벌 스코프 중의 this 값을指:
The global variable globalThis is the new standard way of accessing the global object. It got its name from the fact that it has the same value as this in global scope.
P.S. 왜 global 라고 부르지 않는지? global 은現有의 몇 가지 코드에 영향을 줄 가능성이 있으므로,別の globalThis 를起하여 충돌을 피함
至此, ES2020 의 모든 새 특성이清楚해졌습니다
六.정리
ES2019 와 비교하여, ES2020 는 큰 업데이트라고 할 수 있습니다. 동적 import, 안전한 체인 조작, 큰 정수 서포트……모두 호화 런치에 추가되었습니다
아직 댓글이 없습니다