Skip to main content

[JavaScript] ECMAScript 정리하기

· 15 min read

ECMAScript 정리하기

ECMAScript 2015 (ES6)

여기에 정리하지 않은 그 외의 ES6 문법들은 ES6 문법 정리를 참고

변수 선언 (let, const)

기존 var 키워드를 이용한 변수 선언의 단점을 보완하기 위해 letconst 키워드가 도입되었음

다음과 같이 사용하는 것을 추천한다고...

  • ES6를 사용한다면 var 키워드는 사용하지 않는다.
  • 변경이 발생하지 않는(재할당이 필요없는) primitive형 변수와 객체형 변수에는 const를 사용한다.
  • 재할당이 필요한 primitive형 변수에는 let를 사용한다.

let

  1. Block-level scope를 갖는다.
let foo = 123;
{
let foo = 456;
let bar = 456;
}
console.log(foo); // 123
console.log(bar); // ReferenceError: bar is not defined
  1. 중복 선언시 SyntaxError가 발생한다.
let bar = 123;
let bar = 456; // Uncaught SyntaxError: Identifier 'bar' has already been declared
  1. 호이스팅(Hoisting) let 키워드로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다. 즉, 스코프에 변수가 등록(선언단계)되지만 초기화 단계는 변수 선언문에 도달했을 때 이루어진다. 초기화 이전에 변수에 접근하려고 하면 ReferenceError 에러가 발생한다. 이는 변수가 아직 초기화되지 않았기 때문인데 스코프의 시작 지점부터 초기화 시작 지점까지를 일시적 사각지대(Temporal Dead Zone; TDZ)라고 부른다.

    호이스팅 var 선언문이나 function 선언문 등을 해당 스코프의 선두로 옮기는 것을 말함.

const

상수 선언을 위해 보통 사용하지만, 객체 타입 변수 선언을 위해서도 사용한다. 객체 타입 변수 선언에 const를 사용하는 경우의 장점은 다음과 같다.

  • 객체에 대한 참조는 변경될 필요가 없다. 즉, 재할당이 필요없다. 만일 새로운 객체에 대한 참조를 변수에 할당해야 한다면 새로운 변수를 사용하면 된다.
  • const를 사용한다 하더라도 객체의 프로퍼티를 변경할 수 있다.

템플릿 리터럴(template Literals)

다음과 같이 문자열을 정의하는 표기법을 의미한다.

  • 작은 따옴표와 큰 따옴표를 혼용하여 사용 가능
const template = `템플릿 리터럴은 '작은따옴표(single quotes)'과 "큰따옴표(double quotes)"를 혼용할 수 있다.`;

console.log(template);
  • 여러 줄에 걸쳐 문자열을 작성할 수 있음
const template = `<ul class="nav-items">
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
</ul>`;

console.log(template);
  • ${expression}과 같은 템플릿 대입문을 이용하여 문자열 대입 또는 javascript 표현식을 사용할 수 있다.
const first = 'Ung-mo';
const last = 'Lee';

// ES6 String Interpolation
console.log(`My name is ${first} ${last}.`); // My name is Ung-mo Lee.
// 템플릿 대입문에는 문자열뿐만 아니라 표현식도 사용할 수 있다.
console.log(`1 + 1 = ${1 + 1}`); // 1 + 1 = 2
console.log(`Hello ${last.toUpperCase()}`); // Hello LEE

Tagged Template Literals

함수 이름을 이용하여 작성하는 템플릿 리터럴이다.

function sides(literals, ...expressions) {
console.log(literals);
console.log(expressions);
}

let name = 'James';
let day = 'Morning';
sides`Hello, ${name}. Good ${day} !!`;

// ["Hello, ", ". Good ", " !!"]
// ["James", "Morning"]

위와 같이 호출하면 literals 변수에는 문자열을 expression을 기준으로 split 하여 배열로 넘겨준다. 그리고 expressions에는 표현식의 결과를 배열로 넘겨준다.

String.raw

특수문자와 유니코드는 문자열 그대로 인식하여 출력한다.

let one = 1,
two = 2;
console.log(String.raw`1+2=${one + two}\n 라인개행도 문자열로 표시된다.`);
// 1+2=3\n 라인개행도 문자열로 표시된다.
console.log(`1+2=${one + two}\n 라인개행도 문자열로 표시된다.`);
// 1+2=3
// 라인개행도 문자열로 표시된다.

위와 같은 template literals는 아래와 같은 정규식 문자열 생성 시에 유용하게 활용할 수 있다.

function createNumberRegExp(english) {
const PERIOD = english ? String.raw`\.` : '';
return new RegExp(`[0-9]+(${PERIOD}[0-9]+)?`);
}

Arrow function

Arrow function(화살표 함수)은 function 키워드 대신 화살표(=>)를 사용하여 간략한 방법으로 함수를 선언할 수 있다. 함수로 사용되는 경우에는 익명함수로만 사용할 수 있다.

// 매개변수 지정 방법
() => { ... } // 매개변수가 없을 경우
x => { ... } // 매개변수가 한개인 경우, 소괄호를 생략할 수 있다.
(x, y) => { ... } // 매개변수가 여러개인 경우

// 함수 몸체 지정 방법
x => { return x * x } // single line block
x => x * x // 함수 몸체가 한줄의 표현식이라면 중괄호를 생략할 수 있으며 자동으로 return된다. 위 표현과 동일하다.

() => { return { a: 1 }; }
() => ({ a: 1 }) // 위 표현과 동일하다. 객체 반환시 소괄호를 사용한다.

() => { // multi line block.
const x = 10;
return x * x;
};

Arrow function(화살표 함수)는 lexical this를 가진다.

// 출력결과 : Bob knows John, Brian
var bob = {
_name: 'Bob',
_friends: ['John, Brian'],
printFriends() {
this._friends.forEach((f) => console.log(this._name + ' knows ' + f));
},
};

Arrow Function을 사용해서는 안되는 경우

  1. 메소드 정의 (객체 내부, prototype 등에 메소드를 정의하는 경우)
  2. 생성자 함수
  3. addEventListener 함수의 콜백 함수 - 콜백함수 내부의 this가 상위 컨텍스트를 가리키게 된다.

Extended Parameter Handling

파라미터 초기값 설정

ES5에서는 별도로 파라미터 초기값을 설정할 수 없었으나, ES6 부터는 파라미터에 초기값을 설정할 수 있다.

// ES5
function plus(x, y) {
x = x || 0;
y = y || 0;
return x + y;
}

// ES6
function plus(x = 0, y = 0) {
return x + y;
}

Rest 파라미터 (Rest Parameter)

spread 연산자(...)을 사용하여 파라미터를 작성한 형태를 말하며, 반드시 마지막 파라미터로 정의되어야한다.

function foo(...rest) {
console.log(Array.isArray(rest)); // true
console.log(rest); // [ 1, 2, 3, 4, 5 ]
}

ES5에서는 파라미터를 통하여 가변인자 전달을 받기 위한 arguments 프로퍼티가 존재하였으며, 이 전달받은 인자를 배열 메서드로 사용하기 위해 다음과 같이 변환을 해주어야 했다.

// ES5
function sum() {
// 가변 인자 함수의 경우, 파라미터를 통해 인수를 전달받는 것이 불가능하므로 arguments 객체를 활용하여 인수를 전달받는다.
// arguments 객체를 배열로 변환
var array = Array.prototype.slice.call(arguments);
return array.reduce(function (pre, cur) {
return pre + cur;
});
}

console.log(sum(1, 2, 3, 4, 5)); // 15

하지만, ES6에서는 arguments 프로퍼티가 없으며, rest 파라미터를 사용하도록 가이드하고 있다.

Spread 연산자 (Spread Operator)

Spread 연산자(...)는 연산자의 대상 배열 또는 이터러블을 개별 요소로 분리한다.

// ...[1, 2, 3]는 [1, 2, 3]을 개별 요소로 분리한다(-> 1, 2, 3)
console.log(...[1, 2, 3]); // -> 1, 2, 3

// 문자열은 이터러블이다.
console.log(...'Helllo'); // H e l l l o

Enhanced Object Literals

프로퍼티 이름과 값을 단축하여 표현할 수 있다.

let obj = {
// __proto__
__proto__: theProtoObj,

// ‘handler: handler’의 단축 표기
handler,

// Methods
toString() {
// Super calls
return 'd ' + super.toString();
},

// Computed (dynamic) property names
['prop_' + (() => 42)()]: 42,
};

Classes

클래스 패턴을 이용하여 객체 지향 패턴을 더 쉽게 만들 수 있다. Constructor Pattern

ES6 이전.

function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
}
Car.prototype.toString = function () {
return this.model + ' has done ' + this.miles + ' miles';
};

var civic = new Car('Honda Civic', 2009, 20000);
var mondeo = new Car('Ford Mondeo', 2010, 5000);

console.log(civic.toString());
console.log(mondeo.toString());

ES6의 class 활용한 코드.

class Car {
constructor(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
}

toString() {
return this.model + ' has done ' + this.miles + ' miles';
}
}

Object Destructuring

변수의 선언과 분리하여 할당해야 하는 경우

let result = null;
try {
({ data: result } = await (isMovies
? moviesApi.movieDetail
: tvApi.showDetail)(id));
} catch {
//...
} finally {
console.log(result);
}

key에 해당하는 값이 없는 경우에 대한 default value 설정

let { a = 10, b = 5 } = { a: 3 };

console.log(a); // 3
console.log(b); // 5

새로운 변수명 선언과 함께 default value 설정

let { a: aa = 10, b: bb = 5 } = { a: 3 };

console.log(aa); // 3
console.log(bb); // 5

Reflect API

Reflect API는 객체의 속성과 메서드를 조사하고 조작하는 API이다.

일부 api는 브라우저에서 지원하지 않으므로 사용에 주의해야한다. 2019.03.28 기준

기존에는 어떤 메소드는 Object 객체를 통해서 접근하고, 어떤 메소드는 만들어진 객체를 통해서 접근했으나, Reflect를 이용하여 접근하면 메소드를 구분하지 않아도 된다.

ECMAScript 2016 (ES7)

제곱연산자

Math.pow(a, b)대신에 연산자를 이용하여 표현할 수도 있다.

const i = 3;
i ** 3; // 15
let i **=3; // 15

배열.includes(찾을 요소, 시작 순서)

indexOf 메소드와 동작이 유사하나 NaN에 대한 처리가 다르다.

[NaN].includes(NaN); // true
[NaN].indexOf(NaN) > -1; // false

ECMAScript 2017 (ES8)

String

문자열에 padding을 추가하기 위한 메소드 지원

문자열.padStart(최종길이, 보충문자열)

'zero'.padStart(10); // '      zero' (빈칸 6글자 + zero 4글자)
'zero'.padStart(10, 'babo'); // 'babobazero' (baboba 6글자 + zero 4글자)
'zero'.padStart(6, 'babo'); // 'bazero'
'zero'.padStart(3); // 'zero' (원래 길이보다 최종 길이가 작으면 원래 상태로)

문자열.padEnd(최종길이, 보충문자열)

'zero'.padEnd(10); // 'zero      '
'zero'.padEnd(10, 'babo'); // 'zerobaboba'
'zero'.padEnd(6, 'babo'); // 'zeroba'
'zero'.padEnd(3); // 'zero'

ECMAScript 2018 (ES9)

Promise finally

Promise의 성공/실패와 상관없이 무조건 실행된다.

Promise.resolve('hello')
.then((msg) => Promise.resolve(msg))
.finally(() => console.log('finally!'))
.then((msg) => console.log(msg)); // 콘솔에 finally와 hello가 찍힘

Async iteration

비동기 반복 처리에 대하여 아래와 같이 처리가 가능해짐.

(async () => {
const promises = ['1000', '2000', '3000'].map(
(timer) =>
new Promise((res, rej) => {
setTimeout(() => res(timer), timer);
}),
);
for await (const result of promises) {
console.log(result);
}
})();

정규표현식

s 플래그, lookbehind, 캡쳐링 그룹을 사용할 수 있다.

/zero.cho/s.test('zero\ncho') // true
/zero.cho/.test('zero\ncho') // false

const result = /(?<babo>바보)(?<name>제로초)/.exec('바보제로초');
result.groups.babo; // 바보
result.groups.name; // 제로초
result.groups[1]; // 바보
result.groups[2]; // 제로초

ECMAScript 2019 (ES10)

Object.fromEntries

Object.entries의 반대 기능이다.

Object.entries({ a: 'zerocho', b: ['hello'] }); // [['a', 'zerocho'], ['b', ['hello']]]
Object.fromEntries([
['a', 'zerocho'],
['b', ['hello']],
]); // { a: 'zerocho', b: ['hello'] }

Array.prototype.flat, Array.prototype.flatMap

다중 배열을 펼치는 기능이다.

let multi = [1, 2, 3, [4, 5, 6, [7, 8, 9, [10, 11, 12]]]];
multi.flat(); // [1,2,3,4,5,6,Array(4)]
multi.flat().flat(); // [1,2,3,4,5,6,7,8,9,Array(3)]
multi.flat().flat().flat(); // [1,2,3,4,5,6,7,8,9,10,11,12]
multi.flat(Infinity); // [1,2,3,4,5,6,7,8,9,10,11,12]

let array = [1, 2, 3, 4, 5];
array.map((x) => [x, x * 2]);
array.flatMap((v) => [v, v * 2]); // [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]

String.prototype.trimStart, trimEnd, trimLeft, trimRight

let greeting = '     Space around     ';
greeting.trimEnd(); // " Space around";
greeting.trimStart(); // "Space around ";

Optional Catch

try~catch 구문을 사용 시에 error 매개변수를 사용하지 않는 경우 생략이 가능하다.

try {
JSON.parse(text);
return true;
} catch {
return false;
}

Function.toString()

function sayHello(text) {
console.log(`Hello ${text}`);
}
console.log(sayHello.toString());
// function sayHello(text) { console.log(`Hello ${text}`) }

console.log(Number.parseInt.toString());
// function parseInt() { [native code] }

References