티스토리 뷰
let과 const
var
키워드를 주로 사용했다. var
키워드는 이름이 같은 변수를 중복 선언해도 오류가 발생하지 않는다.var
키워드는 함수 단위의 스코프만 지원한다. { }
로 묶여진 블록 내에서 선언한 변수는 별도의 스코프를 만들지 않는다는 것을 의미한다.let
키워드를 지원한다. 블록 단위의 스코프도 해결했고, 변수의 중복 선언을 방지할 수 있다.const
는 상수 기능을 제공한다. 즉 한 번 값이 주어지면 다시 변경할 수 없다. const
또한 블록 스코프를 제공한다.var
키워드는 중복 선언을 허용한다. 즉 아래 코드는 오류를 일으키지 않는다.var a = 100;
var a = 'hello';
var a = { name: '홍길동', age: 20 };
반면 let
과 const
는 중복 선언을 허용하지 않는다. 위 코드에서 var
를 let
으로 변경하면 오류가 발생한다.
기본 파라미터와 가변 파라미터
function addContact(name, mobile, home='없음', address='없음', email='없음') {
var str = `name=${name}, mobile=${mobile}, email=${email}`;
console.log(str);
}
addContact('홍길동', '010-222-3331');
addContact('이몽룡', '010-222-3332', '02-3322-9900', '서울시');
addContact
함수의 home
, address
, email
파라미터는 값을 전달하지 않을 경우 주어진 기본값이 할당된다. 위 코드의 경우는 각각 '없음'이라는 기본값이 할당된다.
가변 파라미터(Rest Parameter)는 여러 개의 파라미터 값을 배열로 받을 수 있도록 한다. 전달하는 파라미터의 개수는 가변적으로 적용할 수 있다.
function foodReport(name, age, ...favoriteFoods) {
console.log(name + ", " + age);
console.log(favoriteFoods);
}
foodReport("이몽룡", 20, "짜장면", "냉면", "불고기");
foodReport("홍길동", 16, "초밥");
파라미터의 앞 부분이 ...
으로 시작하는 favoriteFoods
가 가변 파라미터이다. 함수 호출시 가변 파라미터가 주어진 3번째부터 주어진 인자들은 favoriteFoods
에 배열 형태로 전달된다.
구조분해 할당(destructuring assignment)
let arr = [10, 20, 30, 40];
let [a1, a2, a3] = arr;
console.log(a1, a2, a3);
let p1 = { name: '홍길동', age: 20, gender: 'M' };
let { name: n, age: a, gender } = p1;
console.log(n, a, gender);
2행은 arr
의 배열 값을 순서대로 a1
, a2
, a3
변수에 각각 10, 20, 30을 할당한다. 6행의 코드에서는 p1
객체의 name
속성을 변수 n
에 할당하고 p1.age
를 변수 a
에 할당한다. p1
객체의 속성과 할당하려는 변수의 이름이 동일할 때는 변수명을 생략할 수 있다.
구조분해 할당은 함수의 파라미터에서도 사용할 수 있다.
function addContact({name, phone, email="없음", age=0}) {
console.log('이름 : ' + name);
console.log('전번 : ' + phone);
console.log('메일 : ' + email);
console.log('나이 : ' + age);
}
addContact({
name: "이몽룡",
phone: "010-3434-8989"
})
위 예제는 구조분해 할당과 기본 파라미터를 함께 사용했다. addContact
함수를 호출할 때 자바스크립트 객체를 파라미터 값으로 전달하고 있다. 전달된 객체는 구조분해 할당을 수행한다. 기본 파라미터에 해당하는 인자에 대한 객체의 속성이 존재하지 않을 경우 기본값이 할당된다. 이와 같이 객체로 파라미터 값을 전달하는 경우에는 파라미터의 전달 순서는 실행 결과에 영향을 주지 않는다.
화살표 함수(Arrow function)
this
를 그대로 전달받을 수 있다. 얼마나 간결한 표현식을 사용하는지 확인해보자. 아래 3개의 함수는 동일한 기능을 수행한다.var test1 = function(a, b) {
return a+b;
}
let test2 = (a, b) => {
return a+b;
}
let test3 = (a, b) => a+b;
console.log(test1(3,4));
console.log(test2(3,4));
console.log(test3(3,4));
하지만 주의할 점이 하나 있다. 바로 화살표 함수와 전통적인 함수는 서로 다른 this
값이 바인딩된다는 점이다. 우선 전통적인 함수를 사용했을 때를 살펴보자.
function Person(name, yearCount) {
this.name = name;
this.age = 0;
var incrAge = function() {
this.age++;
}
for (var i=1; i<=yearCount; i++) {
incrAge();
}
}
var p1 = new Person("홍길동", 20);
// -- 여기서 this.age는 0이 출력됨.
console.log(p1.name + "님의 나이 : " + p1.age);
위와 같이 Person
함수를 생성자를 이용해서 객체를 생성하면 Person
함수 안에서의 this
는 객체 p1
을 가리킨다. incrAge
함수는 Person
함수 안에 정의되어 있고, 반복문을 통해 반복적으로 호출되고 있는데, 간단히 생각하면 incrAge
함수 안에서의 this.age
는 함수를 둘러싸고 있는 환경의 this.age
가 전달될 것 같지만 그렇지 않으며 결과적으로 p1.age
값이 20이 될 것 같지만 그렇지 않다.
자바스크립트에서 this
는 호출하는 문맥에 의해 좌우된다. 문맥을 넘어서서 this
를 연결하려면 bind
, apply
, call
등의 함수 수준의 메서드를 이용해야 한다. 이 메서드들은 직접 this
를 연결할 수 있는 기능을 제공한다.
위 코드가 의도한대로 작동하도록 해결하기 위해서 다음과 같이 변경할 수 있다.
...
for (var i=1; i<=yearCount; i++) {
incrAge.apply(this);
}
...
함수 수준의 apply
메서드를 이용해 incrAge
함수를 둘러싸고 있는 영역의 this
를 incrAge
함수 내부의 this
로 강제 지정하는 것이다.
또는 같은 결과를 위해 아래 코드와 같이 바깥쪽 영역의 this
를 다른 변수에 할당하고 참조하는 방법을 사용할 수도 있다.
var outerThis = this;
var incrAge = function() {
outerThis.age++;
}
for (var i=1; i<=yearCount; i++) {
incrAge();
}
...
하지만 화살표 함수는 함수를 둘러싸고 있는 영역의 this
를 화살표 함수 내부에서 this
로 그대로 사용한다. 따라서 동일한 결과를 얻기 위해서 앞의 두 가지 방법처럼 코드를 변경하여 작성할 필요가 없이 아래 코드와 같이 작성할 수 있다.
function Person(name, yearCount){
this.name = name;
this.age = 0;
var incrAge = () => {
this.age++;
}
for (var i=1; i<=yearCount; i++) {
incrAge();
}
}
var p1 = new Person("홍길동", 20);
// --- 여기서 this.age는 20이 출력됨.
console.log(p1.name + "님의 나이 : " + p1.age);
새로운 객체 리터럴
var name = "홍길동";
var age = 20;
var email = "gdhhong@test.com";
// var obj = { name: name, age: age, email: email }; // 기존 표기법
var obj = { name, age, email }; // 속성명과 변수명이 같을 경우 개선된 표기법
console.log(obj);
이와 같이 객체를 생성할 때 변수 값을 객체의 속성으로 지정하는 경우, 위와 같이 속성 값을 생략하여 표기할 수 있다.
또한 아래 예제와 같이 새로운 메서드 표기법도 제공한다.
let p1 = {
name: '아이패드',
price: 200000,
quantity: 2,
order: function() { // 기존 메서드 표기법
if (!this.amount) {
this.amount = this.quantity * this.price;
}
console.log('주문금액 : ' + this.amount);
},
discount(rate) { // 새로운 메서드 표기법
if (rate > 0 && rate < 0.8) {
this.amount = (1 - rate) * this.price * this.quantity;
}
console.log((100 * rate) + '% 할인된 금액으로 구매합니다.');
}
}
p1.discount(0.2);
p1.order();
위 코드에서 ES2015가 제공하는 방식으로 작성된 discount()
메서드 부분을 보면 function
키워드를 사용하지 않고 바로 { }
구현부가 따라오는 것을 알 수 있다.템플릿 리터럴
`
)로 묶여진 문자열에서 템플릿 대입문(${}
)을 이용해 동적으로 문자열을 끼워넣어 구성할 수 있는 방법을 제공한다. 템플릿 대입문에는 수식, 구문, 변수, 함수 호출 구문 등 대부분의 표현식을 사용할 수 있다. 또한 템플릿 문자열은 개행 문자를 포함하여 여러 줄로 작성할 수 있다.var d1 = new Date();
var name = "홍길동";
var r1 = `${name}님에게 ${d1.toDateString() }에 연락했다.`;
console.log(r1));
var product = '갤럭시S7';
var price = 199000;
var str = `${product}의 가격은
${price}원 입니다.`;
console.log(str);
컬렉션
set
)이나 맵(map
) 형식의 데이터로 사용하기에는 불편함이 있다. ES2015에서는 Set
, Map
, WeakSet
, WeakMap
과 같은 집합, 맵을 제공하여 이런 불편함를 해소할 수 있다.var s1 = new Set();
s1.add('사과'); s1.add('배');
s1.add('사과'); s1.add('포도');
// 실행결과 : Set { '사과', '배', '포도' }
console.log(s1);
var john = new Set(['사과', '포도', '배']);
var susan = new Set(['파인애플', '키위', '배']);
// 합집합 : Set { '사과', '포도', '배', '파인애플', '키위' }
var union = new Set([...john.values(), ...susan.values()]);
console.log(union);
// 교집합 : Set { '배' }
var intersection = new Set([...john.values()].filter(e => susan.has(e)));
console.log(intersection);
// 차집합 : Set { '사과', ;'포도' }
var diff = new Set([...john.values()].filter(e => !susan.has(e)));
console.log(diff);
여러 개의 요소를 가진 집합을 초기화할 때는 Set
생성자 함수에 배열값을 인자로 전달하면 된다. 또한 교집합(Intersect), 합집합(Union), 차집합(Difference)을 연산하기 위해서는 배열의 기능을 활용한다. 교집합, 차집합의 경우는 배열의 filter
메서드를 이용했다.
맵(Map)은 키-값 쌍의 집합체이며, 키는 고유한 값이어야 한다.
let teams = new Map();
teams.set('LG', '트윈스'); teams.set('삼성', '라이온스');
teams.set('NC', '다이노스'); teams.set('기아', '타이거스');
teams.set('한화', '이글스'); teams.set('롯데', '자이언츠');
console.log(teams.has('SK')); // false
console.log(teams.get('LG')); // 트윈스
위 예제에서는 set()
, get()
, has()
메서드만 사용했다. 각각 값의 설정, 획득, 키의 존재 여부를 확인하는 메서드이다. 이 밖에도 clear()
, delete()
와 같은 메서드를 사용할 수 있다.클래스
class Person {
constructor(name, tel, address) {
this.name = name;
this.tel = tel;
this.address = address;
if (Person.count) { Person.count++; } else { Person.count = 1; }
}
static getPersonCount() {
return Person.count;
}
toString() {
return `name=${name}, tel=${tel}, address=${address}`;
}
}
var p1 = new Person('이몽룡', '010-111-2222', '경기도');
var p2 = new Person('홍길동', '010-333-4444', '서울');
console.log(p1.toString());
console.log(Person.getPersonCount());
다른 프로그래밍 언어의 클래스와 유사하게 생성자(Constructor), 정적 메서드(Static Methhod), 인스턴스 메서드(Instance Method)를 모두 잘 지원하고 있다.// 위 예제 코드에 이어서 작성...
class Employee extends Person {
constructor(name, tel, address, empno, dept) {
super(name, tel, address);
this.empno = empno;
this.dept = dept;
}
toString() {
return super.toString() + `, empno=${empno}, dept=${dept}`;
}
getEmpInfo() {
return `${this.empno} : ${this.name}은 ${this.dept} 부서입니다.`;
}
}
let e1 = new Employee('이몽룡', '010-222-1111', '서울시', 'A12311', '회계팀');
console.log(e1.getEmpInfo());
console.log(e1.toString());
console.log(Person.getPersonCount());
이 예제의 Employee
클래스는 Person
클래스로부터 상속 받았다. 기존 클래스의 기능들을 상속받아 사용하고, getEmpInfo()
와 같은 메서드를 추가하여 기능을 확장했다. 자바나 C#과 같은 객체지향프로그래밍을 다루어 본 경험이 있다면 익숙한 코드일 것이다.모듈
import
, export
구문을 이용해서 모듈을 가져오거나 내보낼 수 있다. export
해야 한다. export
된 모듈은 다른 모듈에서 import
구문으로 참조하여 사용할 수 있다. export
할 수 있는 대상은 변수, 함수, 객체, 클래스 등이며 다음과 같이 export
할 수 있다.export let a = 1000;
export function f1(a) { ... }
export { n1, n2 as othername, ... }
이와 같이 변수나 함수 등을 export
하기 위해서 export
키워드를 앞에 붙여주면 된다. 다른 방법으로는 일단 함수나 변수, 클래스를 작성한 다음 한 번에 export
하는 방법이 있다.
let var1 = 1000;
function add(a, b) {
return a+b;
}
export { var1, add };
이제 export
한 요소들을 import
해 보자. 파일 단위로 모듈을 생성하므로 파일의 경로를 지정하면 된다. .js 확장자는 생략될 수 있다.
import { add, var1 } from './utils/utility1';
console.log(add(4, 5);
console.log(var1;
import
할 때 주의 사항은 상대경로를 사용한다는 것이다. import
할 때 이름을 변경하고 싶다면 as
예약어를 사용한다. ...
import { add, var1 as v } from '.utils/utility1';
...
모듈 단위에서 export
하는 값이 여러 개인 경우를 위해서 { add, var1 }과 같이 import
했지만, 만일 export
하는 값이 단일 값, 단일 객체, 단일 함수, 단일 클래스라면 default
키워드를 이용해 export
한 후 단일 값으로 import
할 수 있다.
let calc = {
add(x, y) {
return x+y;
},
multiply(x, y) {
return x*y;
}
}
export default calc;
이 예제는 단일 객체를 export
하기 위해 default
를 사용했다. 단일 객체이므로 import
할 때 import { calc } from ...
와 같이 구조분해 할당(destructuring assignment)를 사용하지 않고 import calc from ...
와 같이 단일 객체로 가져올 수 있다.
import calc from './utils/utility2';
console.log(calc.add(4, 5);
console.log(calc.multiply(4, 5);
Promise
var p = new Promise(function(resolve, reject) {
setTimeout(function() {
var num = Math.round(Math.random()*20);
var isValid = num % 2;
if (isValid) { resolve(num); }
else { reject(num); }
}, 2000);
});
p.then(function(num) {
console.log('홀수 : ' + num);
}).catch(function(num) {
console.log('짝수 : ' + num);
});
console.log('20까지의 난수 중 홀수/짝수>');
console.log('결과는 2초 후에 나옵니다!!');
첫 행에서 Promise 객체를 생성할 때 전달하는 함수가 비동기로 실행된다. 첫 번째 인자로 전달된 resolve
함수를 호출하면 Promise 객체의 then
메서드에 등록된 함수가 호출되고 두 번째 인자로 전달된 reject
함수를 호출하면 Promise 객체의 catch
메서드에 등록된 함수가 호출된다. 이로써 비동기로 실행할 코드와 비동기 처리 결과를 받아 실행하는 코드를 분리할 수 있다.
전개 연산자(Spread Operator)
...
연산자를 살펴본 적이 있다. ...
연산자를 함수의 인자로 사용하면 가변 파라미터(Rest Parameter)라고 부른다. 가변 파라미터는 개별 값을 나열하여 함수의 인자로 전달하면 함수의 내부에서 배열로 사용할 수 있도록 한다. ...
연산자와 함께 객체 리터럴, 배열 리터럴에서 사용하면 분해된 값으로 전달한다. 아래 예제로 기능을 확인해 보자.let obj1 = { name: '박문수', age: 29 };
let obj2 = { ...obj1 };
let obj3 = { ...obj1, email: 'mspark@test.com' };
console.log(obj2);
console.log(obj3);
console.log(obj1 == obj2); // false
let arr1 = [ 100, 200, 300 ];
let arr2 = [ 'hello', ...arr1, 'world' ];
console.log(arr2);
위 예제의 결과를 확인해 보면 obj1 객체의 속성이 obj2에 복제된 것을 확인할 수 있다. obj1과 obj2는 동일한 속성 값을 가지고 있지만 서로 다른 객체이다. obj2는 새로운 객체를 만든 후 obj1의 속성 값들을 obj2의 속성의 값으로 할당했을 뿐이다.
obj3 을 정의한 코드를 보면 전개 연산자의 목적을 명확하게 이해할 수 있다. obj3은 obj1의 속성값을 모두 포함하면서 새로운 속성이 추가된 새로운 객체이다.
이와 같이 전개 연산자는 기존 객체의 속성이나 배열의 요소들을 포함하여 새로운 객체, 배열을 생성하고자 할 때 사용한다.
- Total
- Today
- Yesterday