프로그래밍/JavaScript

[정리] 러닝 리액트 - 2장 최신 자바스크립트

seungdols 2018. 4. 18. 00:31

러닝 리액트

2장 최신 자바스크립트

ES5의 내용은 아래 두 곳의 내용을 가져왔습니다.

ES5에 대해 간략히 알아보자.

3.0에 비해 5.0에서 실질적으로 추가된 문법은 strict mode와 Object리터럴 get-set추가, 문자열의 인덱스 인식등에 대한 부분이 변화 했습니다.

가장 중요한 특징은 객체에 대한 통제클래스를 생성하는 방법이 변화했다는 것입니다.문법적으로도 사용자 Object에 대해 getter, setter 지정이 가능해지므로 더 이상 평범한 값의 할당으로 작동하지 않고 내부에는 많은 제어문을 내포할 수 있는 형태가 되어 기존의 방식으로 문의 작동을 확정할 수 없습니다.

strict mode

파일의 가장 첫 줄에 해당 명시하게 되면, 함수의 일부의 기능들을 금지하고, 더 많은 검사, 더 많은 예외들을 통하여 더 깨끗한 JavaScript를 만드는 엄격한 사용한다고 명시한다.

'use strict';

Object literal get-set

getterssetters을 활용하면, 메소드를 통해 프로퍼티에 접근하고, 수정하는 것을 구현 할 수 있다.

> var obj = { get foo() { return 'abc' } };
> obj.foo
'abc'

Syntactic Changes

ECMAScript 5 includes the following syntactic changes:

New Functionality in the Standard Library

ECMAScript 5 brought several additions to JavaScript’s standard library. This section lists them by category.

Metaprogramming

Getting and setting prototypes (see Getting and Setting the Prototype):

  • Object.create()

  • Object.getPrototypeOf()

Managing property attributes via property descriptors (see Property Descriptors):

  • Object.defineProperty()

  • Object.defineProperties()

  • Object.create()

  • Object.getOwnPropertyDescriptor()

Listing properties (see Iteration and Detection of Properties):

  • Object.keys()

  • Object.getOwnPropertyNames()

Protecting objects (see Protecting Objects):

  • Object.preventExtensions()

  • Object.isExtensible()

  • Object.seal()

  • Object.isSealed()

  • Object.freeze()

  • Object.isFrozen()

New Function method (see Function.prototype.bind(thisValue, arg1?, ..., argN?)):

  • Function.prototype.bind()

New Methods

Strings (see Chapter 12):

New Array methods (see Array Prototype Methods):

  • Array.isArray()

  • Array.prototype.every()

  • Array.prototype.filter()

  • Array.prototype.forEach()

  • Array.prototype.indexOf()

  • Array.prototype.lastIndexOf()

  • Array.prototype.map()

  • Array.prototype.reduce()

  • Array.prototype.some()

New Date methods (see Date Prototype Methods):

  • Date.now()

  • Date.prototype.toISOString()

JSON

Support for JSON (see Chapter 22):

ES6에서 변수 선언

const 도입

var seungdols = '승호';
seungdols = 'seung ho'; //변경 가능
const seungdols = '승호';
seungdols = 'seung ho'; //재할당 불가능
let

lexical variable scoping 을 지원함으로써 글로벌 변수를 보호할 수 있게 된다.

원래는 {} 의 스코프를 따르지 않지만, ES6에서 let 키워드를 도입함으로써 if-else 에서 스코프의 범위를 줄일 수 있게 되었다.

var topic = 'javascript';

if(topic) {
let topic = 'react';
console.log('block', topic);
}
console.log('global', topic);

if 내부의 변수를 let 키워드로 설정함으로써 글로벌 변수인 topic을 보호 할 수 있다.

또 하나, for loop에서도 스코프를 축소하는데 활용할 수 있다.

템플릿 문자열

console.log(name + ', ' + nickname + ', ' + age);

console.log(`${name}, ${nickname}, ${age}`);

특히나, 템플릿 문자열은 공백을 유지해준다. 빈 칸뿐만 아니라 탭, 개행 문자등도 포함한다.

심지어 HTML코드 또한 집어 넣을 수 있다.

디폴트 파라미터

function hello(s='Hello') {
console.log(`${s}` world`);
}

물론 문자열만 가능한 것은 아니고, 디폴트로 지정할 수 있는 타입은 자바스크립트에서 사용할 수 있는 모든 타입이 가능하다. 즉, object도 가능하다.


var seungdols = {
name: {
first: 'seung ho',
last: 'choi'
},
nickname: 'seungdols'
};

function whoami(person=seungdols) {
console.log(`${person.name.first}는 별칭으로 ${person.nickname}을 사용합니다.`);
}
Arrow function

이를 사용하면 좋은 점은 function 키워드 없이도 함수를 만들 수 있으며, return을 사용하지 않아도 식을 계산한 값이 자동으로 반환 된다.

var programmer = function (language, name) {
return `${name}의 주력 언어는 ${language} 입니다.`;
}

var programmer = (language, name) => `${name}의 주력 언어는 ${language}입니다.`;

함수를 여러줄로 구성해야 한다면, => { }로 표현하면 된다.

추가적으로 화살표 함수는 this를 새로 바인딩 하지 않는다. 이 내용은 중요하다.

var gangwon = {
resorts: ['용평', '평창', '강촌', '강릉', '홍천'],
print: function(delay=1000) {
setTImeout(function() {
console.log(this.resorts.join(','));
}, delay);
}
}

gangwon.print(); // error

위 경우에는 this.resorts의 thiswindow 객체이기 때문이다.

화살표 함수를 쓸 경우 this 영역이 제대로 유지가 된다.

var gangwon = {
resorts: ['용평', '평창', '강촌', '강릉', '홍천'],
print: function(delay=1000) {
setTImeout(() => {
console.log(this.resorts.join(','));
}, delay);
}
}

gangwon.print();

그렇지만, print 프로퍼티를 화살표 함수로 바꾸면 다시 에러가 발생한다. 이유가 무엇일까?

ES6 트랜스파일링

모든 웹 브라우저가 Es6를 지원하는 것은 아니다. 그리고, 지원한다고 하더라도 ES6의 모든 기능을 지원하는 것은 아니다. 그래서 ES5로 컴파일을 하여 모든 브라우저가 동작할 수 있도록 하는 것이 필요로 하다. 이러한 변환을 트랜스파일링이라고 부른다.

브라우저에서 바로 트랜스 파일링을 할 수도 있으나, 프로덕션에서는 추천하는 방법은 아니다. 프로덕션에서는 빌드 툴을 이용해 바벨 트랜스 파일링을 한 파일로 서비스를 하게 될 것이다.

단점으로는 babel은 컴파일 시점에 문법을 변환해주는 것이지만, 모든 문법을 변환해주는 것은 아니다. polyfill은 프로그램이 처음에 시작될 때 현재 브라우저에서 지원하지 않는 함수를 검사해서 각 object의 prototype에 붙여주는 역할을 한다. 즉, babel은 컴파일-타임에 실행되고 babel-polyfill은 런-타임에 실행된다. 고로, babel-polyfill도 필요로 할 수 있다.

const add = (x=5, y=10) => console.log(x+y)

위 내용을 babel로 변환하게 되면, 아래의 내용으로 변환된다.

"use strict";

var add = function add() {
var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 5;
var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10;
return console.log(x + y);
};

ES6 객체와 배열

구조 분해를 사용한 대입

구조분해 (destructuring)를 사용하면, 객체 안에 있는 필드 값을 원하는 변수에 대입할 수 있다.

var seungdols = {
name: 'seungdols',
age: 28,
company: 'Naver',
role: 'web programmer'
}

var {name, role} = seungdols;

console.log(`${name}, ${role}`);

두 변수의 값은 seungdols에 있는 같은 이름의 필드 값으로 초기화 되지만, 두 변수를 변경해도 원래의 필드 값을 변경 되지 않는다.

추가적으로 객체를 분해해서 함수의 인자로 넘겨줄 수 있다.

배열을 구조 분해해서 원소의 값을 변수에 대입할 수도 있다. 배열의 첫 번째 원소를 변수에 대입하고 싶다고 해보자.

var [first] = ['용평', '평창', '강릉'];

console.log(first);

var [,,third] = ['용평', '평창', '강릉'];
console.log(third);

콤마를 사용하여, list matching을 사용할 수도 있다.

Object literal enhancement

구조 분해와는 반대라고 이해하면 쉽다. 현재 영역에 있는 변수를 객체의 필드로 묶을 수 있다.

var name = 'seungdols';
var age = '28';
var print = function() {
console.log(`${this.name}, ${this.age}`);
}
var seungdolsObj = {name, age};

console.log(seungdolsObj);

당연히 메소드 또한 객체 메소드로 묶을 수 있다. 다만, 객체의 필드에 접근하기 위해 this를 사용했다는 사실을 기억해야 한다. 그러므로, 앞으로는 객체 메소드를 정의할때, function 키워드를 입력하지 않아도 된다.

//old 

var seungdols = {
name: name,
age: age,
print: function() {
//
},
hello: function(name) {
//
}
}

//new
const seungdols = {
name,
age,
print() {
//
},
hello(name) {

}
}

spread operator

... 세개로 이루어진 연산자로 몇 가지 다른 역할을 담당한다.

  1. 배열의 내용을 조합 할 수 있다.

  2. 원본 배열을 변경하지 않고, 복사본을 만들어 사용하게 된다.

  3. 배열뿐만 아니라 객체에도 사용 할 수 있다.


var company = ['kakao', 'naver', 'facebook'];
var person = ['seungdols', 'seung ho', 'seung ho choi'];
var person_company = [...company, ...person];


//스프레드 연산자로 배열의 나머지 원소를 얻을 수 있음.
var [first, ...rest] = company;

스프레드 연산자를 이용해 함수의 인자를 배열로 모을 수 있다.

function directions(...args) {
var [start, ...remaining] = args;
var [finish, ...stops] = remaining.reverse();

console.log(`${args.length} 도시를 운행합니다. `);
console.log(`${start}에서 출발합니다. `);
console.log(`목적지는 ${finish}입니다. `);
console.log(`중간에 ${stops.length}군데 경유합니다. `);

}

directions(
'서울',
'수원',
'천안',
'대전',
'대구',
'부산'
)

Computed property name

//ES5
var seungdols = {
name: 'seungdols';
};

//ES6
var key = 'name';
var seungdols = {
[key] = 'seungdols';
};


class

class validation {

construnctor(destination, length) {
this.destination = destination,
this.length = length
}
}

클래스가 ES6에서 추가가 되었으나, 실질적인 동작은 기존 방식으로 동작한다. 결국은 함수를 정의하고, 그 함수 객체에 프로토 타입을 사용해 메서드를 사용하는 기존 방식과 차이가 없다. 하지만, 문법에서 지원하여 가독성 측면에서 향상 된 점을 생각하면 나름의 장족의 발전이라고 생각한다.

class를 정의하고 나면, new 키워드를 이용해 해당 클래스의 새로운 인스턴스를 만들 수 있다. 그리고 확장도 가능하며, extends 키워드를 이용해 클래스를 확장 할 수 있다.

ES6 모듈

모듈이란 다른 곳에서도 쉽게 불러서 활용할 수 있도록 하는 작은 코드 조각을 말한다. importexport를 처리하는 라이브러리를 사용했었는데, 자체에서 지원하게 되었다.

export const print(message) => log(message, new Date());

export const log(message, timestamp) => console.log(`${timestamp.toString()}: ${message}`);

모듈에서 단 하나의 이름만 외부에 export하고 싶을 때에는 export default를 사용한다.

const seungdols(msg) => console.log(`Hello, ${msg}`);

export default seungdols

exportexport default에서는 기본 타입, 객체, 배열, 함수 등 모든 타입의 자바스크립트 이름을 외부에 노출시킬 수 있다.

모듈은 import명령을 사용해 다른 자바스크립트 파일을 불러와 사용할 수 있다. 외부에 여러 이름을 노출한 모듈을 불러올 때는 객체 구조 분해를 활용할 수 있다.

import {print , log} from './test-export'
import seungdols from './seungdols'

print('Hello, seungdols')
seungdols('seungdols')

import *를 사용하면 다른 모듈에서 가져온 모든 이름을 사용자가 정한 로컬 이름 공간 안에 가둘 수 있다.

import * as test from './test-export'

CommonJS

모든 버전의 노드에서 지원하는 일반적인 패턴을 말하며, module.exports를 사용해 아래처럼 할 수 있다.


const print(msg) => log(msg, new Date());

const log(msg,timestamp) => console.log(`${timestamp.toString()}: ${msg}`);

module.exports = {print, log}


//외부

const {print, log} = require('./test-exprt');

CommonJS에서는 import 키워드를 지원하지 않기에 require 키워드를 이용해 모듈을 가져온다.

ES7 and ES8 주요 feature

아래의 내용은 하기의 페이지에서 발췌하였습니다.

String.prototype.padStart/padEnd

// padStart(desiredLength, textToPrepend)

// No text
''.padStart(10, 'Hi') // 'HiHiHiHiHi'

// Some text
'def'.padStart(6, 'abc') // 'abcdef'

// Only use what gets to length
'5678'.padStart(7, '1234') // '1235678'

// padEnd(desiredLength, textToAppend)

'23'.padEnd(8, '0') // '23000000'

Object.entries

// Object literal
Object.entries({ 'a': 'A', 'b': 'B' }); // [["a","A"],["b","B"]]

// String
Object.entries('david') // [["0","d"],["1","a"],["2","v"],["3","i"],["4","d"]]

Object.values

// Object literal
Object.values({ 'a': 23, 'b': 19 }) // [23, 19]

// Array-like object (order not preserved)
Object.values({ 80: 'eighty', 0: 1, 1: 'yes' }) // [1, 'yes', 'eighty']

// String
Object.values('davidwalsh') // ["d", "a", "v", "i", "d", "w", "a", "l", "s", "h"]

// Array
Object.values([1, 2, 3]) // [1, 2, 3]

Array.prototype.includes

['a', 'b', 'c'].includes('a') // true, not 0 like indexOf would give
['a', 'b', 'c'].includes('d') // false

Exponentiation

// 2 to the power of 8
Math.pow(2, 8) // 256

// ..becomes
2 ** 8 // 256

Trailing Commas

let myObj = { a:'b', b: 'c', } // No error!

let myArr = [1, 2, 3, ] // No error!

[1, 2, 3,].length // 3
[1, 2, 3, , , ].length // 5

async/await

  • async is a keyword for the function declaration

  • await is used during the promise handling

  • await must be used within an async function, though Chrome now supports "top level" await

  • async functions return a promise, regardless of what the return value is within the function

  • async/await and promises are essentially the same under the hood

  • Available now in most browsers as well as Node.js

// Function declared as async so await can be used
async function fetchContent() {
// Instead of using fetch().then, use await
let content = await fetch('/');
let text = await content.text();

// Inside the async function text is the request body
console.log(text);

// Resolve this async function with the text
return text;
}

// Use the async function
var promise = fetchContent().then(...);

async/await의 장점

  • 소스 코드가 간결해진다.

  • 콜백헬 보다 디버깅이 간편하다.

  • 이전에 작성한 promise then / catch 코드를 변경하기 쉽다.

  • 소스코드가 top-down으로 작성 되며, 중첩되지 않는다. (가독성이 좋다는 뜻)

출처


반응형