본문으로 건너뛰기

ES2019

무료2019-09-28#JS#ES10#ES 2019#ES2019指南#ES2019总结#ES2019教程

몇 가지 작은 특성이 추가되었고, 구문/의미론上で 몇 가지 수정이 이루어졌습니다.总之, 이번 업데이트에서는 큰 일은 발생하지 않았습니다

一.개관

2019 년 6 월에 ES2019 사양, 즉 ES10 이 릴리스되었습니다

4 개의 신특성이 포함됩니다:

및 6 개의 구문/의미론上の 변화:

  • Optional catch binding:try-catch 구조 중의 catch 블록의 파라미터 부분을 생략 가능

  • Array.prototype.sort:소트 알고리즘은 안정적이어야 함 (같은 요소의 소트 전후의 순서는 불변)

  • Well-formed JSON.stringify:JSON.stringify 가 포맷이 좋은 UTF-8 문자열을 반환할 것을 요구

  • JSON superset:문자열 리터럴 중에 U+2028(LINE SEPARATOR) 와 U+2029(PARAGRAPH SEPARATOR) 의 출현을 허가

  • Function.prototype.toString revision:function 소스 텍스트, 또는 표준 플레이스홀더를 반환할 것을 요구

P.S.V8 v7.3+, Chrome 73+ 는 ES2019 의 모든 특성을 서포트

二.Array.prototype.{flat,flatMap}

flat

Array.prototype.flat( [ depth ] )

배열을 평탄화하는 flatten 메서드로, 옵션의 depth 파라미터를 서포트하며, 지정된 층수를 평탄화하는 것을 나타냅니다 (디폴트는 1):

[[1], [[2]], [[[3]]]].flat()
// [1, [2], [[3]]] 를 취득
[[1], [[2]], [[[3]]]].flat(Infinity)
// [1, 2, 3] 를 취득

간단한 구현은 다음과 같습니다:

const flat = (arr, depth = 1) => {
  if (depth > 0) {
    const flated = Array.prototype.concat.apply([], arr);
    // 또는
    // const flated = arr.reduce((a, v) => a.concat(v), []);
    const isFullFlated = flated.reduce((a, v) => a && !Array.isArray(v), true);
    return isFullFlated ? flated : flat(flated, depth - 1);
  }

  return arr;
};

flatMap

Array.prototype.flatMap ( mapperFunction [ , thisArg ] )

P.S. 옵션 파라미터 thisArgmapperFunction 중의 this 로서 사용됩니다. 예를 들어:

[1, 2, 3, 4].flatMap(function(x) {
  return this.value ** x;
}, { value: 2 })
// [2, 4, 8, 16] 를 취득

작용上, flatMapmap 과 유사하며,主な 차이는:map 은 1 대 1 의 매핑을 수행하고, flatMap 은 1 대 다 (0 개에 대응하는 것도 가능) 를 서포트합니다

예를 들어:

[2, 0, 1, 9].flatMap(x => new Array(x).fill(x))
// [2, 2, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9] 를 취득

각 요소를 배열에 매핑하고, 마지막에 1 층을 평탄화하는 것에 상당합니다:

// 성능을 고려하지 않는 경우, 이렇게 간단히 구현 가능
// const flatMap = (arr, f) => arr.map(f).flat();
// 또는
// const flatMap = (arr, f) => arr.reduce((a, v) => a.concat(f(v)), []);

主に 2 개의 응용 장면이 있습니다:

  • map + filter:빈 배열을 반환하는 것은 1 대 0 을 나타내며, 즉 filter

  • 1 대 다 매핑

예를 들어 지정 디렉토리 내의 모든 비숨김 파일을 리스트:

// node 12.10.0
const fs = require('fs');
const path = require('path');

// map + filter 결합 1 대 다 매핑
const listFiles = dir => fs.readdirSync(dir).flatMap(f => {
  if (f.startsWith('.')) return [];

  const filePath = path.join(dir, f);
  return fs.statSync(filePath).isDirectory() ? listFiles(filePath) : filePath;
});

三.Object.fromEntries

Object.fromEntries ( iterable )

한 조의 키값 쌍을 오브젝트로 변환하는 데 사용되며, Object.entries 역연산에 상당하며, 데이터 타입 변환上の 결락을 보충 하는 데 사용됩니다 (key-value pairs to Object):

const entries = Object.entries({ a: 1, b: 2 });
// [["a", 1], ["b", 2]] 를 취득
const obj = Object.fromEntries(entries);
// {a: 1, b: 2} 를 취득

lodash 가 제공하는 _.fromPairs(pairs) 와 유사하며, 간단한 구현은 다음과 같습니다:

const fromEntries = pairs => pairs.reduce((acc, [ key, val ]) => Object.assign(acc, { [key]: val }), {});

P.S. 공식 polyfill 은 es-shims/Object.fromEntries 참조

特殊的:

  • key 가 같은 키값 쌍이 존재하는 경우, 뒤가 앞을 커버

  • Symbol 을 key 로서 사용 가능 (Object.entries 는 Symbol key 를 무시)

  • 키값 쌍 중의 비 String/Symbol 타입의 key 는 강제로 String 으로 변환

  • 파라미터는 iterable 을 서포트하며, 배열에 한정되지 않음

  • 열거 가능한 데이터 속성의 작성のみ를 서포트

예를 들어:

// 1.key 가 같은 키값 쌍이 존재하는 경우, 뒤가 앞을 커버
Object.fromEntries([['a', 1], ['b', 2], ['a', 3]]);
// {a: 3, b: 2} 를 취득

// 2.Symbol 을 key 로서 사용 가능 (`Object.entries` 는 Symbol key 를 무시)
Object.fromEntries([[Symbol('a'), 1], ['b', 2]]);
// {b: 2, Symbol(a): 1} 를 취득

// 3.키값 쌍 중의 비 String/Symbol 타입의 key 는 강제로 String 으로 변환
Object.fromEntries([[new Error('here'), 1], [{}, 2]]);
// {['Error: here']: 1, ['[object Object]']: 2} 를 취득

// 4.파라미터는 iterable 을 서포트하며, 배열에 한정되지 않음
Object.fromEntries(function*(){
  yield ['a', 1];
  yield ['b', 2];
}());
// {a: 1, b: 2} 를 취득

// 5.열거 가능한 데이터 속성의 작성のみ를 서포트
Object.getOwnPropertyDescriptors(Object.fromEntries([['a', 1]]))
// { a: {value: 1, writable: true, enumerable: true, configurable: true} } 를 취득

四.String.prototype.{trimStart,trimEnd}

trimLeft/trimRight 의 표준 정의와算是하며, 명명上은 ES2017 의 padStart/padEnd 와 일치를 보존하기 위함입니다

기능上, 공백 문자 및 개행符는 trim 됩니다:

// 공백 문자 https://tc39.github.io/ecma262/#sec-white-space
'\u0009'  // <TAB> CHARACTER TABULATION
'\u000B'  // <VT> LINE TABULATION
'\u000C'  // <FF> FORM FEED
'\u0020'  // <SP> SPACE
'\u00A0'  // <NBSP> NO-BREAK SPACE
'\uFEFF'  // <ZWNBSP> (ZERO WIDTH NO-BREAK SPACE
// ...및其它 Space_Separator 類下에 White_Space 속성을 가진 Unicode 문자

// 개행符 https://tc39.github.io/ecma262/#sec-line-terminators
'\u000A'  // <LF> LINE FEED
'\u000D'  // <CR> CARRIAGE RETURN
'\u2028'  // <LS> LINE SEPARATOR
'\u2029'  // <PS> PARAGRAPH SEPARATOR

예를 들어:

'\u0009\u000B\u000C\u0020\u00A0\uFEFF\u000A\u000D\u2028\u2029'.trim().length === 0
'\u0009\u000B\u000C\u0020\u00A0\uFEFF\u000A\u000D\u2028\u2029'.trimStart().length === 0
'\u0009\u000B\u000C\u0020\u00A0\uFEFF\u000A\u000D\u2028\u2029'.trimEnd().length === 0

더욱이, 후방 호환을 위해, trimLeft/trimRight 는 여전히 보류되며, 사양 Annex B(B Additional ECMAScript Features for Web Browsers, Web 브라우저 구현을 요구) 중에 정의되어 있지만, trimStart/trimEnd 의 사용을 권장:

The property trimStart is preferred. The trimLeft property is provided principally for compatibility with old code. It is recommended that the trimStart property be used in new ECMAScript code.

二者는 별명 관계이므로, 재미있는 일이 발생했습니다:

String.prototype.trimLeft.name === 'trimStart'
String.prototype.trimRight.name === 'trimEnd'

五.Symbol.prototype.description

[Symbol 작성시에传入된 description 파라미터](/articles/symbol-es6 노트 7/#articleHeader3) 에 Symbol.prototype.description 를 통해 액세스하는 것을 허가합니다. 예를 들어:

const mySymbol = Symbol('my description for this Symbol');
mySymbol.description === 'my description for this Symbol'

이전에는 toString 로該 설명 정보를截取하는 수밖에 없었습니다:

mySymbol.toString().match(/Symbol\(([^)]*)\)$/)[1]

P.S.description 속성은 읽기 전용입니다:

Symbol.prototype.description is an accessor property whose set accessor function is undefined.

六.구문/의미론 변화

Optional catch binding

예상 내의 이상에 대해, 보통 이렇게 합니다:

try {
  JSON.parse('');
} catch(err) { /* noop */ }

err 파라미터를 사용하지 않지만, 선언해야 합니다. 파라미터를 생략하면, 구문 해석 오류가 존재합니다:

try {
  JSON.parse('');
} catch() { }
// 오류 Uncaught SyntaxError: Unexpected token )

ES2019 는 try-catch 구조 중의 catch 블록의 파라미터 부분을 생략하는 것을 허가:

Allow developers to use try/catch without creating an unused binding

구문上, 2 종류의 형식의 catch 블록을 서포트:

// 파라미터 부분을 가지는 catch 블록
catch( CatchParameter[?Yield, ?Await] ) Block[?Yield, ?Await, ?Return]
// 파라미터 부분을 생략한 catch 블록
catch Block[?Yield, ?Await, ?Return]

예를 들어:

// node 12.10.0
const parseJSON = (str = '') => {
  let json;
  try {
    json = JSON.parse(str);
  } catch {
    consle.error('parseJSON error, just ignore it.');
  }
};

parseJSON('');
// parseJSON error, just ignore it. 를 출력

이론上, 대다수의 장면 중의 이상 정보는 무시해서는 안 됩니다 (기록하거나, 던지거나, 善後策을 강구). 비교적 합리적인 몇 종류의 장면 은:

  • assert.throws(func):테스트 구동 라이브러리용, 지정 함수 실행이 이상을 던지는 것을 단언 (어떠한 이상인지는 관심 없음)

  • 브라우저 특성 검출:특정 특성을 서포트하는かどうか만 알고 싶음

  • 善後措置 이상:예를 들어 logError() 자신이 이상을 발생, 포획해도 어찌할 도리 없음

P.S. 이러한 장면에서도, 어떤 이상을 무시한다고 결정할 때는 주석 중에서 원인을 설명해야 함

Array.prototype.sort

안정 소트여야 함 (소트 전후의 같은 요소의 상대 순서는 불변):

The sort must be stable (that is, elements that compare equal must remain in their original order).

예를 들어:

const words = [{ id: 1, value: 'I' }, { id: 3, value: 'am' }, { id: 1, value: 'feeling' }, { id: 4, value: 'lucky' }];
words.sort((a, b) => a.id - b.id);
console.log(words.map(v => v.value).join(' '));
// 기대 결과는 I feeling am lucky
// feeling I am lucky 가 아님

Well-formed JSON.stringify

JSON 사양 은 널리 통용되는 JSON 은 UTF-8 인코드를 사용해야 함을 요구:

JSON text exchanged between systems that are not part of a closed ecosystem MUST be encoded using UTF-8.

JavaScript 중에서, 단독으로 출현하는 절반의 代理對儿 에 대해, JSON.stringify() 시에 문제가 존재:

JSON.stringify('\uD800')
// '"?"' 를 취득

실제로는, JSON 은 \u 형식의 이스케이프 구문을 서포트하므로, ES2019 는 JSON.stringify() 가 포맷이 좋은 UTF-8 인코드 문자열을 반환할 것을 요구:

JSON.stringify('\uD800');
// '"\\ud800"' 를 취득

JSON.stringify() 의 bug 修復과算是

P.S. JavaScript 중의 Unicode 에 관한 더 많은 정보는, JavaScript 중의 Unicode 참조

JSON superset

리터럴 형식의 (이스케이프되지 않은) U+2028U+2029 문자는 JSON 중에서는 합법이지만, JavaScript 문자열 리터럴 중에서는 非法 문자입니다:

const LS = "?";
const PS = eval("'\u2029'");
// 오류 Uncaught SyntaxError: Invalid or unexpected token

ES2019 사양은 문자열 리터럴이 완전한 JSON 문자 세트를 서포트할 것을 요구, 즉JavaScript 는 JSON 의 超集. ES2019 를 서포트하는 환경 중에서는, 이중 인용부호/단일 인용부호 중의 U+2028U+2029 문자에 대해, 上記 구문 오류를 던지지 않게 되었습니다 (정규 표현식 리터럴 중에서는 여전히 이들 2 개의 문자의 출현을 허가하지 않음)

P.S.[템플릿 문자열](/articles/템플릿 문자열-es6 노트 3/#articleHeader5) 은 이 문제가 존재하지 않습니다:

const LS = `?`;
const PS = eval("`\u2029`");

Function.prototype.toString revision

function 소스 텍스트, 또는 표준 플레이스홀더를 반환할 것을 요구:

implementations must not be required to retain source text for all functions defined using ECMAScript code

구체적으로는 다음과 같습니다:

  • 함수가 ES 코드를 통해 작성된 경우, toString() 은 그 소스를 반환해야 함

  • toString() 이 합법적인 ES 코드를 취득할 수 없는 경우, 표준 플레이스홀더를 반환. 플레이스홀더 문자열은 반드시 합법적인 ES 코드여서는 안 됨 (eval(플레이스홀더) 은 반드시 SyntaxError 를 던짐)

P.S. 사양이 권장하는 플레이스홀더 형식은 "function" BindingIdentifier? "(" FormalParameters ")" "{ [native code] }" 로, 파라미터는 생략 가능하고,かつ内置 메서드는 메서드 이름을给出해야 합니다. 예를 들어:

document.createAttribute.toString()
// "function createAttribute() { [native code] }" 를 출력

特殊的:

  • toString() 이 반환하는 함수 소스는 반드시 합법적이지 않으며, 그 詞法上下文中에서만 합법적인 가능성이 있음

  • Function 생성자等方式을 통해 동적으로 작성된 함수도, toString() 이 적절한 소스를 반환할 것을 요구

    // 1.toString() 반환 값은 그 詞法上下文中에서만 합법적인 가능성이 있음 class C { foo() { /hello/ } } const source = C.prototype.foo.toString(); eval(source) // 오류 Uncaught SyntaxError: Unexpected token {

    // 2.Function 생성자等方式을 통해 동적으로 작성된 함수도 서포트 new Function('a', 'b', 'return a + b;').toString() // function anonymous(a,b) { return a + b; } 를 출력

七.まとめ

flat/flatMap, trimStart/trimEnd 등의 툴 함수는 모두 표준에 조립되었고, Object 는 또 하나 어떻게でもいい 메서드가 늘어났으며, Symbol 은 그 설명 정보를 직접 읽을 수 있게 되었습니다

더욱이, 구문/의미론上에서도 몇 가지 수정이 이루어졌으며, catch 블록의 파라미터 부분을 생략 가능, 배열 sort() 는 안정 소트여야 함, 함수 toString() 의 구체적 구현을 명확, JSON 서포트를完善, JSON 의 超集이 되는 것을 기대 (JSON ? ECMAScript)

참고 자료

댓글

아직 댓글이 없습니다

댓글 작성