ECMAScript 6 부록 B. ECMAScript 7 (2016) 이해하기

ECMAScript 6 부록 B. ECMAScript 7 (2016) 이해하기

ECMAScript 6의 개발은 약 4년이 걸렸으며, 그후 TC-39는 이러한 긴 개발 프로세스가 지속 가능하지 않다고 결정했습니다. 대신, 매년 릴리즈하는 주기로 전환하여 새로운 기능 적용을 더 빨리 진행할 수 있도록 했습니다.

더자주 출시되려면 ECMAScript의 새버전은 ECMAScript 6보다 새로운 기능이 적어야합니다. 이 변경 사항을 나타내기 위해 새버전의 사양에는 더이상 버전 번호가 표시되지 않으며 대신 사양이 게시된 연도를 나타냅니다. 결과적으로 ECMAScript 6은 ECMAScript 2015로 알려져 있으며 ECMAScript 7은 공식적으로 ECMAScript 2016으로 알려져 있습니다. TC-39는 향후 모든 ECMAScript 에디션에 연도 기반 명명 시스템을 사용할 것으로 기대하고 있습니다.

ECMAScript 2016은 2016년 3월에 완성되었으며 새로운 수학 연산자, 새로운 Array 메서드 및 새로운 구문 오류가 추가된 3가지 기능만 포함되었습니다. 모두 이 부록에서 다룹니다.

지수 연산자

ECMAScript 2016에서 소개된 JavaScript 구문의 유일한 변경 사항은 지수 연산자입니다. 이는 베이스에 지수를 적용하는 수학 연산입니다. JavaScript는 지수 연산을 수행하기 위해 이미 Math.pow() 메서드를 가지고 있었지만 JavaScript는 공식 연산자가 아닌 메서드를 필요로하는 유일한 언어중 하나이기도합니다. (일부 개발자들은 연산자가 읽고 이해하기가 더 쉽다고 주장합니다.)

지수 연산은 두개의 별표(**)이며 왼쪽 피연산자는 베이스이고 오른쪽 피연산자는 지수입니다.

1
2
3
4
let result = 5 ** 2;
console.log(result); // 25
console.log(result === Math.pow(5, 2)); // true

이 예제에서는 5^2와 동일한 25를 계산합니다. Math.pow()를 사용하여 동일한 결과를 얻을 수 있습니다.

연산자 우선순위

지수 연산자는 JavaScript에서 모든 2진 연산자중 우선 순위가 가장 높습니다 (단항 연산자는 **보다 우선 순위가 높음). 즉, 아래 예제에서와 같이 모든 복합 연산에서 처음에 적용됩니다.

1
2
let result = 2 * 5 ** 2;
console.log(result); // 50

먼저 5^2의 계산이 수행되고 결과 값에 2를 곱해 최종 결과 50을 얻습니다.

피연산자 제한

지수 연산자는 다른 연산자에 대해 존재하지 않는 다소 특이한 제한이 있습니다. 지수 연산의 왼쪽은 ++ 또는 --이외의 단항식일 수 없습니다. 예를 들어 아래는 잘못된 구문입니다.

1
2
// 구문 오류
let result = -5 ** 2;

이 예제에서 -5는 연산 순서가 모호하기 때문에 구문 오류입니다. -5 또는 5 ** 2 표현식의 어떤 결과에 적용되어야 할까요? 지수 연산자의 왼쪽에 단항식을 허용하지 않으면 해당 모호성이 제거됩니다. 의도를 명확하게 지정하려면 다음과 같이 -5 또는 5 ** 2 주변에 괄호를 포함시켜야합니다.

1
2
3
4
5
// ok
let result1 = -(5 ** 2); // equal to -25
// also ok
let result2 = (-5) ** 2; // equal to 25

식 주변에 괄호를 넣으면 -가 전체에 적용됩니다. 괄호가 -5를 둘러 싸면 -5의 2승에 대한 계산이 분명해집니다.

지수 연산자의 왼쪽에 ++--를 사용하면 두 연산자가 피연산자에 대해 명확하게 정의된 동작을 하기 때문에 괄호가 필요하지 않습니다. 접두어 ++ 또는 --는 다른 연산이 수행되기 전에 피연산자를 변경하며, 접미어 버전은 전체 표현식이 평가될 때까지 변경 사항을 적용하지 않습니다. 이 코드는 다음과 같이 이 연산자의 왼쪽에서 두 사례 모두 안전합니다.

1
2
3
4
5
6
7
8
let num1 = 2,
num2 = 2;
console.log(++num1 ** 2); // 9
console.log(num1); // 3
console.log(num2-- ** 2); // 4
console.log(num2); // 1

이 예제에서, 지수 연산자가 적용되기 전에 num1이 증가하기 때문에 num1은 3이되고 연산 결과는 9가됩니다. num2의 경우, 지수 연산값은 2를 유지한 다음 1로 감소합니다.

Array.prototype.includes() 메서드

주어진 문자열 내에 특정 부분 문자열이 존재하는지 확인하기 위해 ECMAScript 6에 String.prototype.includes()를 추가했음 기억하실 것입니다. 원래 ECMAScript 6에는 문자열과 Array를 비슷한 방식으로 처리하기 위해 Array.prototype.includes() 메서드를 도입했습니다. 그러나 Array.prototype.includes()에 대한 사양을 ECMAScript 6 마감 시한에 완료하지 못하고 Array.prototype.includes()는 ECMAScript 2016에서 완료되었습니다.

Array.prototype.includes() 사용법

Array.prototype.includes() 메서드는 검색할 값과 검색을 시작할 선택적 인덱스라는 두개의 파라미터를 받습니다. 두 번째 파라미터가 제공되면 includes()가 해당 인덱스에서 검색을 시작합니다. (기본 시작 인덱스는 0입니다.) 값이 Array 안에 있으면 true를 반환하고 그렇지 않으면 false를 반환합니다.

1
2
3
4
5
6
7
let values = [1, 2, 3];
console.log(values.includes(1)); // true
console.log(values.includes(0)); // false
// 인덱스 2부터 검색 시작
console.log(values.includes(1, 2)); // false

여기서 values.includes()를 호출하면 1의 값은 true를, 0Array에 없기 때문에 false를 반환합니다. 두 번째 파라미터가 인덱스 2 (값 3을 포함)에서 검색을 시작하는데 사용되고, 인덱스 2와 Array의 끝 사이에 숫자 1이 없기 때문에 values.includes() 메서드는 false를 반환합니다.

값 비교

includes() 메서드에 의해 수행된 값 비교는 한가지 예외를 제외하고는 === 연산자를 사용합니다(NaN === NaNfalse로 평가 되더라도NaNNaN과 같은 것으로 간주됩니다). 이는 비교를 위해 엄격하게 ===를 사용하는 indexOf() 메서드의 동작과는 다릅니다. 차이점을 확인하기위해 다음 코드를 살펴 보겠습니다.

1
2
3
4
let values = [1, NaN, 2];
console.log(values.indexOf(NaN)); // -1
console.log(values.includes(NaN)); // true

values.indexOf() 메서드는 NaNvalues Array에 포함되어 있더라도 NaN에 대해 -1을 반환합니다. 하지만, values.includes()는 다른 값 비교 연산자를 사용하기 때문에 NaN에 대해 true를 반환합니다.

Array에서 값의 존재만을 확인하고 인덱스를 알 필요가 없는 경우 NaN을 처리하는 방법의 차이 때문에 includes()를 사용하는 것이 좋습니다. 하지만 Array의 어느 위치에 값이 있는지 알아야하는 경우 indexOf() 메서드를 사용해야합니다.

이 구현의 또 다른 단점은 +0-0이 같은 것으로 간주된다는 것입니다. 이 경우 indexOf()includes()의 동작은 동일합니다.

1
2
3
4
let values = [1, +0, 2];
console.log(values.indexOf(-0)); // 1
console.log(values.includes(-0)); // true

여기에서 indexOf()includes()는 두값이 같다고 간주되어 -0이 전달되면 +0을 찾습니다. 이것은 +0-0을 다른 값으로 간주하는 Object.is() 메서드의 동작과 다릅니다.

함수 Scope의 Strict 모드 변경

strict 모드가 ECMAScript 5에 도입되었을 때, ECMAScript 6의 변화보다 더 단순했습니다. 그럼에도 불구하고 ECMAScript 6에서는 전역 Scope(모든 코드가 strict 모드로 실행됨) 또는 함수 Scope에서(함수만 strict 모드로 실행됨) “use strict” 지시문을 사용하여 strict 모드를 지정할 수있었습니다. 후자는 ECMAScript 6에서 파라미터를 정의할 수있는 보다 복잡한 방법, 특히 DestructuringDefault 파라미터로 인해 문제가되었습니다. 아래 코드를 살펴 보겠습니다.

1
2
3
4
5
function doSomething(first = this) {
"use strict";
return first;
}

여기서, 명명된 파라미터 first에는 Default 값으로 this가 할당됩니다. first의 값이 무엇이라고 기대 하시겠습니까? 이 경우, ECMAScript 6 사양은 strict 모드로 실행되는 파라미터를 처리하도록 JavaScript 엔진에 지시 했으므로 thisundefined와 같아야합니다. 그러나 strict 모드에서 실행중인 파라미터를 구현할 때 “use strict” 기능이 있는 경우 파라미터 Default값이 기능을 수행할 수 있기 때문에 매우 어려웠습니다. 이러한 어려움으로 인해 대부분의 JavaScript 엔진은 이 기능을 구현하지 못했습니다 (따라서 this는 전역 객체와 동일합니다).

구현의 어려움으로 인해 ECMAScript 2016은 파라미터가 Destructuring되거나 Default값을 갖는 함수 내부에 “use strict” 지시문을 사용하는 것을 구문 오류로 판단합니다. 함수의 본문에 “use strict”가 있는 경우 단순 파라미터 목록(simple parameter list), 즉 Destructuring 또는 Default값을 포함하지 않는 파라미터 목록만 허용됩니다. 아래 예제를 살펴보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// okay - using simple parameter list
function okay(first, second) {
"use strict";
return first;
}
// syntax error
function notOkay1(first, second=first) {
"use strict";
return first;
}
// syntax error
function notOkay2({ first, second }) {
"use strict";
return first;
}

단순 파라미터 목록에서 “use strict”를 사용할 수 있습니다. 따라서 okay()가 예상대로 작동합니다(ECMAScript 5에서와 동일). notOkay1() 함수는 Default 파라미터 값을 가진 함수라서 더 이상 “use strict”를 사용할 수 없으므로 구문 오류입니다. 비슷하게, notOkay2() 함수는 Destructuring 파라미터를 가진 함수라서 “use strict”를 사용할 수 없기 때문에 마찮가지로 구문 오류입니다.

전반적으로 이러한 변경으로 인해 JavaScript 개발자가 느끼는 혼란스러운 점과 JavaScript 엔진의 구현상 문제가 모두 제거됩니다.


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

참고

공유하기