꼭 알아야 할 ES6+ 문법

guava·2022년 2월 3일
1

파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트 강의를 듣고 정리한 글입니다.

리액트를 공부하기위해 필요한 ES6+ 문법을 정리해본다.

필수 문법


  1. 상수/변수 선언 (const/let)
  2. Object 선언, 단축 속성, key 계산, 객체 복사
  3. Template Literals
  4. 배열/객체 비구조화 (Array/Object Destructuring)
  5. 전개 연산자 (Spread Operator)
  6. 함수와 인자 (Functions, Parameters, Named Parameters)
  7. Arrow Functions
  8. Promise와 async/await
  9. 클래스와 상속
  10. 모듈 시스템
  11. 고차 함수 (High Order Function)

1. 상수 / 변수 선언


var 대신에 const 혹은 let을 사용 → block scope

  • const: 재할당 불가. 내부 속성은 수정 가능.
  • let: Lexical Variable Scoping을 지원하는 변수 선언 문법
var div;
var container = document.getElementsByTagName('body')[0];

for(let i=0; i<5; i++) {
  div = document.createElement('div’);
  div.onclick = function() {
    alert("clicked : #" + i); // var/let 사용 여부에 따라 alert 메시지가 달라집니다.
  };
  div.innerHTML = "#" + i;
  container.appendChild(div);
}

⇒ let으로 작성할 경우, i가 새로 선언되지만, var로 작성할 경우에는 하나의 변수를 공유하므로 실제 클릭 이벤트 시 무조건 5로 출력된다.

최근 트렌드는 const위주로, 간간히 let을 사용한다.

2. Object 선언


2.1. Object 선언

JavaScript

// tom1 / tom2는 동일
let tom1 = {
  name: "Tom",
  age: 10,
  region: "Seoul"
};

let tom2 = {
  "name": "Tom",
  "age": 10,
  "region": "Seoul"
};
  • Python
tom = {
    "name": "Tom",
    "age": 10,
    "region": "Seoul"
}

2.2. Key 계산이 필요한 경우

JavaScript

const tom2 = {
  "name": "Tom",
  "age": 10,
  "region": "Seoul",
  ["score" + "1"]: 100,
  // key는 "scope1" (Array가 아님)
};

const key1 = "location";
const tom = {
  name: "Tom",
  [key1]: "Seoul"
};

Python

tom = {
    "name": "Tom",
    "age": 10,
    "region": "Seoul",
    "score" + "1": 100,
}

key1 = "location"
tom = {
    "name": "Tom",
    key1: "Seoul"
}

2.3. 단축 속성명

let name = "Tom";
let age = 10;

let tom1 = { // 이와 같이 클래스 없이도 객체를 만들 수 있다. -> tom1.print()
  name: name,
  age: age,
  print: function() {
    console.log(`name: ${this.name}, age: ${this.age}`);
  }
};

let tom2 = {  // 위와 동일한 객체 -> tom2.print()
  name,
  age,
  print() {
    console.log(`name: ${this.name}, age: ${this.age}`);
  }
};

3. 객체 복사


JS는 Object/Array에 대해서는 대입시에 얕은 복사 (Shallow Copy)

t.js

const obj1 = { value1: 10 };
const obj2 = obj1; // 얕은 복사
const obj3 = JSON.parse(JSON.stringify(obj1))  // 깊은 복사

obj1.value1 += 1;

console.log(`obj1:`, obj1);
console.log(`obj2:`, obj2);
console.log(`obj3:`, obj3);
> node t.js
obj1: { value1: 11 }
obj2: { value1: 11 }
obj3: { value1: 10 }

⇒ obj2.value1도 변경되었음.

4. Template Literals


Multi-line string

String Interpolation

javascript

`string text ${expression}
string text`

python

"""string text
string text"""

f"""string text {expression}
string text"""

5. 배열 비구조화 (Array Destructuring)


리액트에서 자주 쓰는 문법

let [name] = ["Tom", 10, "Seoul"];

let [,age,] = ["Tom", 10, "Seoul"];

let [name, age, region, height] = ["Tom", 10, "Seoul"]
                       // height = undefined 할당 (파이썬에서는 ValueError 예외)
let [name, age, region, height=150] = ["Tom", 10, "Seoul"]
                       // height = 디폴트값 할당 (150)
function get_default_height() {
  console.log("get_default_height() 호출")
  return 150;
}

let [name, age, region, height=get_default_height()] = ["Tom", 10, "Seoul"]
                       // height에 실제 디폴트값 할당이 필요할때, get_default_height()가 호출됩니다.

6. 객체 비구조화 (Object Destructuring)


리액트에서 자주 쓰는 문법

const tom = {
  name: "Tom",
  age: 10,
  region: "Seoul"
};

// 아래와 같이 할 수 있지만,
const age = tom.age;
const name = tom.name;

// 다음과 같이 객체에서 필요한 값들만 뽑아냅니다. (리액트에서 정말 자주 쓰는 문법)
const {age, name, height}= tom; // height는 undefined

const print_person1 = (person) => {
  console.log(person.name);
};

const print_person2 = ({ name }) => {
  console.log(name);
};

print_person1(tom);
print_person2(tom);

const people = [
  { name: 'Tom', age: 10, region: 'Seoul' },
  { name: 'Steve', age: 12, region: 'Pusan' }
];

for (const person of people) {
  console.log(person.name, person.age);
}

for (const {name, age} of people) {
  console.log(name, age);
}

const person = {
  name: 'Tom',
  age: 10,
  region: {
    country: '서울',
    postcode: '06222',
  }
};

const { name, region: { postcode }} = person;  // region은 할당X

console.log(name, postcode);

7. 전개 연산자 (Spread Operator)


리액트에서는 수많은 값들을 불변객체로서 처리합니다 → 이때 전개연산자를 많이 쓰며, 구조가 복잡할 경우 immer 라이브러리를 쓰는 것이 코드 가독성에 도움이 됩니다.

let [name, ...rest] = ["Tom", 10, "Seoul"];

let names = ["Steve", "John"];
let students = ["Tom", ...names, ...names]; // ["tom", "steve", "john", "steve", "john"]

let printArgs = (...args) => {
  console.log(args);
}

let tom = {
  name: "Tom",
  age: 10,
  region: "Seoul"
};

let steve = {
  ...tom,
  name: "Steve" // 속성 명이 중복일 경우 마지막 값이 남습니다.
}; // {name: "Steve", age: 10, region: "Seoul"}

8. 함수 / Default Parameters


모든 타입의 값들을 디폴트 파라미터로 지정할 수 있습니다.

⇒ 파이썬에서는 Immutable값들만 디폴트 파라미터로 지정 가능

function hello(name="Tom", age=10) {
  console.log(`나는 ${name}. ${age}살이야.`)
}

const get_default_age = () => 10

function hello(name="Tom", age=get_default_age()) {
  console.log(`나는 ${name}. ${age}살이야.`);
}

console.log(hello("steve"));

[node/python 비교] 디폴트 값에 함수를 적용 할 경우

nodejs

function get_default_height() {
	console.log("get_default_height() 호출");
	return 150;
}

function say_hello(name, height=get_default_height()) {
	console.log(`name: ${name}, height: ${height}`);
}

say_hello("Tom", 160);
say_hello("John");
say_hello("Steve", 170);
say_hello("Suji");
> node t.js
name: Tom, height: 160
get_default_height() 호출
name: John, height: 150
name: Steve, height: 170
get_default_height() 호출
name: Suji, height: 150

python

def get_default_height():
    print("get_default_height() 호출")
    return 150

def say_hello(name, height=get_default_height()):
    print(f"name: {name}, height: {height}")

say_hello("Tom", 160)
say_hello("John")
say_hello("Steve", 170)
say_hello("Suji")
> python t.py
get_default_height() 호출
name: Tom, height: 160
name: John, height: 150
name: Steve, height: 170
name: Suji, height: 150

9. 함수 / Named Parameters


객체 비구조화를 활용

javascript

function print_person1(name, age, region) {
  console.log('1>', name, age, region)
}

print_person1('Tom', 10, 'Seoul');

function print_person2({ name, age, region }) {
  console.log('2>', name, age, region)
}

print_person2({ name: 'Tom', age: 10, region: 'Seoul' });

python

def print_person(name, age, region):
    print(name, age, region)

print_person('Tom', 10, 'Seoul')

print_person(name='Tom', age=10, region='Seoul')
# out -> ('Tom', 10, 'Seoul')
print_person(**{'name': 'tom', 'age': 10, 'region': 'seoul'})
# out -> ('tom', 10, 'seoul')

10. 함수 / Arrow Function


return을 사용하지 않아도, 계산된 값을 반환. 인자가 1개일 경우, 소괄호 생략 가능.

var hello1 = function(name, age) {
  return `안녕. 나는 ${name}. ${age}이야.`;
};

let hello2 = (name, age) => `안녕. 나는 ${name}. ${age}이야.`; // 함수를 중괄호로 감싸지 않으면, return문을 쓰지 않아도 반환값으로 사용됩니다.

let hello3 = (name, age) => {
  return `안녕. 나는 ${name}. ${age}이야.`;
}

this와 arguments를 바인딩하지 않습니다.

var tom = {
  name: "Tom",
  print1: function() {
    console.log(`[print1-1] name : ${this.name}`);
    (function() {
      console.log(`[print1-2] name : ${this.name}`); // 본래 새로운 function을 생성하면 this도 바뀐다.
    })();
  },
  print2: function() {
    console.log(`[print2-1] name : ${this.name}`);
    var me = this;
    (function() {
      console.log(`[print2-2] name : ${me.name}`); // 새로운 function을 생성하면 this도 바뀌기 때문에, me를 통해 this를 바인딩하였다. (꼼수!)
    })();
  },
  print3: function() {
    console.log(`[print3-1] name : ${this.name}`);
    (() => {
      console.log(`[print3-2] name : ${this.name}`);  // this가 변경되지 않았다!
    })();
  }
};

tom.print1();
tom.print2();
tom.print3();

실행 결과

[print1-1] name : Tom
[print1-2] name : undefined

[print2-1] name : Tom
[print2-2] name : Tom

[print3-1] name : Tom
[print3-2] name : Tom

11. 함수 / 다양한 형태


const mysum1 = (x, y) => x + y;  // 더한 값 반환
const mysum2 = (x, y) => ({x, y}); // 오브젝트 반환
const mysum3 = (x, y) => ({x: x, y: y});  // mysum2와 같음
const mysum4 = (x, y) => {
  return {x: x, y: y};
}

const mysum5 = function(x, y) {
  return {x: x, y: y};
};

function mysum6(x, y) {
  return {x: x, y: y};
}

12. 콜백 지옥 : callbackhell.com


비동기 프로그래밍을 위해 콜백(callback)을 많이 사용

fs.readdir(source, function (err, files) {
	if (err) {
		console.log('Error finding files: ' + err)
	} else {
		files.forEach(function (filename, fileIndex) {
			console.log(filename)
			gm(source + filename).size(function (err, values) {
				if (err) {
					console.log('Error identifying file size: ' + err)
				} else {
					console.log(filename + ' : ' + values)
					aspect = (values.width / values.height)
					widths.forEach(function (width, widthIndex) {
						height = Math.round(width / aspect)
						console.log('resizing ' + filename + 'to ' + height + 'x' + height)
						this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
							if (err) console.log('Error writing file: ' + err)
						})
					}.bind(this))
				}
			})
		})
	}
})

13. 콜백 → Promise → async/await


콜백

const fs = require('fs');

fs.readdir('.', function (err, files) {
	if (err) {
		console.log('Error finding files: ' + err)
	}
	else {
		console.log(files);
	}
});

// 위 fs.readdir이 끝나기 전에 수행
console.log("ENDED");

Promise

const fs = require('fs');
const fsPromises = fs.promises;

fsPromises.readdir('.')
	.then(files => { // 정상 처리
	console.log(files);
	})
	.catch(err => console.error(err)); // 에러 상황

// 위 fsPromises.readdir이 끝나기 전에 수행
console.log("ENDED");

async/await → ES8 (ECAM 2017)부터 지원

비동기 문법임에도 동기적으로 코드를 해석할 수 있다.

const fs = require('fs');
const fsPromises = fs.promises;

async function fn() {
	try {
		let files = await fsPromises.readdir('.');
		console.log(files);
	}
	catch(err) {
		console.error(err);
	}
}

fn(); // async 함수 이기에, 완료 전에 다음 로직이 동작

console.log("ENDED");

14. 클래스와 상속


ES6 이전

function Person(name, age) {
	this.name = name;
	this.age = age;
}

Person.prototype.print = function() {  // 멤버함수 정의
	console.log(this.name + ", " + this.age);
}

var tom = new Person("Tom", 10);
tom.print();

ES6 이후

class Person {
	constructor(name, age) {  // 생성자
		this.name = name;
		this.age = age;
	}

	print() {  // 멤버함수
		console.log(this.name + ", " + this.age);
	}
}

const tom = new Person("Tom", 10);
tom.print();

class Developer extends Person {
	constructor(name, age, field) {
		super(name, age);
		this.field = field;
	}
	print() {
		super.print();
		console.log(`field : ${this.field}`);
	}
}

⇒ 문법이 다를 뿐, 여전히 prototype을 사용합니다.

15. 모듈 시스템


  • 예전 웹 상의 자바스크립트에서는 html파일에서 script 태그를 통해서만 로딩 → 모두 전역 객체에 바인딩
  • 2가지 모듈 시스템
    1. CommonJS Module : nodejs에서 주로 사용
    2. ES6 Module : 리액트에서 주로 활용

ES6 module

  • React를 쓰실 때 사용할 모듈 시스템
  • IE를 포함한 구형 브라우저에서는 미지원
  • node 이후에 ES6 Module이 나왔기에, node에서는 왠만한 ES6문법은 지원하지만, 모듈은 ES6를 지원하지 않고, CommonJS만을 지원
  • 문법: export, export default, import ... from

추가:

CommonJS

  • node에서 지원하는 일반적인 모듈 패턴

모듈 정의하기

commonjs module

my_module.js : 모듈 정의

const name = "tom";
const age = 10;

module.exports = {
	name,
	age,
	region: "Seoul"
};

in_nodejs.js : 모듈 불러오기

const my_module = require("./my_module");
const { name } = require("./my_module");

console.log(my_module);
console.log(name);

es6 module

my_module_es6.js : 모듈 정의

const name = "tom";
const age = 10;

export default {
	name,
	age,
	region: "Seoul"
};

export {
	name,
};

in_react.js : 모듈 불러오기

import my_module from "./my_module"; // export default를 참조
import { name } from "./my_module"; // export를 참조

16. 고차 함수 (High Order Function)


함수를 인자로 받거나 반환이 가능하고, 다른 함수를 조작하는 함수.
함수/클래스 역시 모두 객체

javascript #1

function base_10(fn) {
  function wrap(x, y) {
    return fn(x, y) + 10;
  }
  return wrap;
}

function mysum(a, b) {
  return a + b;
}

mysum = base_10(mysum);

console.log(mysum(1, 2));

javascript #2

const base_10 = fn => (x, y) => fn(x, y) + 10;

let mysum = (a, b) => a + b;
mysum = base_10(mysum);

console.log(mysum(1, 2));

비교) python

def base_10(fn):
    def wrap(x, y):
        return fn(x, y) + 10
    return wrap

def mysum(x, y):
    return x + y

mysum = base_10(mysum)

print(mysum(1, 2))

17. 참고) ES6 (ECMAScript2015) 문법 더 살펴보기


https://babeljs.io/docs/en/learn

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/977d99e9-4254-4bcf-b5a6-c94da75eecd6/Untitled.png

0개의 댓글