코딩앙마 자바스크립트 중급 강좌 총정리

백돼지·2022년 12월 19일
0

변수와 호이스팅, 스코프

  • var : 한번 선언 된 변수를 또 다시 선언이 가능.
  • let : 한번 선언 된 변수는 다시 선언 불가능.
  • const : 한번 선언 된 변수값은 절대 변경되지 않음.
  • 호이스팅 : 스코프(scope:범위) 내부 어디서든 변수 선언은 최상위에서 선언된 것처럼 행동하는 것.

let과 const는 es6부터 등장한 변수들이다.
그 전까지는 var를 사용했었다. let과 const는 var와 다르게 코드만으로도 의도를 알고 버그를
줄일 수 있기 때문에 변수는 let과 const를 사용하는것이 좋다.

3가지 변수 모두 호이스팅을 일으킨다.

let과 const는 블록 스코프(block - scoped)다.
즉, 코드 블록(함수, if문, while문 등)안에서 선언 된 변수는 지역변수다.
그 블록 안에서만 사용이 가능한 변수라는 뜻이다.


생성자 함수와 new

생성자 함수는 유사한 여러 객체를 손쉽게 만들 수 있다.
(쉽게 말해 붕어빵 틀 이라고 생각하면 쉽다.)

  • 함수명의 첫글자를 대문자로 하는것이 관례이다.
  • 실행할때는 반드시 new 연산자를 붙여줘야한다.
    (new를 붙이지 않으면 일반적인 함수로 생각하기때문에 아무것도 반환하지 않는다.(undifined)
	function Item(title,price){
    	this.title = title;
        this.price = price;
        this.showPrice = function(){
        	console.log(`가격은 ${price}원 입니다.`);
            }
       }
    
    const item1 = new Item('인형',1000);
    const item2 = new Item('가방',2000);
    const item3 = new Item('신발',3000);
    
    console.log(item1, item2, item3) 
    // Item {title: '인형', price: 1000, showPrice: ƒ} 
    // Item {title: '가방', price: 2000, showPrice: ƒ} 
    // Item {title: '신발', price: 3000, showPrice: ƒ}
       
    item1.showPrice()
    // 가격은 1000원 입니다.

계산 된 프로퍼티(computed property)

컴퓨티드 프로피티는 이미 계산된 프로퍼티이다.
즉, 변수명을 넣으면 그 변수에 할당 된(계산 된)값이 할당 되고,
['안녕'+'하세요'] 또는 [2+3] 처럼 계산식도 넣을 수 있다.

    function computedProperty(key,val){
        return {
        [key] : val
        };
     } 
    const name = computedProperty('이름','yong');

    console.log(name);	
    //{이름: 'yong'}
    // 이렇게 어떤게 key가 될 지 모르는 객체를 만들 때 유용하다.

객체 메소드 object methods

  • Object.assign : 객체 복사하기
const user = {
    	name: 'yong',
        age: 27
     };
     
const user2 = Object.assign({},user); //{}를 넣으면 새로운 객체를 만든다는 것이다.
user2.name = 'gal';
    
console.log(user); // {name: 'yong', age: 27}
console.log(user2); // {name: 'gal', age: 27}

객체의 key와 value만 따로 배열로 출력시킬 수 도 있다.

  • Object.keys : 객체의 key만 배열로 모두 반환
  • Object.values : 객체의 value만 배열로 모두 반환
  • Object.entries : 객체의 key,value 각각 배열로 모두 반환
  • Object.fromEntries : 배열을 객체로 변환
<Object.keys>
  
const user = {
    name: 'yong',
    age: 27
 };
 
const key = Object.keys(user);

console.log(key); //['name', 'age']
<Object.values>
  
const user = {
    name: 'yong',
    age: 27
 };
 
const value = Object.values(user);

console.log(value); //['yong', 27]
<Object.entries>
  
const user = {
    name: 'yong',
    age: 27
 };
 
const entrie = Object.entries(user);

console.log(entrie); //  0 : (2) ['name', 'yong']
					 //  1 : (2) ['age', 27]
<Object.fromEntries>
  
let arr = [
          ['name','yong'],
          ['age','27']
];
 
const fromE = Object.fromEntries(user);

console.log(fromE); //{name: 'yong', age: '27'}

심볼 Symbol (반드시 S는 대문자)

심볼은 기존의 객체의 내용을 건드리지 않고 속성을 추가할 수 있는 태그다.
남이 만든 코드에 함부로 속성을 건드리면 코드에 어떤 일이 일어날지 모르고,
또 속성을 추가한다한들 내가 지은 네이밍이 스코프 어딘가에 같은 이름으로
쓰이고 있을지 모르기 때문에!!

이럴때 심볼을 사용해주면 좋다.

const a = Symbol('name'); //Symbol <--S 대문자 명심!! ('name')은 심볼의 아이디
const b = Symbol('name');

console.log(a); //Symbol(name)
console.log(b); //Symbol(name)

//a와 b에 같은 심볼을 넣어서 콘솔에 출력해보면 동일하게 나온다. 하지만..

console.log(a==b); //false
console.log(a===b); //false

//비교해보니 둘은 완전히 각각 다른 심볼이다.
//즉, 심볼은 유일성이 보장된다. 
  • Symbol.for() : 전역 심볼
    유일한 식별자인 Symbol과 다르게 Symbol.for()을 쓰면 전역 스코프에서
    여러개로 사용할 수 있다.
  • Symbol.keyFor() :
    해당 변수의 심볼에 정해 준 아이디(이름)를 불러와준다. () 괄호 안에 불러올 변수를 넣어주면 된다. (단, keyFor는 전역 심볼에만 사용이 가능하다.
    대신 다른 방법으로는 description으로 불러올 수 있다.)

<Symbol 실제로 사용하기>
  
const user = {
    name: "yong",
    age: 27
}

const showName = Symbol("show name"); //showName을 심볼로 지정(id:show name)
user[showName] = function(){ //user에 showName을 넣는다.
    console.log(this.name);
}

user[showName]();   //yong
					//showName을 실행해도 아래 for반복문에 영향을 주지 않는다.


for(let key in user){
    console.log(`His ${key} is ${user[key]}`);
} //His name is yong
  //His age is 27

Number 와 Math (숫자,수학 method)

통계,지표,쇼핑몰 등 연산이 필요한 동작에서 빠질 수 없는 태그들이다.
어떤것들이 있는지 쭉~쭉쭉 알아두자.

* toString() : ()에 받은 인수만큼 ()진수로 출력한다.
  
let num = 10;

console.log(num.toString()); //10
console.log(num.toString(2)); //1010
console.log(num.toString(16)); //a

-------------------------------------------------------
  
* Math.ceil() : 올림

let num1 = 5.1;
let num2 = 5.7;

console.log(Math.ceil(num1)); //6
console.log(Math.ceil(num2)); //6

-------------------------------------------------------

* Math.floor() : 내림
  
let num1 = 5.1;
let num2 = 5.7;

console.log(Math.floor(num1)); //5
console.log(Math.floor(num2)); //5

-------------------------------------------------------

* Math.round() : 반올림
  
let num1 = 5.1;
let num2 = 5.7;

console.log(Math.round(num1)); //5
console.log(Math.round(num2)); //6

-------------------------------------------------------
  
* Math.random() : 0~1 사이의 무작위 숫자를 생성한다. 

console.log(Math.random());  //0.8344920908341382
							 //새로고침 할때마다 값은 매번 달라진다.

1~100 사이의 무작위 숫자를 얻고 싶다면?

Math.floor(Math.random()*100)+1;	//54  
			    					//새로고침 할때마다 값은 매번 달라진다.
									//마지막 +1은 1~100까지의 숫자를 원하기 때문에 1을 더해준다.

-------------------------------------------------------
  
* Math.max()와 Math.min() : ()안의 인수 중 최댓값/최솟값을 구할 수 있다.

console.log(Math.max(1,2,3,4,5));	//5
console.log(Math.min(1,2,3,4,5));	//1

-------------------------------------------------------  

* Math.abs() : ()안의 인수의 절대값을 구해준다.(abs는 absolute의 약자)

console.log(Math.abs(-2));	//2

-------------------------------------------------------  
  
* Math.pow(n,m) : 제곱 (pow는 power의 약자)

console.log(Math.pow(2,3));	//8

-------------------------------------------------------

* Math.sqrt() : 제곱근 (sqrt는 square root(영어로 제곱근)의 약자)  

console.log(Math.sqrt(16));	//4

-------------------------------------------------------  
  
* toFixed() : ()에 받은 인수만큼 소수점 자릿수 출력한다.

let num = 3.123;

console.log(num.toFixed()); 	//3
console.log(num.toFixed(0));	//3
console.log(num.toFixed(2));	//3.12
console.log(num.toFixed(5));	//3.12300

`<주의> tofixed는 반환값을 string으로 출력한다!!
따라서 Number(tofixed())처럼 Number로 다시 반환해줘야 하는 경우가 많다!!`

-------------------------------------------------------
  
* isNaN() : ()의 인수가 NaN인지 불린형으로 확인한다.

let x = Number('x');
let y = Number('2');

console.log(isNaN(x));  //true
console.log(isNaN(y));	//false

`isNaN 만이 유일하게 NaN인지 확인할 수 있다. 아래 사진도 같이 알아두자.`

* parseInt() : ()안의 인수의 문자열을 정수로 반환한다. Number와 다른점은 parseInt는 '34px'와 같은
숫자+문자열의 경우 숫자로 반환할 수 있는 만큼 반환한다는 점이다.

let margin = '10px'; //문자열 10px
let colorCode = 'ff5533';	

console.log(parseInt(margin));	//10
console.log(Number(margin)); 	//NaN
console.log(parseInt(colorCode));	//때문에 문자열이 숫자로 시작하지 않는다면 NaN을 반환한다.

*또한 parseInt는 두번째 인수를 받아서 '첫번째 인수'의 진수를 지정할 수 있다.

let colorCode = 'f3';

console.log(parseInt(colorCode,16));	//243
console.log(parseInt('11',2));	  //3

-------------------------------------------------------
  
* parseFloat() : parseInt와 다르게 소수점까지 모두 반환한다.

let a = '15.3256';  

console.log(parseFloat(a));    //15.3256  

문자열 메소드 (String method)

문자열은 '' , "", `` 3가지로 표현할 수 있다.
'', "" 는 상황에 맞게 잘 사용하며 되고,

``백틱의 경우에는 다른 따옴표와 달리 줄바꿈을 해도 그대로 적용이 된다는 장점이 있다.
(작은.큰 따옴표들은 \n 을 써야함)

let news = `안녕하세요.
반갑습니다.
오늘의 날씨는 좋습니다.`;

console.log(news);	//안녕하세요.
					//반갑습니다.
					//오늘의 날씨는 좋습니다.
  • length :
    문자열도 배열과 마찬가지로 .length를 사용해 문자열의 길이를 구할 수 있다.
    (공백도 한 길이로 포함!!)
let result = "안녕하세요.";
console.log(result.length);	//6
  • 특정위치에 접근 : 배열처럼 변수[ ]의 [ ]안의 인수를 통해 [ ]번째 배열요소를 불러올 수 있다.
    배열과 달리 문자열은 한글자 씩 고칠 수 없다.
let hi = "안녕하세요.";
console.log(hi[2]);	//하

hi[0] = "아";

console.log(hi);	//안녕하세요.
					//배열과 달리 문자열은 '한글자씩' 고칠수 없다.
  • toUpperCase() : 모두 대문자로 바꿈
  • toLowerCase() : 모두 소문자로 바꿈
let result = "Hi, What are you doing this weekend?";
console.log(result.toUpperCase());	//HI, WHAT ARE YOU DOING THIS WEEKEND?
console.log(result.toLowerCase());	//hi, what are you doing this weekend?
  • str.indexOf(text) : ( )안의 문자가 있는 위치를 반환
    • indexOf는 ()안의 문자가 '첫번째'로 나타나는 위치 index를 리턴한다.
      • 문자가 없으면 -1을 리턴합니다.
      • 문자열을 찾을 때 대소문자를 구분합니다.
      • if문에서 사용할 때는 > -1 을 넣어서 첫글자가 0이되어 if문이 false되는걸 막아야한다.

str.includes(text) : ( )안의 문자가 있는지 없는지 확인

특정 문자가 있는지의 여부만 확인할때는,
includes(text)를 쓰면 indexOf와 달리 > -1 를 추가해줄 필요 없다.

let result = "안녕하세요,제 이름은 용돌이 입니다.";
console.log(result.indexOf("용돌이"));	//12
console.log(result.indexOf("돼지"));	//-1

if(result.indexOf("안녕하세요")){
   console.log("반갑습니다")
}	//"안녕하세요"의 indexOf는 0이고, if문에서 0은 false이기 때문에 아무것도 반환하지 않음. 따라서,

if(result.indexOf("안녕하세요" > -1)){
   console.log("반갑습니다")
}	//반갑습니다.	
	//이렇게 -1보다 큰가? 를 논리연산자로 써서 0도 true에 포함되게 해줌.

str.slice(n, m) : n번째~m번째(m본인 미포함)까지의 문자 반환

  • m이 없다면 문자열의 끝까지 반환한다.
  • 음수라면 오른쪽 끝에서부터 센다.
let text = "와주셔서 감사합니다";
console.log(text.slice(2));		//셔서 감사합니다 (2부터 끝까지)
console.log(text.slice(2,4));		//셔서 (2,3)
console.log(text.slice(1,-3));		//주셔서 감 (1,2,3,4)

str.substring(n,m) : slice와 비슷하지만 n과 m이 바뀌어도 그대로 동작한다.

  • 쉽게 말해 n과 m사이를 반환한다.
  • 단, 음수는 허용하지 않는다.
let text = "와주셔서 감사합니다";
console.log(text.substring(0,3));	//와주셔
console.log(text.substring(3,0));	//와주셔

str.substr(n,m) : 배열의 n번째 부터 m개(n포함)를 가져온다.

let text = "와주셔서 감사합니다";
console.log(text.substr(2,3));	//셔서
console.log(text.substr(-4,2));	//사합
								//음수를 쓰면 0,1,2번째가 아니라
								//-1번째, -2번째 이렇게 된다.

str.trim() : 앞 뒤 공백 제거 (trim:불필요한 것을 제거하다.)

let text = "   반  갑습니다        ";
console.log(text.trim());		//반  갑습니다
								//문자열의 앞,뒤 공백만 제거

str.repeat(n) : 문자열을 n번 반복한다.

let text = "hello!";
console.log(text.repeat(3));	//hello!hello!hello!

문자끼리도 엄연히 크기를 비교할 수 있다.
a보다 z가 크고, 대문자보다 소문자가 크다는것만 알고 있으면 된다. (아래 이미지 참고)


배열 메소드 Array method

arr.splice(n,m,x) : 배열에서 지정한 배열요소를 삭제하고 삭제 된 값을 반환하며, 추가한다.
n번째부터 m개를 삭제, x는 추가하는 값

let arr = [1,2,3,4,5,'a','b'];

let arrDelete = arr.splice(1,2);

console.log(arrDelete);	//[2,3]	(삭제한 값을 반환할 수 있음)
console.log(arr);	//[1,4,5,a,b]	(삭제하고 남은 배열)

<x를 추가해 배열에 새로운 요소를 추가하기>
  
let arr2 = [1,2,3,4];

let arr2Delete = arr2.splice(1,0,100,2000);	//(1,0)과 같이 아무것도 삭제하지 않으면 
											//출발한 인덱스는 1, 즉 0과 1사이에 x를 추가한다.

console.log(arr2);	//[1, 100, 2000, 2, 3, 4]

arr.slice(n,m) : str.slice(n,m)과 동일하다.

  • m은 미포함하고 n번째 부터 m번째 까지 반환한다.
  • ( )안에 아무것도 넣지 않으면 복사된다.
let arr = [1,2,3,4];

console.log(arr.slice(1,3));	//[2,3]
console.log(arr.slice());	//[1,2,3,4]

arr.concat([arr2], [arr3],...) : concat은 배열을 병합시키고 새 배열을 만들어준다.

  • (concat : 연결하다)
let arr = [1,2]

console.log(arr.concat([3,4])); //[1,2,3,4]
console.log(arr.concat([3,4], [5,6]));	//[1,2,3,4,5,6]  배열과 배열 병합 가능
console.log(arr.concat([3,4],5,6));	//[1,2,3,4,5,6] 배열과 숫자 병합 가능

arr.forEach(fn) : 배열 반복

  • 배열을 반복시키는 요소는 item과 index를 많이쓰고 arr는 거의 사용하지 않는다.
  • 내장함수(map,filter..등 7~8개 정도 됨)에 쓰이는 ( )안의 인수는 (item, index, arr)순으로 고정으로 넣을 수 있다.
    각각에 들어가는 인수 이름은 내가 정할 수 있다.
<arr.forEach(함수)를 사용해 배열 반복시키기>
let users = ['Mike', 'Tom', 'Jane'];

users.forEach((item)=>{
	console.log(item)})		//Mike
							//Tom
							//Jane

users.forEach((item,index)=>{
	console.log(item,index)})	//Mike 0
								//Tom 1
								//Jane 2

users.forEach((yong,dol)=>{
	console.log(yong,dol)})	    //Mike 0
								//Tom 1
								//Jane 2
								//forEach(item, index, arr)<==이 3자리는 고정이다.
								//이 3가지는 내가 아무 인수나 지정해줘서 쓸 수 있다.

users.forEach((item,index)=>{
	console.log(`${index + 1}. ${item}`)}) //1. Mike
								//2. Tom
								//3. Jane

arr.indexOf() : str.indexOf()와 동일하게 ( )가 있는 위치 index를 반환한다.

  • arr.indexOf(2,2)처럼 인수가 두개인 경우, 두번째 인수는 "찾기 시작하는 지점의 인덱스"가 된다.
let arr = [1,2,3,4,5,6,7,3];

console.log(arr.indexOf(3));	//2
console.log(arr.indexOf(3,2));	//2  (idx[2]부터 출발하니 3이 있는곳은 idx[2])
console.log(arr.indexOf(3,3));	//7	(idx[3]부터 출발하니 3이 있는곳은 idx[7])

arr.includes() : str.includes()와 동일. 배열에 ()의 인수가 포함되는지 확인하고 싶을때 사용.

  • include : 포함하다.
let arr = [1,2,3];

console.log(arr.includes(2));	//true
console.log(arr.includes(4));	//false

arr.find(f) 와 arr.findIndex(f) : 함수를 사용해 원하는 값을 찾아낸다. (짝수를 찾거나, 성인을 찾거나.. 등등)

  • 첫번째 true값만 반환하고 끝
  • 없으면 undefined를 반환

arr.filter(f) : find와 동일하나 모든 true값을 반환한다는 차이가 있다.

<arr.find(f),arr.filter(f)>
: arr 배열 중 첫번째 짝수와 모든 짝수를 찾아보자.
  
let arr = [1,2,3,4,5];

const arrEvenNumber = arr.find((item)=>{
    return item % 2 === 0; //(arr의 item 중 첫번째 짝수를 반환 후 끝)
});

const arrAllEvenNumber = arr.filter((item)=>{
    return item % 2 === 0; //(arr의 item 중 첫번째 짝수를 반환 후 끝)
});

console.log(arrEvenNumber); //2
console.log(arrAllEvenNumber);	//2,4
<arr.find(f)와 arr.findIndex(f)>
  
let userList = [
    { name: "Mike", age: 30},
    { name: "Tom", age: 25},
    { name: "Jane", age: 10}
]

const result = userList.find((user)=>{
    if(user.age < 19){
        return true;
    }return false;
})

const resultIndex = userList.findIndex((user)=>{
    if(user.age < 19){
        return true;
    }return false;
})

console.log(result);    //{name: 'Jane', age: 10}
console.log(resultIndex);   //2

arr.reverse() : 배열 요소를 역순으로 재정렬한다.

  • 최근에 게시된 글이나, 최근에 가입한 유저순으로 정렬 등..이럴때에 많이 쓰인다.
let arrNum = [1,2,3,4,5];
console.log(arrNum.reverse());	//5,4,3,2,1

arr.map(f) : 함수를 받아 특정 기능을 시행하고 새로운 배열을 반환한다.

  • map은 실무에서 정말 많이 쓰인다고 하니 틈틈히 연습해두자.
let userList = [
    { name: "yong", age: 20},
    { name: "gal", age: 30},
    { name: "lee", age: 40},
];

let newUserList = userList.map((user,index) => {	
  //map으로 기존 배열에서 값을 변경한 새 배열을 선언
    return Object.assign({}, user, {id: index+1, isAdult: user.age > 19})	
  //Object.assign()을 넣어야 기존의 배열을 복사 후 그 배열에 요소를 추가를 시킬 수 있다.
});

console.log(newUserList);
//  {name: 'yong', age: 20, id: 1, isAdult: true}
//	{name: 'gal', age: 30, id: 2, isAdult: true}
//	{name: 'lee', age: 40, id: 3, isAdult: true}

arr.join( ) : 배열을 합쳐서 하나의 문자열로 만든다.

  • 괄호 안에 받는 인수로 합치는 배열간 들어갈 구분 요소를 지정해 줄 수 있다.
let arr = ["나는","용돌이","입니다"];
console.log(arr.join(" "));	//나는 용돌이 입니다

arr.split( ) : 하나의 문자열을 배열로 나눠준다.

  • 괄호 안에 받는 인수로 배열을 만들때 배열의 각 요소를 구분할 기존 문자열의 구분자를 정할 수 있다.
let arr = "용돌이, 만세!";

console.log(arr.split());		//['용돌이, 만세!']	
console.log(arr.split(""));		//['용', '돌', '이', ',', ' ', '만', '세', '!']
console.log(arr.split(" "));	//['용돌이,', '만세!']
console.log(arr.split(","));	//['용돌이', ' 만세!']
console.log(arr.split("돌"));	//['용', '이, 만세!']

Array.isArray( ) : 괄호 안에 받는 변수가 배열인지 불린형으로 알려준다.

let user = {name: "yong", age: 20};	//객체
let user2 = [1,2,3,4];	//배열(배열도 객체에 속함)

console.log(Array.isArray(user));	//false
console.log(Array.isArray(user2));	//true
//user과 user2는 둘다 객체이나, Array.isArray로 배열인지를 확인할 수 있다.

arr.Sort(fn) : 인수를 함수로 받아서 배열을 재정렬한다.

  • 함수가 생략되면 배열 요소를 문자열로 받아 유니코드 순으로 배열을 재정렬한다.
let arr = [25,4,13,88];
arr.sort((a,b)=>{
	return a - b 	//b - a 로 뒤집으면 내림차순이 됨.
}
        );
console.log(arr);	//[4, 13, 25, 88]

arr.reduce(fn) : 누적계산값(a)과 현재값(b)를 받아 함수를 사용해 배열의 각 요소를 순회하며 함수의 실행 값을 누적하여 하나의 결과값을 반환 한다.

  • 초기값을 빈 배열 [ ], 0 과 같이 반드시 설정해줘야한다.
let userList = [
    {name : "yong", age: 18},
    {name : "Tom", age: 28},
    {name : "ave", age: 35},
    {name : "hong", age: 13},
    {name : "gigi", age: 5},
];

let result = userList.reduce((p,c)=>{
    return (p += c.age)}	//모든 age를 더한 값 구하기
    ,0);
let result2 = userList.reduce((p,c)=>{
    if(c.age > 19){		
        p.push(c.name)		//현재(c) age가 19보다 클때, 
      						//p(누적 계산값)에 c의 name을 추가한다.
      						//즉 초기값 []에 하나씩 추가한다.
    }
    return p;
},[])

let result3 = userList.reduce((p,c)=>{
    if(c.name.length === 3){	//현재(c)의 name의 길이가 3일때,
      							//p(누적 계산값)에 c의 name을 추가한다.
      							//즉 초기값 []에 하나씩 추가한다.
        p.push(c.name)
    }
    return p
},[])

console.log(result);	//88
console.log(result2);	//["Tom","ave"]
console.log(result3);	//["Tom","ave"]

구조 분해 할당 (Destructuring assignment)

구조 분해 할당이란, 배열이나 객체의 각 속성을 분해해서,
그 값을 변수에 할당할 수 있게 해주는 표현식을 말한다.

<배열 구조 분해> [ ]

배열 구조 분해의 개념은 아래와 같다.

let [user1, user2, user3] = ["yong", "ga", "ri"];

console.log(user1);	//yong
console.log(user2);	//ga
console.log(user3);	//ri

배열에 기본값을 미리 할당해 변수가 undefined 되는걸 막을 수도 있다.

let [a=1, b=2, c=3] = [4,5];

console.log(a);	//4
console.log(b);	//5
console.log(c);	//3

일부 반환값을 무시할 수도 있다.

let [user1, ,user3] = ["Tom","Eve","Mike"];	//user2부분은 공백으로 둔 상태

console.log(user1); //Tom
console.log(user2); //undefined	(공백)
console.log(user3); //Mike (user3가 공백을 건너뛰고 Mike를 할당받음)

배열의 값을 바꿔치기 할때도 구조 분해 할당은 유용하다.

let a = 1;
let b = 2;

[a,b] = [b,a];

console.log(a);	//2
console.log(b);	//1

<객체 구조 분해> { }

객체 구조 분해는 배열 구조 분해와 동일하나
한가지 차이점은 객체 요소의 순서를 무시해도 된다는 점이다.

let user = {name: "yong", age: 27};
let {name,age} = user;	//이 표현식은 let name = user.name;
						//		   let age = user.age; 와 같다.
//let {age,name} = users;	//이렇게 순서를 바꿔도 정상 작동
console.log(name);	//yong
console.log(age);	//27

새로운 변수 이름으로 할당해 사용할 수도 있다.

let user = {name: "yong", age: 27};
	
let {age: userAge, name: userName} = user;

console.log(userAge);	//27
console.log(userName);	//yong

배열 구조 분해와 동일하게 기본값을 지정해줄 수 있다.

let user = {name: "yong", age: 27};
	
let {name, age, gender = "data is not defined."} = user;

console.log(name);	//yong
console.log(age);	//27
console.log(gender);	//data is not defined.
						//만약 user에 gender값이 할당되어 있다면, 당연히 그 값이
						//기본값을 덮어씌우고 그 값이 반환된다.

나머지 매개변수(...)

함수에서 매개변수를 받을때 마지막 매개변수에 ...을 넣어서 남는 나머지 인수들을
모두 할당 받을 수 있다. 나머지 매개변수는 반드시 마지막에 위치해야한다.

  • 나머지 매개변수는 array의 형태로 반환되기 때문에, array 메소드(forEach,map,filter등)
    를 따로 사용할 수가 있다.
function add(...numbers){	//받는 모든 인수를 number가 매개변수로 활용
    let result = 0;
    numbers.forEach((num)=>(result += num))	//배열 메소드 forEach를 사용
    console.log(result);
}

add(1,2,3);	//6           

생성자 함수를 사용해 좀 더 실용적인 예제를 만들 수 있다.

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

let user1 = new Users("yong", 18, "a", "b", "c");

console.log(user1) //아래와 같이 name과 age에 할당되고 남은 인수들이,
				   //모두 나머지 매개변수인 ...skills에 할당되어 배열 형태로 반환되었다.

전개구문(...)

...은 전개구문으로 사용할 수도 있다. 배열과 객체를 다룰때
복잡한 메소드를 사용하지 않고 대신 전개구문으로 쉽게 배열과 객체에 활용할 수 있다.

let a = [1,2,3];
let b = [4,5,6];

let c = [...a, ...b];
console.log(c);	//[1, 2, 3, 4, 5, 6]

배열 중간에 전개구문을 넣을 수도 있다.

let a = [1, 2, 3];

let b = [0, ...a, 4, 5, 6];
console.log(b);	//[0, 1, 2, 3, 4, 5, 6]

전개구문으로 object.assign()을 쓰지 않고도 객체와 배열 모두 복제가 가능하다.

let num1 = [1,2,3,4];
let num2 = [...num1];

console.log(num2);	//[1,2,3,4]

let user1 = {name: "yong", age: 27};
let user2 = {...user1};

console.log(user2);	//{name: "yong", age: 27}

**여기서 user2의 name을 Tom으로 바꾸어도 user1의 객체에는 아무런 영향을 끼치지 않는다., user2는 별개의 객체로 복제되었다.**

user2.name = "Tom";
console.log(user1.name);	//yong
console.log(user2.name);	//Tom

클로저(Closures)

클로저에 대한 내용들


함수 실행을 시간으로 제어하기

( setTimeout(변수,ms) / setInterval(변수,ms) )

setTimeout : 지정한 일정 시간이 지난 뒤 함수를 실행하게 한다.
setInterval : 지정한 일정 시간 간격으로 함수를 실행하게 한다.

<setTimeout>
  
let num = 0;

function showTime(){
  console.log(`현재 접속한지 ${num+=3}초가 지났습니다.`)
};

setTimeout(showTime,3000);	//(3초 뒤)현재 접속한지 3초가 지났습니다.


<setInterval>
  
let num = 0;

function showTime(){
  console.log(`현재 접속한지 ${num++}초가 지났습니다.`)
};

setInterval(showTime,1000);	//현재 접속한지 0초가 지났습니다. (1초마다)
							//현재 접속한지 1초가 지났습니다.
							//현재 접속한지 2초가 지났습니다.
							//			...

각각 set -> clear 으로 바꿔서 멈추게 할 수 있다.

<clearTimeout>
  
let num = 0;

function showTime(){
  console.log(`현재 접속한지 ${num+=3}초가 지났습니다.`)
};

const tId = setTimeout(showTime,3000);

clearTimeout(tId);	//(아무 출력 없음)
`clearTimeout이 setTimeout보다 밑에 있는데도 먼저 실행되는 이유는,
스케줄 함수들은 모든 코드가 쭉 실행된 후에 실행되기 때문에 아무 출력이 없음`
  


<clearInterval>
 
let num = 0;

function showTime(){
  console.log(`현재 접속한지 ${num++}초가 지났습니다.`)
  if(num>3){		//num이 3보다 커지면 아래 함수를 실행
    clearInterval(tId);	//tId를 clearInterval 시킨다.
	}
};

const tId = setInterval(showTime,1000);	
							//현재 접속한지 0초가 지났습니다. (1초마다)
							//현재 접속한지 1초가 지났습니다.
							//현재 접속한지 2초가 지났습니다.
							//현재 접속한지 3초가 지났습니다.
							//         (끝)

함수에 this값을 지정하는 메소드

.call, .apply, .bind

  • 세가지 모두 함수의 호출방식과 상관없이 this값을 지정해줄 수 있다.

call : 모든 함수에서 사용할 수 있으며, this를 특정값으로 지정 할 수 있다.

const mike = {
    name: "Mike",	
};

const tom = {
    name: "Tom",
};

function showName(){
    console.log(this.name);	//this에는 이 함수를 호출한 그 인수가 들어간다.
};

showName();		//
showName.call(tom);	    //Tom
						//call은 모든 함수에서 접근이 가능하기 때문에, 함수안에 없던 변수를 불러넣음!

function update(birthYear, skills){
    this.birthYear = birthYear;
    this.skills = skills;
};

update.call(tom,1999,'HTML5');		
									//call()에서 받는 첫번째 매개변수는 this를 지정한다.
									//call()에서 받는 두번째 매개변수 부터는 update에 지정 된
									//매개변수(birthYear, skills)에 차례로 들어간다.

console.log(tom);	  //{name: 'Tom', birthYear: 1999, skills: 'HTML5'}

apply : call 과 완전히 동일하나, apply는 받는 매개변수를 배열로 받는다는 차이점이 있다.

const nums = [1, 2, 3, 5, 8];

const minNum = Math.min.apply(null, nums);	//Math.min.apply(null, [1,2,3,5,8])과 같고,
											//이는 Math.min(1,2,3,5,8)이 된다.
		      //apply(null, nums)에서 null에는 this에 들어가는
			  //매개변수를 지정해야하나, 현재 Math.메소드에서는 딱히 this가 필요가 없으므로 null을 넣음.

console.log(minNum);    //1

const maxNum = Math.max.call(null, ...nums);	//Math.max.call(null, 1,2,3,5,8)과 같고,
							//이는 Math.max(1,2,3,5,8)이 된다.
console.log(maxNum);    //8

bind : 함수의 this값을 영구히 바꿀 수 있다.

const user = {
    name: "Mike",
    showName: function(){
        console.log(`hello, ${this.name}`);
    },
};

user.showName();    //hello, Mike
                    //변수 user의 지역함수 showName을 실행하면
                    //user에 할당 된 name인 "Mike"가 this로 들어가 
                    //정상적으로 호출한다.

let fn = user.showName  //하지만 fn이라는 새로운 함수를 만들어
                        //user의 showName함수를 불러온다면,

fn();               //hello,
                    //이렇게 user(this).name을 잃어버리기 때문에 hello,만 출력된다.

let boundFn = fn.bind(user);    //따라서 boundFn이라는 새로운 함수를 만들고,
                                //변수 fn에 .bing(user)로 fn의 this의 값을
                                //user로 영구히 지정해버린다.

boundFn();          //hello, Mike
                    //따라서 user 변수가 맨위에 const로 생성되어 있기 때문에,
                    //boundFn()은 fn()이고, fn()은 user를 this로 받는 showName함수를 실행한다.

상속과 프로토타입(prototype)

  • 프로토타입 객체 (prototype) : 다른 객체의 원형이 되는 객체이다.
    모든 객체는 프로토타입 객체에 접근할 수 있다.
<Bmw는 redBmw의 프로토타입이다.>

const Bmw = function(color){
    this.color = color;		//Bmw의 color는 ()의 인수.
}

Bmw.prototype.wheels = 4;
Bmw.prototype.nav = 1;		//Bmw의 원형 객체, 즉 프로토타입을 각각 지정하였다.
Bmw.prototype.blackbox = 1;

const redBmw = new Bmw("red");	//redBmw를 Bmw의 red color로 생성.

console.log(redBmw);
//(아래사진)

그럼 redBmw의 현재 모든 프로퍼티는 무엇인지 살펴보자.

for(p in redBmw){
  console.log(p)
};		
		//color
		//wheels
		//nav
		//blackbox

하지만 Object.keys( )나 Object.values( )로 출력을 해보면,

console.log(Object.keys(redBmw));	//['color']
console.log(Object.values(redBmw))	//['red]

이렇게 프로토타입의 프로퍼티들은 출력되지 않는것을 확인 할 수 있다.
이를 구분해서 사용하자.

또, Bmw.prototype.wheels 말고 다른 방법으로 프로토타입 객체를 만들어 줄 수도 있다.

const car = {
  wheels: 4,
  nav: 1,
  blackbox: 1,
};

const bmw = {
  color : "white",
};

bmw.__proto__ = car;

console.log(bmw);
//(아래 사진)

이와 같이 (프로토타입 객체가 되는 변수명).__proto__ = (그것을 받는 객체)
(.언더바2개 proto 언더바2개) 를 사용해서 손쉽게 특정 객체를 프로토타입으로 지정해줄 수 있다.

다른 방법으로 원하는 객체에 직접 프로토타입 프로퍼티를 지정해줄 수 있으나,
이는 constructor(생성자)가 표기되지 않으므로 처음의 Bmw.prototype.wheels 의 방법을 주로 쓰거나, 만약 써야 한다면 직접 constructor를 지정해줘야 한다.

const Bmw = function (color) {
  this.color = color;
};

Bmw.prototype = {
  constructor: Bmw,	//Bmw.prototype의 constuctor(생성자)를 Bmw로 직접 지정해줘야 한다.
  wheels: 4,
  nav: 1,
  blackbox: 1,
};

const redBmw = new Bmw("red");

console.log(redBmw.constructor === Bmw);	//true

프로토타입은 상속의 개념이 적용된다.


클래스 Class

클래스는 생성자 함수와 역할이 동일하나, 사용이 비교적 편하고 new 없이는 사용을 못하는등 개발자가 버그를 찾아내는데에 좀 더 수월하게 한다.
클래스와 생성자함수는 어떤점이 다른지 살펴보자.

  1. 생성자함수와 달리 클래스는 new없이는 사용이 불가하다.
<User1은 생성자함수로, User2는 클래스로 만든 객체이다.>
  
const User1 = function (name, age) {
  this.name = name;
  this.age = age;
  this.showName = function () {
    console.log(this.name);
  };
};

const mike = /*new*/ User1("Mike", 30);	//undefined
console.log(mike);					//new를 빼도 undefined를 반환하여 에러가 발생하지 않는다.
					  //따라서 개발자는 new를 빠뜨린 실수를 했음에도 불구하고 에러를 확인하지 못한다.

class User2 {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  showname() {
    console.log(this.name);
  }
}

const tom = /*new*/ User2("Tom", 25);	//error
console.log(tom);						//하지만 클래스는 생성자함수와 다르게 에러를 반환한다.

new를 빠뜨려도 두가지가 다른 결과를 반환하는 이유는 여기에 있다.

보시다시피 클래스는 생성자함수와 다르게 constuctor(생성자)가 User2로 명시되어있다.
이게 바로 new를 쓰지않고서는 사용할 수 가 없는 이유다.

  1. 또한, 위 사진처럼 이 객체의 생성자가 어떤것인지 바로 알 수가 있다.
  1. for in문을 사용했을 때 프로토타입 프로퍼티까지 반환하지 않는다.
const User1 = function (name, age) {
  this.name = name;
  this.age = age;
};
User1.prototype.showName = function () {	//showName함수를 프로토타입 형태로 바꿈
    console.log(this.name);
  };

for (const p in mike) {
  console.log(p);
}	//name
	//age
	//showName

이 처럼 생성자함수로 만든 mike의 프로퍼티를 확인하면 프로토타입의 프로퍼티까지 
모두 반환되어 구분을 할 수 없다. 
따라서 프로퍼티가 객체만의 것인지 프로토타입의 것인지 확인하려면 hasOwnProperty를 사용해야했지만,
  
  

for (const p in tom) {
  console.log(p);
}	//name
	//age

클래스로 만든 tom은 객채의 프로퍼티만 반환하는것을 확인할 수 있다. 
  1. extends를 사용해 클래스간 상속이 가능하다.
class Car {
  constructor(color) {
    this.color = color;
    this.wheels = 4;
  }
  drive() {
    console.log("drive...");
  }
  stop() {
    console.log("STOP!!");
  }
}							//Car이라는 클래스를 생성하고,

class Bmw extends Car {		//extends를 사용해 Bmw가 Car의 프로퍼티들을 상속받는다.
  
  nav = 1;					//nav는 객체 프로퍼티로 들어감!
  park() {					//함수인 park는 프로토타입 프로퍼티로 들어감!
    console.log("PARK");
  }
}				

const redBmw = new Bmw("red");

console.log(redBmw);	//아래 사진

위와 같이 redBmw는 Car클래스에서 선언한 color와 wheels는 객체 프로피티로,
drive(),stop()은 프로토타입 프로퍼티로 들어가 있고,

Bmw가 Car를 상속받으면서 추가된 nav는 객체 프로퍼티,
park()는 프로토타입 프로퍼티로 들어가 있다!

  1. 오버라이딩(Overriding)
    : 프로퍼티가 중복될때, 자식 객체의 프로퍼티가 우선시 되어 덮어쓰인다.
    하지만, super.프로퍼티 를 사용해서 부모 객체의 값을 받아올 수 있다.
class Car {
  constructor(color) {
    this.color = color;
    this.wheels = 4;
  }
  drive() {
    console.log("drive...");
  }
  stop() {
    console.log("STOP!!");	//중복
  }
}
class Bmw extends Car {
  nav = 1;
  park() {
    console.log("PARK");
  }
  stop() {				//중복
    super.stop();		//이럴때 super.stop()을 넣어서 부모의 프로퍼티를 가져온다.					
    console.log("OFF!!");	//super를 넣지 않으면 그대로 자식 객체의 프로퍼티를 사용!
    						//'부모'의 stop을 쓰려면 아무것도 안쓰기
    						//'자식'의 stop을 쓰려면 덮어 씌우기(오버라이딩)
    						//'부모'와 '자식'의 stop을 모두 쓰려면 super.stop과 새로운 stop쓰기
  }
}

const redBmw = new Bmw("red");

console.log(redBmw);

constructor가 자식 객체에서 추가될때는, 부모의 constructor가 모두 생략되기 때문에
반드시 부모의 constructor를 super로 불러와야하며, 또한 부모의 constructor가 받는
인수들도 constructor와 super에 모두 대입시켜줘야 한다.

class Car {
  constructor(color) {
    this.color = color;
    this.wheels = 4;
  }
  drive() {
    console.log("drive...");
  }
  stop() {
    console.log("STOP!!");
  }
}
class Bmw extends Car {
  constructor(color) {		//this.nav가 포함된 새로운 constructor를 넣기 위해선,
    super(color);			//super로 부모 생성자를 불러오고, 그 안에 받는 인수도 
    						//부모와 동일하게 넣어줘야한다.(넣지 않으면 color의 값이 undefined 됨)
    this.nav = 1;
  }
}

const redBmw = new Bmw("red");

console.log(redBmw);

프로미스 (Promise)와 then, catch, finally

프로미스는 Promise(resolve, reject)로 정의하고,
then(성공,실패), catch(실패), finally(항상)로 그 값을 받는다.
위와 같이 then의 첫번째 인수는 성공했을때 값을, 두번째 인수는 실패했을때 값을 반환한다.
가독성을 위해 실패했을 경우는 catch를 쓰는것이 좋다.
(then(null, f)와 catch(f)는 완벽하게 같다.)

<pr이 성공일 때>
  
const pr = new Promise((resolve, reject) => {
  setTimeout(() => {
     resolve("Ok");
   }, 2000);		//2초 뒤에 pr을 성공시키고 ("Ok)를 출력.
});

pr.then((result) => {
  console.log(result + `, 가져가세요`);	//pr이 성공할때 받은 resolve를 result에 넣고,
  									    //result에 ', 가져가세요'를 붙여서 출력시킨다.
})
  .catch((err) => {
    console.log(err);
  })
  .finally(() => {
    console.log("끝");		//finally는 pr이 성공이든 실패든 항상 나오는 값!
  });

<pr이 실패일 때>
  
 const pr = new Promise((resolve, reject) => {
  setTimeout(() => {
     reject(new Error("err..."));
   }, 2000);		//2초 뒤에 pr을 실패시키고 새로운 Error를 생성: "err..."
});

pr.then((result) => {
  console.log(result + `, 가져가세요`);	
})
  .catch((err) => {
    console.log(err + `다시 실행하세요.`);		//실패 시 실행 된 reject값을 err로 가져온다.
  											//그리고 그 err에 "다시 실행하세요"를 붙이고 출력.
  })
  .finally(() => {
    console.log("끝");		//finally는 pr이 성공이든 실패든 항상 나오는 값!
  }); 

프로미스 체이닝 : 스크립트를 불러오는 것과 같이 순차적으로 처리해야 하는 비동기 작업이 여러 개
있을때 사용하면 좋다.


<프로미스 체이닝>
  
const order1 = () => {
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`1번 주문 완료`);
    }, 1000);
  });
};

const order2 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`2번 주문 완료`);
    }, 2000);
  });
};

const order3 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`3번 주문 완료`);
    }, 3000);
  });
};

order1()
  .then((result1) => order2(result1))		//order1의 res값을 result1으로 받고, 
											//order2의 인수로 넘겨줌.
  .then((result2) => order3(result2))		//order2의 res값을 result2으로 받고, 
											//order3의 인수로 넘겨줌.
  .then((result3) => console.log(result3))	//order3의 res값을 콘솔창에 출력.
  .catch(console.error)		//실패시 콘솔에 에러를 표시.
  .finally(() => {
    console.log(`주문이 끝났습니다.`);		//끝날때 항상 '주문이 끝났습니다' 출력.
  });

Promise.all( [ ] ) : 모든 프로미스를 동시에 이행한다. 주어진 프로미스 중 하나라도 거부하는 경우, 첫 번째로 거절한 프로미스의 이유를 사용해 자신도 거부한다.
예로 "하나의 정보라도 누락되면 페이지를 보여주거나/안보여주거나" 처럼 사용할 수 있다.

<promise.all을 사용해 모든 res값을 출력>
  
const order1 = () => {
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`1번 주문 완료`);
    }, 1000);
  });
};

const order2 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`2번 주문 완료`);
    }, 2000);
  });
};

const order3 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`3번 주문 완료`);
    }, 3000);
  });
};

Promise.all([order1(), order2(), order3()])		//promise.all은 매개변수를 배열로 받는다. 
											//배열안에 이행할 프로미스들을 지정해준다.
  .then((res) => {
    console.log(res);		//"모든"프로미스가 res일때 콘솔에 배열로 출력함.(promise.all이기 때문에)
  })
  .catch(console.error)		//promise.all은 하나의 프로미스라도 거부당하면 해당 catch문을 출력하고 모두 거부한다.
  .finally(() => {
    console.log("끝났습니다.");	//끝날때는 항상 "끝났습니다"를 출력함.
  });

Promise.race([ ]) : promise.all과 동일하게 사용하면 된다. 모든 작업이 끝날때 까지 기다리는 all과 달리, race는 가장 먼저 프로미스가 이행되면 즉시 수행을 끝낸다는 점이다.
용량이 큰 이미지들을 로딩하는데, 그 중 하나라도 완료되면 그 이미지를 보여주는 등 처럼 사용할 수 있다.

<위의 all코드와 동일하고 promise.all -> race로 만 바꾸었을 때>
const order1 = () => {
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`1번 주문 완료`);
    }, 1000);
  });
};

const order2 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`2번 주문 완료`);
    }, 2000);
  });
};

const order3 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`3번 주문 완료`);
    }, 3000);
  });
};

Promise.race([order1(), order2(), order3()])		//배열 내 프로미스 하나가 먼저 실행되면 즉시 중단
  .then((res) => {
    console.log(res);
  })
  .catch(console.error)
  .finally(() => {
    console.log("끝났습니다.");
  });

async과 await

async은 함수의 맨 앞에 추가해서 해당 함수가 항상 값을 프로미스로 반환하게 해준다.
await은 반드시 async함수 내에서만 사용 가능하며, 프로미스가 처리될 때 까지 기다린다.
위에서 사용했던 order코드에서 프로미스를 async과 await을 사용해서
코드를 줄이고 가독성을 높여보자.
async과 await


<asyncawait을 사용해서 코드 줄이기>
  
const order1 = () => {
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`1번 주문 완료`);
    }, 1000);
  });
};

const order2 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`2번 주문 완료`);
    }, 2000);
  });
};

const order3 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`3번 주문 완료`);
    }, 3000);
  });
};

async function doOrder() {	//반드시 function앞에 async 넣어주기
  try {
    const result1 = await order1();		//순차적으로 프로미스가 처리될때 까지 기다리는 중
    const result2 = await order2(result1);	//이 코드는 프로미스 체이닝을 했을때와 똑같다.
    const result3 = await order3(result2);	//then에 사용되는 코드 모두 try문으로 묶어준다.
    console.log(result3);
  } catch {
    console.log("에러가 발생했습니다.");
  } finally {
    console.log("주문이 끝났어유");
  }
}
doOrder();

동일한 코드에 Promise.all을 사용할 수도 있다.

const order1 = () => {
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`1번 주문 완료`);
    }, 1000);
  });
};

const order2 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`2번 주문 완료`);
    }, 2000);
  });
};

const order3 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(`3번 주문 완료`);
    }, 3000);
  });
};

async function doOrder() {
  try {
    const result = await Promise.all([order1(), order2(), order3()]);
    console.log(result);
  } catch {
    console.log("에러가 발생했습니다.");
  } finally {
    console.log("주문이 끝났어유");
  }
}
doOrder();

제너레이터 generator

제너레이터는 함수의 실행을 중간에 멈췄다가 재개할 수 있는 기능을 가지고 있는 함수다.
제네레이터 함수는 특별하게 function*으로 선언할 수 있고, 원하는 값을 yield(반환)문으로 나누어 next()를 사용해 가장 가까운 yield문을 만날때 까지 실행되고 객체를 반환한다.

function* generator() {
  console.log("첫번째");
  yield 1;		//반환되는 객체의 value는 yield의 오른쪽에 있는 값이다.
  console.log("두번째");
  yield 2;
  return "finish";
}

let a = generator();

`콘솔창에 입력`
a.next();
a.next();
a.next();

next로 찍어서 반환된 객체는 위의 사진과 같이 value 와 done 프로퍼티를 가진다.
value는 yield의 오른쪽에 있는 값이고, done은 함수 코드가 끝났는지를 확인한다.
(ture면 끝, flase면 진행 중)

제네레이터 함수는 next(), return(), throw() 메소드를 가지고 있다.
return은 입력 시 함수가 즉시 종료되고, done: true를 반환한다.

function* generator() {
  console.log(1);
  yield 1;
  console.log(2);
  yield 2;
  console.log(3);
  yield 3;
}

let a = generator();

`콘솔창에 입력`
a.next();
a.return();
a.return("finish");
a.next();

이후에 next()를 해도 value를 받을 수 없고 done은 true 상태를 유지한다.
return() 괄호 안의 인수가 반환된 객체의 value에 들어간다.

throw()는 제너레이터 함수 내에서 try, catch문에서 catch문을 호출할 때(즉, 에러를 반환할때) 사용할 수 있으며 이 역시 즉시 함수가 종료되고 done을 true로 반환한다.

function* generator() {
  try {
    console.log(1);
    yield 1;
    console.log(2);
    yield 2;
    console.log(3);
    yield 3;
  } catch (e) {
    console.log(e);		//catch문에서 받는 인수를 콘솔에 출력하도록 되어 있음.
  }
}

let a = generator();

`콘솔에 입력`
a.next();
a.throw(new Error("error!!!"));


throw 역시 즉시 함수가 종료 되고 done: true가 되는것을 확인할 수 있다.

next()에 인수를 직접 전달 할 수도 있다.

<a.next()를 입력할때 마다 어디에서 멈추는지 잘보기>
  
function* generator() {
  const num1 = yield "첫번째 숫자를 입력하세요";	//a.next() yield 문에서 멈추고, 다음에 입력하는 a.next()안의 인수가 num1의 값이 된다.(value는 yield 오른쪽에 있는 값이기 때문)
  console.log(num1);

  const num2 = yield "두번째 숫자를 입력하세요";
  console.log(num2);
  return num1 + num2;		//num1과 num2에 저장된 값을 더한 값을 리턴함.
}

let a = generator();

`콘솔에 입력`
a.next();
a.next(2);
a.next(4);


이처럼 제너레이터는 외부로 부터 값을 입력받을 수 있다.

제너레이터는 값을 미리 만들어 두지 않고 필요할때마다 그떄 그때 값을 처리한다.
즉, 필요한 순간까지 계산을 미루어 둘 수 있다는 장점이 있다.

<break가 없는 while(true)문으로 무한 반복을 막는 제너레이터 함수>
  
function* fn() {
  let i = 0;
  while (true) {		//이 함수는 일반 함수라면 무한히 반복되는 break가 없는 while(true)문이다.
    yield i++;		//하지만 제너레이터 함수를 사용하면 next를 입력할 떄마다 값을 받을 수 있다.
  }
}

const a = fn();

`콘솔창에 입력`
a.next();
a.next();
a.next();
a.next();

yield* 을 사용해서 다른 제너레이터를 불러올 수 있다.

function* fn() {
  yield "Y";
  yield "O";
  yield "N";
  yield "G";
}

function* fn2() {
  yield "Hello,";
  yield* fn();		//yield*로 제너레이터 함수 fn()을 불러온다.
  yield "!";		//yield* 오른쪽에는 반복가능한 모든 객체가 올 수 있다.
}

fn2를 구조분해할당으로 콘솔에 출력하여, fn 함수를 yield* 을 사용해 불러온걸 확인할 수 있다.
fn 을 불러올때, done이 true가 될때 까지 (for of문 처럼)값을 불러온다.

-끝-

profile
용호의 코딩블로그 22.11 코딩공부 시작 23.2 부트캠프 입소

0개의 댓글