2019-07-05 ES6(1) - const,let, 템플릿문자열, 객체리터럴 확장
var의 특징
- 함수레벨 스코프
- 함수의 코드 블록만 스코프로 인정.
- for 문의 변수 선언문에서 선언한 변수를 for문의 코드 블록 외부에서 참조가능….
- var키워드 생략가능 : 암묵적 전역변수 양산가능
- 변수 중복 선언 허용 : 의도치않은 변수값 변경
- 변수 호이스팅 : 변수선언 이전에 참조가능.
대부분의 문제는 전역변수로 인해 발생한다. 전역변수는 유효범위(scope)가 넓어서 어떻게 사용되는지 파악하기 힘들다. ES6에서는 let, const의 도입을 했다.
let
- 대부분의 언어는 블록레벨 스코프를 따르지만 JS는 함수레벨 스코프를 사용했다.
함수레벨 스코프 : 함수 내에 선언된 변수는 함수내에서만 유효. 함수 외부에서 선언된 변수는 모두 전역변수이다. 블랙레벨 스코프 : 모든 코드블록(함수,if문, for문) 내에서 선언된 변수는 코드블록 내에서만 유효. 코드블록 외부에서 참조불가. 코드블록 내부에서 선언된 변수는 지역변수이다.
// Functional Scope
var foo = 123; // 전역 변수
console.log(foo); // 123
{
var foo = 456; // 전역 변수
}
console.log(foo); // 456
// Block Scope
let foo = 123; // 전역 변수
{
let foo = 456; // 지역 변수
let bar = 456; // 지역 변수
}
console.log(foo); // 123
console.log(bar); // ReferenceError: bar is not defined
- let은 블록스코프라 bar가 지역변수로 취급이 된다. 따라서 error가 발생.
변수 중복 금지
var foo = 123;
var foo = 456; // 중복 선언 허용
let bar = 123;
let bar = 456; // Uncaught SyntaxError: Identifier 'bar' has already been declared
- var는 변수 중복이 되지만 let은 안된다.
호이스팅
console.log(foo);
var foo;
console.log(bar); // Error : Uncaught ReferenceError : bar is not defined
let bar;
- JS는 ES6에서 도입된 let, const를 포함하여 모든 선언(var, let, const, function, function*, class)를 호이스팅한다. 하지만 let키워드로 선언된 변수는 선언문 이전에 참조하면 에러가 발생한다.
변수생성단계
선언단계 : 변수를 실행컨텍스트에 등록한다. 변수객체는 스코프가 참조하는 대상이 된다. 초기화단계 : 변수객체(Variable Object)에 등록된 변수를 위한 메모리를 확보. 이때 변수는 undefined로 초기화 할당단계 : undefined로 초기화된 변수에 실제값을 할당한다.
- var로 선언된 변수는 선언, 초기화단계가 한번에 이루어진다. 그렇기때문에 선언문 이전에 변수를 접근하여도 스코프에 변수가 존재해서 undefined라서 에러가 발생하지 않는다. 이후 변수할당문에 도달하면 값이 할당된다. 이를 변수호이스팅
/ 스코프의 선두에서 선언 단계와 초기화 단계가 실행된다.
// 따라서 변수 선언문 이전에 변수를 참조할 수 있다.
console.log(foo); // undefined
var foo;
console.log(foo); // undefined
foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1
- let은 선언과 초기화가 분리되서 진행된다. 스코프에 변수를 등록하지만 초기화 단계는 변수 선언문에 도달했을 때 이루어진다. 초기화 이전에 변수에 접근하면 참조에러가 발생.
let foo = 1;
{
console.log(foo) // ReferenceError
let foo = 2; // local variable
}
전역객체와 let
- 전역객체(Global Object)는 모든 객체의 최상위 객체이다. 브라우저에서는 Window, Server-Side(Node.js)에서는 global객체를 의미한다.
- var키워드로 선언된 변수를 전역 변수로 사용하면 전역 객체의 프로퍼티가 된다.
var foo = 123;
console.log(window.foo); // 123
let bar = 123;
console.log(window.bar) // undefined;
- let 전역변수는 전역 객체의 프로퍼티가 아니다. window.foo처럼 접근할 수 없다.
- 번외 nodejs global.foo, global.bar는 undefined이다.
const
- cosnt는 상수로 보통사용하지만 다른 용도로도 사용한다.
- let은 재할당이 되지만 const는 재할당이 안된다.
- const는 반드시 할당과 선언이 같이 이루어져야한다.
const FOO = 123;
FOO = 456; // TypeError: Assignment to constant variable.
const FOO; // SyntaxError: Missing initializer in const declaration
const와 객체
- const변수 타입이 객체인 경우, 객체에 대한 참조를 변경 못한다. 재할당은 불가능하지만, 할당된 객체의 내용(프로퍼티의 추가, 삭제, 프로퍼티값의 변경)은 변경할 수 있다.
const user = { name: 'Lee' };
// const 변수는 재할당이 금지된다.
// user = {}; // TypeError: Assignment to constant variable.
// 객체의 내용은 변경할 수 있다.
user.name = 'Kim';
console.log(user); // { name: 'Kim' }
- 객체 내용이 변경되도, 주소값은 변하지 않는다.
- 객체 타입 변수선언에는 const가 좋다.
템플릿 리터럴
- 템플릿리터럴은 백틱(`)을 쓴다.
- 문자열에서 공백을 표현하기 위해 \를 사용하는데, 템플릿 리터럴 내의 모든 white-space는 있는 그대로 적용이 된다.
const template = `<ul class="nav-items">
<li><a href="#home">Home</a></li>
<li><a href="#news">News</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#about">About</a></li>
</ul>`;
console.log(template);
- +연산자를 사용하지 않아도 새로운 문자열 삽입이 된다.
const first = 'Ung-mo';
const last = 'Lee';
// ES5: 문자열 연결
console.log('My name is ' + first + ' ' + last + '.');
// "My name is Ung-mo Lee."
// ES6: String Interpolation
console.log(`My name is ${first} ${last}.`);
// "My name is Ung-mo Lee."
객체리터럴 확장
//ES5
var x = 1, y = 2;
var obj = {
x: x,
y: y
};
console.log(obj); // { x: 1, y: 2 }
// ES6
let x = 1, y = 2;
const obj = {x, y};
console.log(obj);
// 객체 리터럴 var로도 적용되는지 확인
var a = 11, b = 22;
var obj2 = { a, b};
console.log(obj2);
// result
{ x: 1, y: 2 }
{ a: 11, b: 22
- ES6에서 프로퍼티 값으로 변수를 사용하는 경우, 프로퍼티 이름을 생략할 수 있다. 이때 프로퍼티 이름은 변수의 이름으로 자동생성된다.
프로퍼티키 동적 생성
- 문자열 or 문자열로 변환 가능한 값을 반환하는 표현식을 사용해 프로퍼티 키를 동적으로 사용할 수 있다. 단 표현식을 대괄호로 묶어야한다.
// ES5
var prefix = 'prop';
var i = 0;
var obj = {};
obj[prefix + '-' + ++i] = i;
obj[prefix + '-' + ++i] = i;
obj[prefix + '-' + ++i] = i;
// ES6
const prefix = 'prop';
let i = 0;
const obj = {
[`${prefix}-${++i}`]: i,
[`${prefix}-${++i}`]: i,
[`${prefix}-${++i}`]: i
};
console.log(obj); // {prop-1: 1, prop-2: 2, prop-3: 3}
메소드 축약표현
// ES5
var obj = {
name: 'Lee',
sayHi: function() {
console.log('Hi! ' + this.name);
}
};
obj.sayHi(); // Hi! Lee
// ES6
const obj = {
name: 'Lee',
// 메소드 축약 표현
sayHi() {
console.log('Hi! ' + this.name);
}
};
obj.sayHi(); // Hi! Lee
- ES6에서는 메소드를 선언할 때, function 키워드를 생략한 축약 표현을 사용할 수 있다.
__proto__ 프로퍼티에 의한 상속
- es5에서 객체 리터럴을 상속하기 위해서는 Object.create()함수를 사용했다. 이를 프로토타입 패턴 상속이라 한다.
// ES5
var parent = {
name: 'parent',
sayHi: function() {
console.log('Hi! ' + this.name);
}
};
// 프로토타입 패턴 상속
var child = Object.create(parent);
child.name = 'child';
parent.sayHi(); // Hi! parent
child.sayHi(); // Hi! child
- es6에서는 객체 리터럴 내부에서 __proto__를 직접 설정할 수 있다. 객체리터럴에 의해 생성된 객체의 __proto__프로퍼티에 다른 객체를 직접 바인딩하여 상속을 표현할 수 있다.
// ES6
const parent = {
name: 'parent',
sayHi() {
console.log('Hi! ' + this.name);
}
};
const child = {
// child 객체의 프로토타입 객체에 parent 객체를 바인딩하여 상속을 구현한다.
__proto__: parent,
name: 'child'
};
parent.sayHi(); // Hi! parent
child.sayHi(); // Hi! child
오늘의 느낀점
JS가 깊어짐에 따라 축약이나 새로운 문법 등 다양한 어려움에 부딪치고 있다. 깊이 원리를 이해하기 위해 기초를 계속 보고 있다. 조금씩 조금씩 꾸준히 나아가자. 불안하더라도 묵묵히 나아가자 Keep Pushing :)
Written on July 5, 2019