민한의 블로그

es2015+ 정리 본문

728x90
반응형




Const, let


ES2015 이전에서 변수를 선언할 수 있는 유일한 방법은 var 키워드를 사용하는 것이었다. var 키워드로 선언된 변수는 아래와 같은 특징이 있다. 이는 다른 언어와는 차별되는 특징(설계상 오류)으로 주의를 기울이지 않으면 심각한 문제를 일으킨다.

  1. 함수 레벨 스코프

    • 전역 변수의 남발
    • for loop 초기화식에서 사용한 변수를 for loop 외부 또는 전역에서 참조할 수 있다.
  2. var 키워드 생략 허용
    • 의도하지 않은 변수의 전역화
  3. 중복 선언 허용
    • 의도하지 않은 변수값 변경
  4.  변수 호이스팅

    • 변수를 선언하기 전에 참조가 가능하다.

대부분의 문제는 전역 변수로 인해 발생한다. 전역 변수는 간단한 애플리케이션의 경우, 사용이 편리하다는 장점이 있지만 불가피한 상황을 제외하고 사용을 억제해야 한다. 전역 변수는 유효 범위(scope)가 넓어서 어디에서 어떻게 사용될 것인지 파악하기 힘들며, 비순수함수에 의해 의도하지 않게 변경될 수도 있어서 복잡성을 증가시키는 원인이 된다. 따라서 변수의 유효 범위(scope)는 좁을수록 좋다.

ES6는 이러한 var의 단점을 보완하기 위해 let과 const 키워드를 도입하였다.


//두가지 방식의 차이

var : 함수의 범위 (function scope)를 가짐

하나의 함수내에서는 적용범위가 유효하게 된다.


let, const : 블록단위(block scope)의 범위를 가지고 있음

{}로 감싸고 있는 범위내에서 유효하다.


1
2
3
4
5
6
7
8
9
if(true){
    var x = 3;
}
console.log(x); //3
 
if(true){
    const y =3;
}
console.log(y); //Uncaught ReferenceError : y is not defined
cs












var는 함수 스코프를 가지고 있으므로 if문의 블록과 상관이 없으며, 전역변수로 선언 및 할당 되었습니다.

하지만 const 는 블록 스코프를 가지고 있으므로 if문의 블록에서의 지역변수로 사용되며, if문 밖에서는 접근을 할수 없습니다.

if, while, for, function, {} 등의 중괄호는 블록 범위입니다. 

함수 스코프 대신 블록 스코프를 사용 함으로써 호이스팅, this 바인딩 등의 문제가 해결되며, 코드관리도 수월해 졌습니다.


var 는 재선언이 가능하지만, let, const는 재선언이 불가능합니다.

const와 let의 차이점은 값을 할당한 후에 변경의 가 불가 여부입니다.



*************************************************************************


const 는 상수 (변하지 않는 수, 하지만 참조형 데이터는 변화가 가능)

let 는 변수 (변할수 있는수)


원시형(Primitives type: string, number, boolean, null, undefined)에서 const는 상수로 동작합니다. 따라서 const 로 선언되면 값을 재할당 할 경우 에러가 발생합니다

1
2
3
4
let aoo = 0;
aoo=1;
const boo = 0;
boo = 1;
cs



단순형의 경우 값의 변경이 있는 경우에는 let으로, 상수로 사용하는 경우에는 const로 선언하는 것이 바람직하겠습니다.하지만, 참조형(Complex type: array, object, function)의 경우 결론부터 말씀드리면 const 로 선언하는 것이 바람직합니다. 참조형은 const로 선언하더라도 멤버값을 조작하는 것이 가능합니다.

1
2
3
4
5
6
7
const foo = [01];
const bar = foo;
 
foo.push(2);
bar[0= 10;
 
console.log(foo, bar)
cs



1
2
3
4
const a = 0;
a  = 1;
let b = 0;
=1;
cs


자바스크립트를 사용할때 한번 초기화했던 변수에 다른 값을 할당하는 경우는 생각보다 적습니다.

따라서 기본적으로 변수 선언시에는 const를 사용하고, 다른값을 대입해야 하는 상황이 생겼을때 let을 사용하면 됩니다.



*************************************************************************


1
2
3
4
5
6
7
var messages = ["Meow!""I'm a talking cat!""Callbacks are fun!"];
 
for (var i = 0; i < messages.length; i++) {
  setTimeout(function () {
    alert(messages[i]);
  }, i * 1500);
}

cs









어떤 값이 나올지는 드래그 하면됩니다!{이 코드의 문제는 var i 를 전역변수로써 공통되게 하나로 공유하고있다.

변수 i는 루프에서 공유될 뿐만 아니라, 세번의 콜백호출에서도 공유하고있다.

루프가 종료될때, 변수 i의 값은 3(messages.length의 값이 2이라서 for 문이 멈추게됩니다.)이고, 값이 3이 될때까지 콜백함수는 호출되지 않았다.

그리고 messages[3] 은 undefined 입니다.

}



기존에 var를 사용할시 이 상황을 원하는 값으로 만들고 싶을때 

1
2
3
4
5
6
7
  var messages = ["Meow!""I'm a talking cat!""Callbacks are fun!"];
 
  messages.forEach(function (message, i) {
    setTimeout(function () {
      cat.say(message);
    }, i * 1500);
  });
cs









forEach 의 파라미터중 index 값을 사용했어야 했다.





1
2
3
4
5
6
7
let messages = ["Meow!""I'm a talking cat!""Callbacks are fun!"];
 
for (let i = 0; i < messages.length; i++) {
  setTimeout(function () {
    alert(messages[i]);
  }, i * 1500);
}
cs










let을 사용하면, 상단의 코드와는 다른값이 나온다.



for (let x...) 형태의 루프는 루프를 반복할 때마다 매번 x 변수를 새로 바인딩합  니다.

이것은 아주 사소한 차이입니다. 하지만 이것은 만약 for (let...) 루프를 실행할 경우, 그리고 말하는 고양이 예제처럼 해당 루프가 클로져(closure)를 포함하고 있을 경우, 각 클로져가 서로 다른 루프 변수를 참조한다는 것을 의미합니다. 모든 클로져가 같은 변수를 참조하는 지금과는 다르게 말이죠.

그래서 말하는 고양이 예제의 경우도 단지 var 를 let 으로 바꾸기만 하면 문제가 해결됩니다.

이것은 3가지 종류의 for 루프 모두에 적용됩니다. forofforin, 그리고 세미콜론을 함께 쓰는 C 언어 스타일의 for 루프 모두에 적용됩니다.



*************************************************************************


자바스크립트는 ES6에서 도입된 let, const를 포함하여 모든 선언(var, let, const, function, function*, class)을 호이스팅한다. 호이스팅(Hoisting)이란, var 선언문이나 function 선언문 등을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성을 말한다.

하지만 var 키워드로 선언된 변수와는 달리 let 키워드로 선언된 변수를 선언문 이전에 참조하면 참조 에러(ReferenceError)가 발생한다. 이는 let 키워드로 선언된 변수는 스코프의 시작에서 변수의 선언까지 일시적 사각지대(Temporal Dead Zone; TDZ)에 빠지기 때문이다.


간단하게 변수가 어떻게 생성되며 호이스팅은 어떻게 이루어지는지 좀 더 자세히 살펴보면, 변수는 3단계에 걸쳐 생성됩니다.

선언 단계(Declaration phase)
변수를 실행 컨텍스트의 변수 객체(Variable Object)에 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.
초기화 단계(Initialization phase)
변수 객체(Variable Object)에 등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 변수는 undefined로 초기화된다.
할당 단계(Assignment phase)
undefined로 초기화된 변수에 실제 값을 할당한다.

var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다. 즉, 스코프에 변수를 등록(선언 단계)하고 메모리에 변수를 위한 공간을 확보한 후, undefined로 초기화(초기화 단계)한다. 따라서 변수 선언문 이전에 변수에 접근하여도 스코프에 변수가 존재하기 때문에 에러가 발생하지 않는다. 다만 undefined를 반환한다. 이후 변수 할당문에 도달하면 비로소 값이 할당된다. 이러한 현상을 변수호이스팅 이라 한다.

let 키워드로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다. 즉, 스코프에 변수를 등록(선언단계)하지만 초기화 단계는 변수 선언문에 도달했을 때 이루어진다. 초기화 이전에 변수에 접근하려고 하면 참조 에러(ReferenceError)가 발생한다. 이는 변수가 아직 초기화되지 않았기 때문이다. 다시 말하면 변수를 위한 메모리 공간이 아직 확보되지 않았기 때문이다. 따라서 스코프의 시작 지점부터 초기화 시작 지점까지는 변수를 참조할 수 없다. 스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 ‘일시적 사각지대(Temporal Dead Zone; TDZ)’라고 부른다.




1
2
3
4
5
6
7
let woo;
alert(woo); // undefined
 
{
alert(jeong);
let jeong;
} // throws a ReferenceError
cs


1
2
3
4
5
let min=1;
{
alert(min);
let min=2;
}
cs

위 예제의 경우, 전역 변수 min의 값이 출력될 것처럼 보인다. 하지만 ES6의 선언문도 여전히 호이스팅이 발생하기 때문에 참조 에러(ReferenceError)가 발생한다.

ES6의 let으로 선언된 변수는 블록 레벨 스코프를 가지므로 코드 블록 내에서 선언된 변수 min는 지역 변수이다. 따라서 지역 변수 min도 해당 스코프에서 호이스팅되고 코드 블록의 선두부터 초기화가 이루어지는 지점까지 일시적 사각지대(TDZ)에 빠진다. 따라서 전역 변수 min의 값이 출력되지 않고 참조 에러(ReferenceError)가 발생한다.

var let/const선언에 대한 범위의 차이 중 하나는 let/const TDZ에 의해 제약을 받는다는 것입니다.
즉, 변수가 초기화되기 전에 액세스하려고 하면, var처럼 undefined를 반환하지 않고, ReferenceError가 발생한다. 이는 코드를 예측가능하고 잠재적 버그를 쉽게 찾아낼 수 있도록 한다.

어떤 변수가 호출되었을 때 블록 안에 같은 이름의 변수가 없으면 상위 블록에서 선언된 같은 이름의 변수를 호출합니다. 하지만 블록 안에서 let이나 const로 변수 선언이 있었다면 그 이름의 변수는 변수가 선언되기 이전까지 그 블록안에서는 정의되지 않은 변수로 간주되는 것입니다.






*************************************************************************







템플릿 문자열
ES2015 이전에 사용하던 큰따옴표 (" "), 작은따옴표 (' ')로 감싸는 기존 문자열과 다르게 백틱(` `) 으로 감쌉니다.(TAB키 상단)
문자열 안에 변수를 넣을수 있는것이 특징입니다.
일반적인 문자열에서 줄바꿈은 허용되지 않으며 공백(white-space)를 표현하기 위해서는 백슬래시(\)로 시작하는 이스케이프 시퀀스를 사용하여야 한다. 
ES6 템플릿 리터럴은 일반적인 문자열과 달리 여러 줄에 걸쳐 문자열을 작성할 수 있으며 템플릿 리터럴 내의 모든 white-space는 있는 그대로 적용된다.

1
2
3
4
5
var num1 = 1;
var num2 = 2;
var result1 = 3;
var result2 = num1 + '더하기' +  num2 +  '는 \' '+ result1 + '\'';
console.log(result2);
cs

1
2
3
4
5
const num3 = 1;
const num4 = 2;
const result3 = 3;
const result4 = `${num3} 더하기 ${num4}는 '${result3}'`;
console.log(result4);
cs

기존 따옴표 대신 백틱을 사용하기 때문에, 큰 다옴표나 작은 따옴표와 함꼐 사용할 수도 있습니다.


1
2
3
4
5
6
// 템플릿 대입문에는 문자열뿐만 아니라 표현식도 사용할 수 있다.
console.log(`+ = ${+ 1}`); // 1 + 1 = 2
 
const name = 'ungmo';
 
console.log(`Hello ${name.toUpperCase()}`); // Hello UNGMO
cs

 ${expression}을 템플릿 대입문(Template substitution)이라 한다. 템플릿 대입문에는 문자열뿐만 아니라 자바스크립트 표현식을 사용할 수 있다.


*************************************************************************

객체 리터럴


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var sayNode = function() {
  console.log('Node');
};
var es = 'ES';
var oldObject = {
  sayJS : function(){
    console.log('JS');
  },
  sayNode: sayNode,
};
oldObject[es + 6= 'Fantastic';
 
oldObject.sayNode(); //Node
oldObject.sayJS(); //JS
console.log(oldObject.ES6); //Fantastic
cs


1
2
3
4
5
6
7
8
9
10
11
let newObject = {
  sayJS() {
    console.log('JS');
  },
  sayNode,
  [es+6]:'Fantastic',
}
newObject.sayNode(); //Node
newObject.sayJS(); //JS
console.log(newObject.ES6); //Fantastic
 
cs












//상단의 전역변수인 sayNode와 es를 받습니다.



oldObject 와 newObejct를 비교해서 볼때, sayJS 같은 객체의 메서드를 선언, 할당 할때 더는 콜론(:) 과 function 을 붙이지않습니다.

sayNode: sayNode 처럼 key(속성명) 와 value(변수명) 이 겹치는 경우에는 한번만 쓰면 됩니다.

자바스크립트에서는 다음과 같은 경우가 많은데, 코드의 중복을 피할수 있게 되어 편리합니다.

{ name : name, age : age } // ES5

{ name , age } //ES2015


객체의 속성명을 동적으로 생성할수 있습니다. 예전 문법에서는 ES6라는 속성명을 만들려면 객체 리터럴(oldObject) 바깥에서 [es +6]를 해야했었지만, ES2015 문법에서는 객체 리터럴 안에 선언해도 됩니다.


1
2
3
4
5
6
7
8
9
10
11
// ES5
var i = 0;
var propNamePrefix = 'prop_';
 
var obj = {};
 
obj[propNamePrefix + ++i] = i;
obj[propNamePrefix + ++i] = i;
obj[propNamePrefix + ++i] = i;
 
console.log(obj); // { prop_1: 1, prop_2: 2, prop_3: 3 }
cs


1
2
3
4
5
6
7
8
9
10
11
// ES6
let i = 0;
const propNamePrefix = 'prop_';
 
const obj = {
  [propNamePrefix + ++i]: i,
  [propNamePrefix + ++i]: i,
  [propNamePrefix + ++i]: i
};
 
console.log(obj); // { prop_1: 1, prop_2: 2, prop_3: 3 }
cs


객체 리터럴에 추가된 문법들은 코딩시 편의를 위해 만들어진 것이라는 느낌이 강하며, 익숙해지면 코드의 양을 많이 줄일수 있습니다.





*************************************************************************



화살표 함수

화살표 함수는 익명 함수로만 사용할 수 있다. 따라서 화살표 함수를 호출하기 위해서는 함수 표현식을 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 매개변수 지정 방법
    () => { ... } // 매개변수가 없을 경우
     x => { ... } // 매개변수가 한 개인 경우, 소괄호를 생략할 수 있다.
(x, y) => { ... } // 매개변수가 여러 개인 경우, 소괄호를 생략할 수 없다.
 
// 함수 몸체 지정 방법
=> { return x * x }  // single line block
=> x * x             // 함수 몸체가 한줄의 구문이라면 중괄호를 생략할 수 있으며 암묵적으로 return된다. 위 표현과 동일하다.
 
() => { return { a: }; }
() => ({ a: })  // 위 표현과 동일하다. 객체 반환시 소괄호를 사용한다.
 
() => {           // multi line block.
  const x = 10;
  return x * x;
};
cs

화살표 함수는 익명 함수로만 사용할 수 있다. 따라서 화살표 함수를 호출하기 위해서는 함수 표현식을 사용한다.
1
2
3
4
5
6
// ES5
var pow = function (x) { return x * x; };
console.log(pow(10)); // 100
// ES6
const pow = x => x * x;
console.log(pow(10)); // 100

cs


콜백함수로 간결하게 사용이 가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
// ES5
var arr = [123];
var pow = arr.map(function (x) { // x는 요소값
  return x * x;
});
 
console.log(pow); // [ 1, 4, 9 ]
// ES6
const arr = [123];
const pow = arr.map(x => x * x);
 
console.log(pow); // [ 1, 4, 9 ]
cs


**기존의 funtion(){}도 그대로 사용할수있습니다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function add1(x, y){
    return x + y;
}
const add2 = (x, y) => {
    return x + y;
};
 
const add3 = (x, y) => x + y;
 
const add4 = (x, y) => (x + y);
 
function not1(x) {
    return !x;
}
const not2 = x =>!x;
cs

add1, add2, add3 ,add4 와 not1, not2 는 각각 같은 기능을 하는 함수입니다

화살표 함수에서는 function 선언대신 => 기호로 함수를 선언합니다.
또한 , 이전의 functinon 과 동일하게 변수에 대입하여 재사용이 가능합니다.
return문을 줄일수 있으며, 중괄호 대신 return할 식을 바로 적어주면됩니다.
add4처럼 보기 좋게 소괄호로 감쌀수 있고, not2처럼 매개변수가 하나면 소괄호를 묶어주지 않아도 됩니다.

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function , 화살표 함수를 사용할시 this에 대해서.

자바스크립트의 경우 함수 호출 방식에 의해 this에 바인딩할 어떤 객체가 동적으로 결정된다. 다시 말해, 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되는 것이 아니고, 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다.

콜백 함수 내부의 this는 전역 객체 window를 가리킨다.


1
2
3
4
5
6
7
8
9
10
11
12
13
function Prefixer(prefix) {
  this.prefix = prefix;
}
 
Prefixer.prototype.prefixArray = function (arr) {
  // (A)
  return arr.map(function (x) {
    return this.prefix + ' ' + x; // (B)
  });
};
 
var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee''Kim']));
cs

(A) 지점에서의 this는 생성자 함수 Prefixer가 생성한 객체, 즉 생성자 함수의 인스턴스(위 예제의 경우 pre)이다.

(B) 지점에서 사용한 this는 아마도 생성자 함수 Prefixer가 생성한 객체(위 예제의 경우 pre)일 것으로 기대하였겠지만, 이곳에서 this는 전역 객체 window를 가리킨다. 이는 생성자 함수와 객체의 메소드를 제외한 모든 함수(내부 함수, 콜백 함수 포함) 내부의 this는 전역 객체를 가리키기 때문이다.

콜백 함수 내부의 this가 메소드를 호출한 객체(생성자 함수의 인스턴스)를 가리키게 하려면 아래의 3가지 방법이 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Solution 1: that = this
function Prefixer(prefix) {
  this.prefix = prefix;
}
 
Prefixer.prototype.prefixArray = function (arr) {
  var that = this;  // this: Prefixer 생성자 함수의 인스턴스
  return arr.map(function (x) {
    return that.prefix + ' ' + x;
  });
};
 
var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee''Kim']));
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
// Solution 2: map(func, this)
function Prefixer(prefix) {
  this.prefix = prefix;
}
 
Prefixer.prototype.prefixArray = function (arr) {
  return arr.map(function (x) {
    return this.prefix + ' ' + x;
  }, this); // this: Prefixer 생성자 함수의 인스턴스
};
 
var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee''Kim']));
cs

ES5에 추가된 Function.prototype.bind()로 this를 바인딩한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Solution 3: bind(this)
function Prefixer(prefix) {
  this.prefix = prefix;
}
 
Prefixer.prototype.prefixArray = function (arr) {
  return arr.map(function (x) {
    return this.prefix + ' ' + x;
  }.bind(this)); // this: Prefixer 생성자 함수의 인스턴스
};
 
var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee''Kim']));
cs


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////




화살표 함수에서의 this

일반 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되는 것이 아니고, 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다고 하였다.



화살표 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정된다. 동적으로 결정되는 일반 함수와는 달리 화살표 함수의 this 언제나 상위 스코프의 this를 가리킨다. 이를 Lexical this라 한다. 화살표 함수는 앞서 살펴본 Solution 3의 Syntactic sugar이다.

*****************중간 단어 설명~!*************

- 사람이 이해 하고 표현하기 쉽게 디자인된 프로그래밍 언어 문법

- 사람이 프로그래밍 언어를 sweeter하게 사용 할 수 있도록 도와주는 문법

- 더욱 더 간결하고 명확하게 표현이 가능한 문법을 뜻 한다.


화살표 함수의 this 바인딩 객체 결정 방식은 함수의 상위 스코프를 결정하는 방식인 렉시컬스코프와 유사하다.


////////////////////////////////////////////////////////////////////////////////////////////////////////////


1
2
3
4
5
6
7
8
9
10
function Prefixer(prefix) {
  this.prefix = prefix;
}
 
Prefixer.prototype.prefixArray = function (arr) {
  return arr.map(x => `${this.prefix}  ${x}`);
};
 
const pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee''Kim']));
cs



///////////////////////////////////////////////잠깐 렉시컬 스코프가 뭐죠?


JAVASCRIPT 는 렉시컬스코프(정적스코프)입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
var x = 1;
 
function foo() {
  var x = 10;
  bar();
}
 
function bar() {
  console.log(x);
}
 
foo(); // ?
bar(); // ?
cs


뭐가 출력될까요?


1
2
3
4
5
6
7
8
9
10
11
12
$x = "global";
 
sub yobu{
    local $x = "yobu:;
    &yobareru();
}
sub yobureru{
    print "$x\n";    
}
&yobu();
cs


위 예제의 실행 결과는 함수 bar의 상위 스코프가 무엇인지에 따라 결정된다. 두가지 패턴을 예측할 수 있는데 첫번째는 함수를 어디서 호출하였는지에 따라 상위 스코프를 결정하는 것이고 두번째는 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정하는 것이다. 첫번째 방식으로 함수의 상위 스코프를 결정한다면 함수 bar의 상위 스코프는 함수 foo와 전역일 것이고, 두번째 방식으로 함수의 스코프를 결정한다면 함수 bar의 스코프는 전역일 것이다.

프로그래밍 언어는 이 두가지 방식으로 함수의 상위 스코프를 결정한다. 첫번째 방식을 동적 스코프(Dynamic scope)라 하고, 두번째 방식을 렉시컬 스코프(Lexical scope) 또는 정적 스코프(Static scope)라 한다. 자바스크립트를 비롯한 대부분의 프로그래밍 언어는 렉시컬 스코프를 지원한다.

렉시컬 스코프는 함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정된다.#yobu 라고 출력됩니다.


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

화살표 함수를 사용해서는 안되는 경우


화살표 함수는 Lexical this를 지원하므로 콜백 함수로 사용하기 편리하다. 하지만 화살표 함수를 사용하는 것이 오히려 혼란을 불러오는 경우도 있으므로 주의하여야 한다.


화살표함수로 메소드를 정의하는 것은 피해야 한다. 화살표 함수로 메소드를 정의하여 보자.

1
2
3
4
5
6
7
// Bad
const person = {
  name'Lee',
  sayHi: () => console.log(`Hi ${this.name}`)
};
 
person.sayHi(); // Hi undefined
cs


위 예제의 경우, 메소드로 정의한 화살표 함수 내부의 this는 메소드를 소유한 객체, 즉 메소드를 호출한 객체를 가리키지 않고 상위 컨택스트인 전역 객체 window를 가리킨다. 따라서 화살표 함수로 메소드를 정의하는 것은 바람직하지 않다.

이와 같은 경우는 메소드를 위한 단축 표기법인 ES6의 축약 메소드 표현을 사용하는 것이 좋다.

1
2
3
4
5
6
7
8
9
// Good
const person = {
  name'Lee',
  sayHi() { // === sayHi: function() {
    console.log(`Hi ${this.name}`);
  }
};
 
person.sayHi(); // Hi Lee
cs


PROTOTYPE

화살표 함수로 정의된 메소드를 prototype에 할당하는 경우도 동일한 문제가 발생한다. 화살표 함수로 정의된 메소드를 prototype에 할당하여 보자.


1
2
3
4
5
6
7
8
// Bad
const person = {
  name'Lee',
};
 
Object.prototype.sayHi = () => console.log(`Hi ${this.name}`);
 
person.sayHi(); // Hi undefined
cs

화살표 함수로 객체의 메소드를 정의하였을 때와 같은 문제가 발생한다. 따라서 prototype에 메소드를 할당하는 경우, 일반 함수를 할당한다.

1
2
3
4
5
6
7
8
9
10
// Good
const person = {
  name'Lee',
};
 
Object.prototype.sayHi = function() {
  console.log(`Hi ${this.name}`);
};
 
person.sayHi(); // Hi Lee
cs
 


화살표 함수는생성자 함수로 사용할 수 없다. 생성자 함수는 prototype 프로퍼티를 가지며 prototype 프로퍼티가 가리키는 프로토타입 객체의 constructor를 사용한다. 하지만 화살표 함수는 prototype 프로퍼티를 가지고 있지 않다.

(축약 메소드도 prototype 프로퍼티가 없다)

1
2
3
4
5
6
const Foo = () => {};
 
// 화살표 함수는 prototype 프로퍼티가 없다
console.log(Foo.hasOwnProperty('prototype')); // false
 
const foo = new Foo(); // TypeError: Foo is not a constructor
cs

#4.4 addEventListener 함수의 콜백 함수

addEventListener 함수의 콜백 함수를 화살표 함수로 정의하면 this가 상위 컨택스트인 전역 객체 window를 가리킨다.


1
2
3
4
5
6
7
// Bad
var button = document.getElementById('myButton');
 
button.addEventListener('click', () => {
  console.log(this === window); // => true
  this.innerHTML = 'Clicked button';
});
cs

따라서 addEventListener 함수의 콜백 함수에서 this를 사용하는 경우, function 키워드로 정의한 일반 함수를 사용하여야 한다. 일반 함수로 정의된 addEventListener 함수의 콜백 함수 내부의 this는 이벤트 리스너에 바인딩된 요소(currentTarget)를 가리킨다.


1
2
3
4
5
6
7
// Good
var button = document.getElementById('myButton');
 
button.addEventListener('click'function() {
  console.log(this === button); // => true
  this.innerHTML = 'Clicked button';
});
cs








*************************************************************************


비구조화 할당


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var candyMachine = {
  status: {
    name : 'node',
    count : 5
  },
  getCandy : function(){
    this.status.count--;
    return this.status.count;
  }
};
var getCandy = candyMachine.getCandy;
var count = candyMachine.status.count;
 
let candyMachine = {
  status: {
    name : 'node',
    count : 5
  },
  getCandy : function(){
    this.status.count--;
    return this.status.count;
  }
};
let {getCandy, status : {count}} = candyMachine.getCandy;
cs

candyMachine 객체 안의 속성을 찾아서 변수와 매칭해줍니다.

count처럼 여러 단계 안의 속성도 찾을 수 있습니다.

getCandy와 count 변수가 초기화된 것입니다.


배열도 비구조화할 수 있습니다.


1
2
3
4
var array = ['nodejs', {}, 10true];
var node = array[0];
var obj = array[1];
var bpp = array[array.length -1];
cs







array란 배열의 첫번째, 두번째 요소와 마지막 요소를 변수에 대입하는 코드입니다. 다음과 같이 바꿀수 있습니다.

1
2
const array = ['nodejs',{},10,true];
const [node, obj, bool]=array;
cs



node, obj 와 bool의 위치를 보면 node는 배열의 첫 번째 요소, obj는 두번째요소, bool은 마지막 요소라는 것을 알수 있습니다.

obj와 bool 사이의 요소들은 무시합니다.



*************************************************************************



프로미스

자바스크립트와 노드에서는 주로비동기 프로그래밍을 합니다.

특히 이벤트 주도 방식 때문에 콜백 함수를 자주 사용합니다.

ES2015부터는 자바스크립트와 노드의 API들이 콜백 대신 프로미스(Promise)기반으로 재구성됩니다.

그래서 악명 높은 콜백을 극복했다는 평가를 받고 있습니다.

프로미스는 다음과  같은 규칙이 있습니다. 먼저 프로미스 객체를 생성해야 합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const condition = true// true면 resolve, false reject
const promise = new Promise((resolve, reject) => {
  if(condition){
    resolve('성공');
  }else {
    reject('실패');
  }
});
 
promise
  .then((message)=>{
    console.log(message);
  })
  .catch((error)=>{
    console.error(error);
  });
cs

new Promise로 프로미스를 생성할수 있으며, 안에 resolve와 reject를 매개변수로 갖는 콜백 함수를 넣어줍니다. 이렇게 만든 promise변수에 then과 cathc 메서드를 붙일수 있습니다.
프로미스 내부에서 resolve가 호출되면 then이 실행되고, reject가 호출되면 catch가 실행됩니다.
resolve와 reject에 넣어준 인자는 각각 then과 catch의 매개변수에서 받을수 있습니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
promise
  .then((message) =>{
    return new Promise((resolve, reject)=> {
      resolve(message);
    });
  })
  .then((message2)=>{
    console.log(message2);
    return new Promise((resolve,reject)=>{
      resoleve(message2);
    });
  })
  .then((message3)=>{
    console.log(message3);
  })
  .catch((error)=>{
    console.error(error);
  });
cs




















then이나 catch에서 다시 다른 then이나 catch를 붙일수 있습니다.

이전 then의 return값을 다음 then의 매개변수로 넘깁니다.

프로미스를 return한 경우 프로미스가 수행된 후 다음 then이나 catch가 호출됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function findAndSaveUser(User){
  User.findOne({},(err,user) => { // 첫번째 콜백
    if(err){
      return console.error(err);
    }
    user.name = 'zero';
    user.save((err)=>// 두번째 콜백
      if(err){
        return console.error(err);
      }
      User.findOne({gender:'m'},(err,user)=>// 세번째 콜백
        //생략
      });
    })
  })
}
cs



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function findAndSaveUser(User){
  User.findOne({})
    .then((user)=>{
      user.name = 'zero';
      return user.save();
    })
    .then((user) => {
      return User.findOne({gender:'m'});
    })
    .then((user) => {
      //생략
    })
    .catch(err=>{
      console.error(err);
    });
}
cs

콜백함수가 세번 중첩되어 있는 경우에 코드의 깊이가 깊어지지만, promise로 바꿔줄경우 then메서드로 순차적 실행이 가능하여 코드의 깊이가 더이상 깊어지지 않습니다


1
2
3
4
5
6
7
8
9
let promise1 = Promise.resolve('성공1');
let promise2 = Promise.resolve('성공2');
Promise.all([promise1,promise2])
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.error(error);
  });
cs

Promise.resolve는 즉시 resolve하는 프로미스를 만드는 방법입니다. 또한 Promise.reject도 있습니다.
프로미스가 여러 개 있을 때 Promise.all에 넣으면 모두 resolve 될떄까지 기다렸다가 then으로 넘어갑니다.
result 매개변수에 각각의 프로미스 결괏값이 배열로 들어 있습니다.
Promise중 하나라도 reject가 되면 catch로 넘어갑니다.


나온배경 :

es2015 이전과 다른점:

사용법 :
예시 :


async/await
asumc/await 문법은 프로미스를 사용한 코드를 한번 더 깔끔하게 줄여줍니다.

나온배경 :

es2015 이전과 다른점:

사용법 :
예시 :


프론트엔드 자바스크립트

AJAX

나온배경 :

es2015 이전과 다른점:

사용법 :
예시 :


FormData

나온배경 :

es2015 이전과 다른점:

사용법 :
예시 :


encoderURLComponent, decodeURLComponent

나온배경 :

es2015 이전과 다른점:

사용법 :
예시 :


data attribute와 dataset

나온배경 :

es2015 이전과 다른점:

사용법 :
예시 :






//참조 : Node.js 교과서 , ㅓ닝 자바스크립트


728x90
반응형
Comments