ECMAScript 6 부록 A. 작은 변경 사항

ECMAScript 6 부록 A. 작은 변경 사항

이 책에서 설명한 주요 변경 사항과 함께 ECMAScript 6은 작지만 JavaScript를 향상시키는데 도움이되는 몇가지 다른 변경 사항을 적용했습니다. 이러한 변경에는 정수를 더 쉽게 사용하고 계산하기 위한 새로운 메서드를 추가하였고, 유니 코드 식별자를 조정하고, __proto__ 프로퍼티를 공식화하는 작업이 포함됩니다. 부록에서 이 모든 내용을 설명합니다.

정수(Integer) 작업

JavaScript는 IEEE 754 인코딩 시스템을 사용하여 정수와 부동 소수점을 모두 표현하므로 수년 동안 많은 혼란을 야기했습니다. JavaScript는 개발자가 숫자 인코딩의 세부사항에 대해 걱정할 필요가 없도록 하기위해 엄청난 노력을 기울였지만 문제는 계속되었습니다. ECMAScript 6은 정수를 쉽게 식별하고 사용할 수 있게함으로써 이 문제를 해결하려고 노력합니다.

정수 확인하기

첫번째로, ECMAScript 6은 JavaScript에서 값(value)이 정수를 나타내는지 여부를 확인할 수있는 Number.isInteger() 메서드를 추가했습니다. JavaScript는 IEEE 754를 사용하여 두 타입의 숫자를 모두 나타내지만, 실수와 정수는 다르게 저장됩니다. Number.isInteger() 메서드는 이를 활용하여 메서드를 호출하면 JavaScript 엔진은 값의 기본 표현을 보고 해당 값이 정수인지 여부를 확인합니다. 즉, 실수처럼 보이는 숫자가 실제로 정수로 저장되어 있어도 Number.isInteger()true를 반환하도록 합니다.

1
2
3
console.log(Number.isInteger(25)); // true
console.log(Number.isInteger(25.0)); // true
console.log(Number.isInteger(25.1)); // false

이 코드에서 Number.isInteger()2525.0 모두에 대해 후자가 실수처럼 보일지라도 true를 반환합니다. 숫자에 소수점을 추가하는 것만으로 자동으로 JavaScript에서 부동 소수점을 차지하지 않습니다. 25.0은 실제로 25와 같기 때문에 정수로 저장됩니다. 그러나 숫자 25.1는 분수 값이 있으므로 실수로 저장됩니다.

안전한 정수

IEEE 754는 -2^53와 2^53 사이의 정수만 정확하게 나타낼 수 있으며 이 “안전” 범위를 벗어나면 바이너리 표현은 여러개의 숫자 값으로 재사용됩니다. 즉, JavaScript는 IEEE 754 범위 내의 정수만 안전하게 나타낼 수 있습니다. 예를 들어 다음 코드를 살펴보겠습니다.

1
2
console.log(Math.pow(2, 53)); // 9007199254740992
console.log(Math.pow(2, 53) + 1); // 9007199254740992

이 예제에서 두개의 다른 숫자가 동일한 JavaScript 정수로 표시됩니다. 값이 안전 범위를 벗어날수록 효과가 더 커집니다.

ECMAScript 6에서는 JavaScript가 정확하게 나타낼 수 있는 정수를 보다 잘 식별할 수 있도록 Number.isSafeInteger() 메서드를 도입했습니다. 또한 정수 범위의 상한과 하한을 각각 나타내는 Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER 프로퍼티를 추가했습니다. Number.isSafeInteger() 메서드는 값이 정수이고 아래의 예제에서와 같이 정수값의 안전한 범위내에 있는지 확인합니다.

1
2
3
4
5
6
7
8
var inside = Number.MAX_SAFE_INTEGER,
outside = inside + 1;
console.log(Number.isInteger(inside)); // true
console.log(Number.isSafeInteger(inside)); // true
console.log(Number.isInteger(outside)); // true
console.log(Number.isSafeInteger(outside)); // false

inside는 가장 큰 안전한 정수이므로 Number.isInteger()Number.isSafeInteger() 메서드 모두에 대해 true를 반환합니다. outside는 의심되는 첫 번째 정수값이며, 정수이지만 안전하지 않은 것으로 간주됩니다.

대부분의 경우, JavaScript에서 정수 연산 또는 비교를 수행할 때 안전한 정수를 처리하기를 원하므로 입력 유효성 검사의 일부로 Number.isSafeInteger()를 사용하는 것이 좋습니다.

새로운 Math 메서드

ECMAScript 6에서 Typed array가 JavaScript에 포함되어 게임 및 그래픽에 대한 JavaScript 엔진이 많은 수학적 계산을 보다 효율적으로 수행할 수 있다는 사실을 깨닫게됩니다. 그러나 asm.js와 같은 최적화 전략은 성능을 향상시키기 위해 JavaScript의 하위 집합에서 작동되고, 가능한 가장 빠른 방법으로 계산을 수행하는데 더 많은 정보가 필요합니다. 예를 들어, 숫자를 32비트 정수로 처리해야하는지 아니면 64비트 부동 소수점으로 처리해야하는지 여부를 아는 것이 중요하고, 소프트웨어 기반 연산보다 하드웨어 기반 연산이 훨씬 빠릅니다.

결과적으로, ECMAScript 6는 일반적인 수학적 계산의 속도를 향상시키기 위해 여러 가지 메서드를 Math 객체에 추가했습니다. 일반 계산 속도를 향상 시키면 그래픽 프로그램과 같이 많은 계산을 수행하는 응용 프로그램의 전체 속도가 향상됩니다. 새로운 메서드는 다음과 같습니다.

  • Math.acosh(x) x의 역쌍곡선 코사인을 반환합니다.
  • Math.asinh(x) x의 역쌍곡선 사인을 돌려줍니다.
  • Math.atanh(x) x의 역쌍곡선 탄젠트를 반환합니다.
  • Math.cbrt(x) x의 세제곱근을 구합니다.
  • Math.clz32(x) x의 32 비트 정수 표현으로 선두의 제로의 비트의 수를 돌려줍니다.
  • Math.cosh(x) x의 쌍곡선 코사인을 반환합니다.
  • Math.expm1(x) x의 지수 함수에서 1을 뺀 결과를 반환합니다.
  • Math.fround(x) 가장 가까운 x의 단정도 float를 반환합니다.
  • Math.hypot(... values) 각 인수의 제곱의 합계의 제곱근을 반환합니다.
  • Math.imul(x, y) 2 개의 인수의 진정한 32 비트 승수를 실행 한 결과를 돌려줍니다.
  • Math.log1p(x) 1 + x의 자연 대수를 구합니다.
  • Math.log10(x) x의 기본 10 로그를 구합니다.
  • Math.log2(x) x의 기본 2 로그를 구합니다.
  • Math.sign(x) x가 음수이면 -1을 반환하고 x가 +0 또는 -0이면 0을, x가 양수이면 1을 반환합니다.
  • Math.sinh(x) x의 쌍곡선 사인을 돌려줍니다.
  • Math.tanh(x) x의 쌍곡선 탄젠트를 반환합니다.
  • Math.trunc(x) float에서 소수 자릿수를 제거하고 정수를 반환합니다.

각각의 새로운 메서드의 자세한 설명은 이 책의 범위를 벗어납니다. 그러나 응용 프로그램에서 합리적으로 공통적인 계산을 수행해야하는 경우 직접 구현하기 전에 새로운 Math 메서드를 확인하십시오

유니코드 식별자

ECMAScript 6은 이전 버전의 JavaScript보다 더 나은 유니코드 지원을 제공하며 식별자로 사용할 수있는 문자도 변경합니다. ECMAScript 5에서는 이미 식별자에 유니코드 이스케이프 시퀀스를 사용할 수있었습니다.

1
2
3
4
5
6
7
// ECMAScript 5와 6에서 유효합니다.
var \u0061 = "abc";
console.log(\u0061); // "abc"
// 아래와 동일합니다.
console.log(a); // "abc"

이 예제에서 var 문장 다음에 \u0061 또는 a를 사용하여 변수에 접근할 수 있습니다. ECMAScript 6에서는 다음과 같이 유니코드 코드포인트 이스케이프 시퀀스를 식별자로 사용할 수도 있습니다.

1
2
3
4
5
6
7
// ECMAScript 5와 6에서 유효합니다.
var \u{61} = "abc";
console.log(\u{61}); // "abc"
// 아래와 동일합니다.
console.log(a); // "abc"

이 예제는 단지 \u0061를 해당 코드포인트와 동일하게 대체합니다. 그리고 앞의 예제와 완전히 똑같습니다.

또한 ECMAScript 6은 유니코드 표준 Annex # 31 : 유니 코드 식별자 및 패턴 구문과 관련하여 유효한 식별자를 공식적으로 지정합니다. 이 식별자는 다음 규칙을 제공합니다.

  1. 첫 번째 문자는 $, _ 또는 ID_Start의 파생 코어 프로퍼티가 있는 모든 유니코드 여야합니다.
  2. 연속되는 각 문자는 $, _, \u200c (zero-width non-joiner), \u200d (zero-width joiner) 또는 ID_Continue의 파생 코어 프로퍼티가 있는 유니코드 여야합니다.

ID_StartID_Continue의 파생 코어 프로퍼티는 변수 및 도메인 이름과 같은 식별자에 사용하기 적합한 기호를 식별하는 방법으로 유니 코드 식별자 및 패턴 구문에서 정의됩니다. 이 사양은 JavaScript에만 해당되는 것이 아닙니다.

__proto__ 프로퍼티 공식화

ECMAScript 5가 완성되기 전에 여러 JavaScript 엔진이 이미 [[Prototype]] 프로퍼티를 가져오고 설정할 수있는 __proto__라는 사용자 정의 프로퍼티를 구현했습니다. __proto__Object.getPrototypeOf()Object.setPrototypeOf() 메서드의 효과적인 초기 선행 도구였습니다. 모든 JavaScript 엔진에서 이 프로퍼티 제거를 기대하는것은 비현실적입니다(__proto__를 사용하는 인기있는 JavaScript 라이브러리가 있으므로). 그래서 ECMAScript 6은 __proto__ 동작을 공식화합니다. 공식화는 아래의 경고와 함께 ECMA-262 부록 B에 설명되어 있습니다.

이러한 기능은 핵심 ECMAScript 언어의 일부로 간주되지 않습니다. 프로그래머는 새로운 ECMAScript 코드를 작성할 때 이러한 기능 및 동작을 사용하거나 가정해서는 안됩니다. ECMAScript 구현은 구현이 웹 브라우저의 일부이거나 웹 브라우저에서 발생하는 것과 동일한 레거시 ECMAScript 코드를 실행해야하는 경우가 아니면 이러한 기능을 구현하지 않는 것이 좋습니다.

__proto__에는 다음과 같은 특성이 있으므로 ECMAScript 사양에서는 Object.getPrototypeOf()Object.setPrototypeOf()를 대신 사용하도록 권장합니다.

  1. 객체 리터럴에서는 __proto__를 한번만 지정할 수 있습니다. 두개의 __proto__ 프로퍼티를 지정하면 오류가 발생합니다. 이것이 유일한 개체 리터럴 프로퍼티 제약입니다.
  2. 계산된 형식 ["__proto__"]는 일반 프로퍼티처럼 작동하며 현재 객체의 프로토 타입을 설정하거나 반환하지 않습니다. 객체 리터럴 프로퍼티와 관련된 모든 규칙이 이 형식으로 적용됩니다. 예외가있는 계산되지 않은 형식과는 대조적입니다.

__proto__ 프로퍼티 사용을 피해야 하지만, 사양에서 정의한 방식은 흥미 롭습니다. ECMAScript 6 엔진에서 Object.prototype.__ proto__get 메서드가 Object.getPrototypeOf()를 호출하고 set 메서드가 Object.setPrototypeOf() 메서드를 호출하는 접근자 프로퍼티로 정의됩니다. 이것은 __proto__Object.getPrototypeOf() / Object.setPrototypeOf() 사이에 실제적인 차이를 남기지 않습니다. 단, __proto__를 사용하면 객체 리터럴의 프로토 타입을 직접 설정할 수 있습니다. 작동 방식은 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let person = {
getGreeting() {
return "Hello";
}
};
let dog = {
getGreeting() {
return "Woof";
}
};
// prototype is person
let friend = {
__proto__: person
};
console.log(friend.getGreeting()); // "Hello"
console.log(Object.getPrototypeOf(friend) === person); // true
console.log(friend.__proto__ === person); // true
// set prototype to dog
friend.__proto__ = dog;
console.log(friend.getGreeting()); // "Woof"
console.log(friend.__proto__ === dog); // true
console.log(Object.getPrototypeOf(friend) === dog); // true

이 예는 Object.create()를 호출하여 friend 객체를 만드는 대신, __proto__ 프로퍼티에 값을 할당하는 표준 객체 리터럴을 만듭니다. 반면에 Object.create() 메서드를 사용하여 객체를 만들때는 추가 객체 프로퍼티에 대해 전체 프로퍼티 descriptor를 지정해야합니다.


이 내용은 나중에 참고하기 위해 제가 공부하며 정리한 내용입니다.
의역, 오역, 직역이 있을 수 있음을 알려드립니다.
This post is a translation of this original article [https://leanpub.com/understandinges6/read#leanpub-auto-appendix-a-smaller-changes]

참고

공유하기