러닝 리액트
2장 최신 자바스크립트
ES5의 내용은 아래 두 곳의 내용을 가져왔습니다.
ECMAscript 5의 feature : http://speakingjs.com/es5/ch25.html
ES5에 대해 간략히 알아보자.
3.0에 비해 5.0에서 실질적으로 추가된 문법은 strict mode와 Object리터럴 get-set추가, 문자열의 인덱스 인식등에 대한 부분이 변화 했습니다.
가장 중요한 특징은 객체에 대한 통제와 클래스를 생성하는 방법이 변화했다는 것입니다.문법적으로도 사용자 Object에 대해 getter, setter 지정이 가능해지므로 더 이상 평범한 값의 할당으로 작동하지 않고 내부에는 많은 제어문을 내포할 수 있는 형태가 되어 기존의 방식으로 문의 작동을 확정할 수 없습니다.
strict mode
파일의 가장 첫 줄에 해당 명시하게 되면, 함수의 일부의 기능들을 금지하고, 더 많은 검사, 더 많은 예외들을 통하여 더 깨끗한 JavaScript를 만드는 엄격한 사용한다고 명시한다.
'use strict';
Object literal get-set
getters
와 setters
을 활용하면, 메소드를 통해 프로퍼티에 접근하고, 수정하는 것을 구현 할 수 있다.
> var obj = { get foo() { return 'abc' } };
> obj.foo
'abc'
Syntactic Changes
ECMAScript 5 includes the following syntactic changes:
Reserved words as property keys
> var obj = { new: 'abc' };
> obj.new
'abc'Legal trailing commas
Trailing commas in object literals and array literals are legal.
Multiline string literals
String literals can span multiple lines if you escape the end of the line via a backslash.
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):
Access characters via the bracket operator
[...]
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):
JSON.parse()
(see JSON.parse(text, reviver?))JSON.stringify()
(see JSON.stringify(value, replacer?, space?))Some built-in objects have special
toJSON()
methods:Boolean.prototype.toJSON()
Number.prototype.toJSON()
String.prototype.toJSON()
Date.prototype.toJSON()
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의 this
가 window
객체이기 때문이다.
화살표 함수를 쓸 경우 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
...
세개로 이루어진 연산자로 몇 가지 다른 역할을 담당한다.
배열의 내용을 조합 할 수 있다.
원본 배열을 변경하지 않고, 복사본을 만들어 사용하게 된다.
배열뿐만 아니라 객체에도 사용 할 수 있다.
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(
'서울',
'수원',
'천안',
'대전',
'대구',
'부산'
)
//ES5
var seungdols = {
name: 'seungdols';
};
//ES6
var key = 'name';
var seungdols = {
[key] = 'seungdols';
};
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 모듈
모듈이란 다른 곳에서도 쉽게 불러서 활용할 수 있도록 하는 작은 코드 조각을 말한다. import
와 export
를 처리하는 라이브러리를 사용했었는데, 자체에서 지원하게 되었다.
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
export
나 export 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 declarationawait
is used during the promise handlingawait
must be used within anasync
function, though Chrome now supports "top level"await
async
functions return a promise, regardless of what thereturn
value is within the functionasync
/await
and promises are essentially the same under the hoodAvailable 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으로 작성 되며, 중첩되지 않는다. (가독성이 좋다는 뜻)
출처
'프로그래밍 > JavaScript' 카테고리의 다른 글
[정리] 러닝리액트 7장 컴포넌트 개선하기 (0) | 2018.06.05 |
---|---|
[정리] 러닝 리액트 3장 함수형 프로그래밍 (0) | 2018.05.10 |
Currying에 대해 알아보자. (1) | 2018.02.08 |
JavaScript Deferred 객체 Promise 패턴 (1) | 2017.04.07 |
jQuery의 data() 메소드의 캐싱이야기 (0) | 2017.02.26 |