TIL-6주차코드 - Day1~3

 

1. ES5 이미지 크롭 후 이동 [23.01.02]


0) 들어가기 전

  • 플랫폼보다는 객체지향을 더 공부하기!!
  • 우리조 1조임
  • 1조 발표 : 파란 사각형이 객체, 방향키가 사용자 입력, Exit
  • 꾸준함이 가장 중요하다.


1) 이미지 크롭하기(일부분을 잘라내기)


ctx.drawImage(img,106,296.5,106,148.25, 0, 0, 106, 148.25); 
// sx는 source(원본의 x크기), dx(display x크기,실제로 보여지는 크기)

  • 전체 화면에서 다른 부분을 크롭하면, 화면이 넘어가는 장면을 만들 수 있다.
  • 하지만, 이렇게 화면에 2개가 이미지가 나오면 머리가 아프다.(좌표 등등)

2) 이미지 크롭의 좌표 문제 해결 방법

  • 해결 방법: 이미지를 배열로 구성한다. 2차원 배열을 1차원 배열처럼 사용해서 좌표를 넣어주기!


  • a. Boy 객체 예제
 window.addEventListener("load", function(){

    // ===================  [1] [Boy 예제] ==========================


    // ---------------------- 8-2. window의 의미 ---------------------------------
    // 8-2. window의 적당한 위치 찾기!! 커다란 윈도우는 처음 pop이되고 영역이 되는 것을 브라우저라고 한다.

    // window라는 객체가 있다.
    // 브라우저는 플랫폼이다. api는 윈도우 객체이다.
    // api는 플랫폼이 없으면 의미가 없다.
    // window의 기생같은 자식 window를 embeded window라고 부른다.
    // window는 출력 영역이 있으며, 입력이 가능한 영역이 존재하다.(입출력이 가능하다!!) - 클릭이 가능하거나, 
    // 문서에서 보여주고 있는 모든 것들은 window이다.따라서 canvas도 window이다!!!(개념적!!)

    // window.onclick = function(){            // **** window 부분은 window만 클릭된다.
    //     console.log("window clicked");      // 실행 결과 후 따라서, window와 canvas는 부모와 자식간의 관계 느낌이다!!
    // }    


    // ---------------------- 1. 이미지가 한 개인 경우 ---------------------------------

    // 이것을 붙여줘야지 canvas의 메서드가 자동완성 한다.
    /** @type {CanvasRenderingContext2D} */ 
    
    var canvas = document.querySelector(".game-canvas");
    // document.querySelector는 css에서 style을 부여할 때, 이름을 명시해줄때, id명을 주거나 태그를 줄 수 있다.  
    

    //  ========== ========== 9. window 버블링 시스템  ==========  ==========  ========== 
    // **** 따라서, window에선 버블링이라는 기능이 있다.(계산기 성질과 같은 의미 - 버튼이 여러 개인 경우, 문제점 해결 )        
    // 자식에서부터 부모까지 다 실행한다.
    // 따라서, 각각의 이벤트 함수를 최대한 적게 사용해야 한다.

    canvas.onclick = function(){            // **** canvas는 window와 canvas가 동시에 클릭된다.
        console.log("canvas clicked");

        boy2.draw(ctx);
        boy2.move(2);
        boy2.clear();
    }
    
    

    var ctx = canvas.getContext('2d');          // 처음에 왜 안 읽혀졌지?
    
    // ---------------------- 2. 이미지가 여러개 인 경우 ---------------------------------

    // 이미지를 여러 개면, 각각의 이미지는 개별적으로 움직여야하고 각자 속성을 가지고 있어야 한다.
    // 따라서, 구조화 시켜야한다.

    // 3. 정면을 바라보고 있는 boy의 기본 형태(생성자 함수) 만들기!!
    // 하지만, 스크립트 언어 특성상 내가 속성이 없는 경우에 속성이 붙어버려서 에러가 난다.
    //

    function Boy(x, y) {         // *******
        this.x = x || 200;      // 6. 오버로드 생성자로 사용자가 넘겨줄 값이 있을 때, 가능하게 하려고 이렇게도 가능하다.
        this.y = y || 100;      // 사용자 입력이 없을 때는, x와 y는 || 뒤의 기본값이 들어간다.
        this.ix = 1;
        this.iy = 2;

        this.sw = 106;
        this.sh = 148.25;
        this.sx = this.sw * this.ix;
        this.sy = this.sh * this.iy;   
    }


    // 내가 처음에 만든것!!(이젠 이렇게 만들지 말자, 개념적으로 아래 코드와 비교!!)
    // boy1.prototype.draw = function(){
    //     var img = new Image();	
    //     this.drawImage(img, this.sx, this.sy, this.sw, this.sh,   
    //         200, 100, 106, 148.25); // sx는 source(원본의 x크기), dx(display의 x크기) 
    // }

    // *******
    Boy.prototype = {               // 4. prototype으로 객체로 만들기!!(캡슐화!!!!********)

        draw : function(ctx){
            var img = new Image();	
            img.src = './image/boy.png';

            //var o = this;                 // 굳이 이렇게 해야 하나?

            img.onload = function(){        //이렇게 만들면 에러가 나는 이유?

                // ctx.drawImage(img, this.sx, this.sy, this.sw, this.sh,   
                //     200, 100, 106, 148.25); // sx는 source(원본의 x크기), dx(display의 x크기)

                // 7. x 값을 초기화해서 10px로 이동시켜보기!!!!
                ctx.drawImage(img, this.sx, this.sy, this.sw, this.sh,   
                this.x, this.y, 106, 148.25);                           // sx는 source(원본의 x크기), dx(display의 x크기)
                

                //console.log(o);       // 5. function 안의 function이며 이미지가 실행 다 되어서 
                                        // img가 this가 되어 실행해준다. 원래는 Boy의 sx여야한다. 그래서, 뭘해야 하지?
            }.bind(this);               // 여기서는 Boy에서 img로 이벤트가 위임 되었다!!(중요)
                                        
                                        // 개념적인 부분 : 위임? 다시 이벤트 위임 공부, Function.prototype.call()은 모든 함수에서 call이 있어야 한다.
            
                                        // 중요!! 함수가 호출한 주체가 this가 된다. apply, call
            console.log(this);
        },

        
        // ========================  7. 캐릭터를 다른곳에 이동시키기 ========================== 
        // 추가적으로 boy2를 만들려면 어떻게 해야하는지?
        // 캐릭터를 이동시키려면 방향 코드가 필요하다.
        // 시계 방향!!
        move : function(dir){

            switch(dir){
                case 1: // 북쪽
                    this.y -= 10;       // 브라우저 기준으로 시작을 생각해보면 된다.
                break;

                case 2: // 동쪽
                    this.x += 10;
                break;
                
                case 3: // 남쪽
                    this.y += 10;
                break;
                
                case 4: // 서쪽
                    this.x -= 10;
                break;
                
            }
        },

        clear : function(){
            
            ctx.clear;
        }
        
    }
        
    var boy1 = new Boy();
    boy1.draw(ctx);



    // 브라우저의 렌더링 처리는 그림을 그리고 이동하는 것은 효율성을 위해서 한번에 처리된다.
    var boy2 = new Boy();
    
   // boy2.draw(ctx);

    // 이전에 for문으로 이동시키는 것과 onclick으로 이동시키는 것의 차이는?
    
    
    

    // ========================  8-1. 사용자 입력 받기 ========================== 
    // 사용자 입력이 있으면, window가 다운되지 않고 백그라운드가 돌아갈 수 있게 해야 한다.
    // window에서는 사용자의 입력(event)을 수신하는 능력이 있다. 이벤트를 확인해서 처리하는 능력도 있다.



  • b. Box 객체 예제
    // ======================  [2] [Box 예제] ============================
    // Box??

    function Box() {

    }

    Box.prototype = {
        test: function(x,y) {         // x와 y를 var로 선언안해줘도 된다.
            console.log(this);        // 함수에서 바로 가져온다.
            console.log(x);
            console.log(y);
        }
    };

    var box1 = new Box();
    box1.test();

    var f1 = box1.test;

    // // f1();                  // 여기서 앞에 window가 생략되어 있다. 

    // // var t = 3;             // 에러가 안난다. 여기서, t가 window이다.
    // // console.log(window.t); 

    // // // var obj = {kor:2};
    // // // obj.f1();               // 에러가 난다.

    // var obj = {kor:2};
    // obj.f1 = box1.test;     // box1의 test함수를 obj의 f1속성에 대입해준다.
    // obj.f1();               // 그 obj의 f1을 바로 호출해서 에러가 안 난다.

    var obj = {kor:2};
    obj.onload = box1.test; // box1의 test함수를 obj의 onload속성에 대입해준다.
    obj.onload();           // 그 obj의 onload를 바로 호출해서 에러가 안 난다.





    // **** 중요 내용 정리!!! (call, apply, bind)
    // // ====================================================================
    // // (1) 중요!! 함수가 호출한 주체가 this가 된다. call

    // var n1 = {id:1, title:'hello'};

    // // 여기서 전달하는 객체가 저 안의 객체가 된다.

    // obj.onload();
    // obj.onload.call(n1);

    // // console.log(n1);





    // // ====================================================================
    // // (2) apply는 argument를 배열로 집어 넣어준다.

    var n1 = {id:1, title:'hello'};

    // // 여기서 전달하는 객체가 저 안의 객체가 된다.(n1이 this가 된다.)

    obj.onload();
    obj.onload.apply(n1,['hi','okay']);







    // ====================================================================
    // (3) bind 중요!! 함수를 호출할 때, 바꿔치기를 해준다. 위임할 때, 나중에 호출
    // 함수를 호출하면서, 나중에 밖에 있는 this를 Boy객체가 되도록 해준다
    // ** bind 정리 : 밖에 있는 this(Boy)를 load에 bind 시켜줘서 안에 있는 this(img)에 위임시켜준다.



    // Box.prototype = {
    //     test: function(){
    //         console.log(this);
    //         console.log(x);
    //         console.log(y);
    //     }
    // };



    // //var img = document.createElement('image');	// 이런 방식도 가능하다!!
    // var img = new Image();			    // image 객체 만들기
    // img.src = './image/boy.png';		// image를 해당 경로로 가져오기 

    // //img.addEventListener("load", function(){  // 이렇게도 load 가능!! 
    // img.onload = function(){

    //     //ctx.drawImage(img,80,80);			// 바로 실행하기!

    //     //ctx.drawImage(img,100,100,106,148.25);	
                                                    
    //     //ctx.drawImage(img,106,296.5,106,148.25, 0, 0, 106, 148.25); // sx는 source(원본의 x크기), dx(display의 x크기) 
        
    //     var ix = 1;
    //     var iy = 2;

    //     var sw = 106;
    //     var sh = 148.25;
    //     var sx = sw*ix;
    //     var sy = sh*iy;
        
    //     ctx.drawImage(img,sx,sy,sw,sh, 
    //         200, 100, 106, 148.25); // sx는 source(원본의 x크기), dx(display의 x크기) 
        
    //     // dx나 dy를 키우면 이미지를 키울 수 있다.

    //     ctx.drawImage(img,sx+sw,sy+sh,sw,sh, 
    //         300, 200, 106, 148.25); // sx는 source(원본의 x크기), dx(display의 x크기) 
        
       
    //     var moveImage = function(ix, iy){
    //         ctx.drawImage(img,sx+sw*ix,sy+sh*iy,sw,sh, 
    //             100*iy, 200, 106, 148.25); // sx는 source(원본의 x크기), dx(display의 x크기) 
            
    //     }  

    //     moveImage(2,3);
    // }

    // ---------------------- 3. 이미지가 여러개 인 경우 ---------------------------------

    // 이미지를 여러 개면, 각각의 이미지는 개별적으로 움직여야하고 각자 속성을 가지고 있어야 한다.
    // 따라서, 구조화 시켜야한다.

});

2. ES5 방 정리 하기 : [23.01.03]


  • main.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

                                                    <!-- 순서가 중요하다.**** -->
    <script src="./item/boy.js"></script>
    <script src="./panel/game-canvas.js"></script>  
    <script src="./app.js"></script>

    <!-- 문서가 다 읽혀지고 읽어지는 방법 : html, js에서 해결 가능(document.addEventListener 이용) -->
    
</head>
<body>
      <!-- 주의사항 : css를 키우면 배율이 커진다. 그래서 속성을 키워야 한다. 즉, canvas를 키워야 한다.
           보통의 경우 속성을 키우고 css로 줄이는 경우는 이미지가 깨지는것을 방지해준다.  -->

      <canvas class="game-canvas" width="500" height="1000"></canvas>
</body>
</html>


  • app.js
window.addEventListener("load", function(){

    var gameCanvas = new GameCanvas();  // 다른 파일을 가져오는 방식이 자바스크립트에서는 없다.
                                        // 그래서 사용할 모듈을 html에게 요청한다.

                                        // 에러 : GameCanvas 객체를 생성시, 참조는 소문자에 넣어준다. 
    gameCanvas.run(); 
 
});


  • gameCanvas.js
// 역할을 주기 위해서 캡슐화를 하자
// GameCanvas는 run(). update(), draw()
// Boy는 move(d:number), draw()

// 호출할 때 전달, call과 apply
// 위임할 때 전달, bind

function GameCanvas() {
    this.dom = document.querySelector(".game-canvas");      // 여기서 this는 GameCanvas의 new된 결과이다.
                                                            // dom이 canvas이다. canvas가 this의 멤버로 들어온 것이다.
                                                            // 원래 java에서는 canvas가 canvas를 갖는 것이 이상하다. 
                                                            // 그래서, JS는 틀로 쓸 수 있는 것이 아니다.(프레임워크가 아니였다.)

                                                            // 앞으로는 this의 dom의 기능을 이용한다.
    
    /** @type {CanvasRenderingContext2D} */ 
    
    this.ctx = this.dom.getContext('2d');                   // var ctx가 아니라 this.ctx이다.(Boy의 ctx이므로)         
    
    this.boy = new Boy(100, 100);

    // ** 다른 애가 불러내서 위임되는 함수라고 부르며 "call back function"이라고 부른다. 
    // 사용자가 클릭해달라고 위임했기 때문이다. call back function은 지금 실행하는 것이 아니라 나중에 실행된다.(위임해야지 나중에 전화할 수 있기 때문이다.(나중에 실행))
    // 전화하는 애가 주체가 된다. 
    // dom이 다시 호출해준다? 
    //this.dom.onclick = this.clickHandler;                   // ()처럼 함수를 호출한 결과 값 넣지 말기

    this.dom.onclick = this.clickHandler.bind(this);          // 이렇게 사용하면 위의 코드와 비교해서 dom은 이전의 생성자에서 넘겨받아서 this가 dom이 아니다.
    //console.log(this)
}

GameCanvas.prototype = {
    run : function(){
        // 60프레임으로 화면을 다시 그리는 코드
        this.update();
        this.draw();
    },

    update : function(){

    },

    draw : function(){
        this.boy.draw(this.ctx);

    },

    // ---------- event handler--------------------------------
    clickHandler: function(){
        this.boy.move(2);

        // 화면 지우기

        this.boy.draw(this.ctx);    // 함수 호출자는 dom이다. 하지만, 우리는 this가 GameCanvas였으면 좋겠다. 서로 역할이 엇갈림.
                                    // 그래서 우리는 보따리를 쌓아 줘야한다.(위에서 bind로 묶어줌.) *******


        console.log(this);      // 위의 dom이 앞에서 위임받아서 dom이 아니라 이전 생성자이다.
    }

};



  • boy.js
// 원래 웹에서는 파일명을 가장 앞에는 소문자로 구분하고 대쉬(-)로 구분한다

// 중요!!!!**** 
// 캡슐화 vs 캡슐(데이터를 구조화한 것) 
// 캡슐화란 - 책임을 부여하는 것, 역할을 나누는 것, 기능을 부여하는 것
// 객체지향은 그림을 그릴 수 밖에 없고, 그 그림은 설계가 된다.

function Boy(x, y) {         // *******
    this.x = x || 200;      // 6. 오버로드 생성자로 사용자가 넘겨줄 값이 있을 때, 가능하게 하려고 이렇게도 가능하다.
    this.y = y || 100;      // 사용자 입력이 없을 때는, x와 y는 || 뒤의 기본값이 들어간다.
    this.ix = 1;
    this.iy = 2;

    this.sw = 106;
    this.sh = 148.25;
    this.sx = this.sw * this.ix;
    this.sy = this.sh * this.iy;   
}


Boy.prototype = {               // 4. prototype으로 객체로 만들기!!(캡슐화!!!!********)

    draw : function(ctx){
        var img = new Image();	
        img.src = './image/boy.png';

        img.onload = function(){        //이렇게 만들면 에러가 나는 이유?

            ctx.drawImage(img, this.sx, this.sy, this.sw, this.sh,   
            this.x, this.y, 106, 148.25);                           // sx는 source(원본의 x크기), dx(display의 x크기)
          
        }.bind(this);               // 여기서는 Boy에서 img로 이벤트가 위임 되었다!!(중요)
                                    
                                    // 개념적인 부분 : 위임? 다시 이벤트 위임 공부, Function.prototype.call()은 모든 함수에서 call이 있어야 한다.
        
                                    // 중요!! 함수가 호출한 주체가 this가 된다. apply, call
        console.log(this);
    },


    move : function(dir){

        switch(dir){
            case 1: // 북쪽
                this.y -= 10;       // 브라우저 기준으로 시작을 생각해보면 된다.
            break;

            case 2: // 동쪽
                this.x += 10;
            break;
            
            case 3: // 남쪽
                this.y += 10;
            break;
            
            case 4: // 서쪽
                this.x -= 10;
            break;
            
        }
    },

    
}

    // var boy1 = new Boy();
    // boy1.draw(ctx);

    // var boy2 = new Boy();
    
    // boy2.draw(ctx);
    // boy2.move(2);
    // boy2.draw(ctx);

3. ES6 JS : [23.01.03]

1) ES5 vs ES6

  • ES5

    • 모듈 시스템이 없다.(스코프 개념, import, export)
    • 객체 지향을 지원하지 않는다.(class 개념)
    • 비동기 처리를 위한 도구가 없다.(프로미스)
    • 인터페이스를 통해 심볼릭 필요

2) 변수 : let, const

  • let : 중괄호가 생기면 stack에 쌓이고 중괄호가 끝나면 변수도 사라진다.

  • const : final과 같은 역할이다. 상수처럼 값이 고정되어 있다.

  • 일반적인 변수는 let이다.

3) 디스트럭처링 (객체 뽀개기)


        // ES6 공부 시작
        let kor = 20;
        let eng = 30;

        // 기존 방식
        let obj = {
            kor: kor,
            eng: eng,
            total: function(){
                return this.kor + this.eng;
            }
        };

        function print(exam){
            let kor = exam.kor;         // 코드를 연산할 때, 더 간단하게 만들어준다!!!(집중화)
            let eng = exam.eng;
            //let total = exam.total.bind(exam); // 문제가 많다. 원래는 exam.total이여야 한다. 하지만, let 변수(let obj는 window에서 호출)에 의해서 exam이 아니라 window가 호출했다.
                                                 // 따라서, exam이 호출했다고 인식하도록 bind를 시켜주어야 한다.

                                                 // 호출되는 시점에 객체를 통해 호출을 한다.
                                                 // 그래서 bind하는 것이고 let을 이용하면, 지역변수가 된다.
                                                 // ** let으로 선언했기 때문에 print함수가 끝나면 let변수는 모두 사라진다. 

            let total = exam.total();            // 굳이 bind를 이용하지 않고 이런식으로 exam.total()을 변수 total에 담아도 된다.

            console.log("kor:"+ kor);
            console.log("eng: "+ eng);
            console.log("total: "+ total);
        }
        print(obj)

        // 새로운 방식!!([객체 뽀개기])
        let obj1 = {kor,eng,total(){   //  키와 값이 같다면 하나만 써도 된다. 함수를 이렇게 바로 쓸 수도 있다.
            return this.kor + this.eng;
        }};

        function print1({kor,eng}){           // 디스트턱처링!! : 객체 뽀개기. 객체를 쪼개서 3개로 나눠서 받는다.
                                              // 원래 데이터만 뽀개야한다. 함수는 뽀개는 것이 아니다!!******
            console.log("kor:"+ kor);
            console.log("eng: "+ eng);
            //console.log("total: "+ total());
        }

        print1(obj1);


        // [디스트럭쳐]의 또 다른 기능
        // 중괄호 : 오른쪽 객체 생성, 왼쪽은 변수 생성
        // 따라서, 중괄호로 변수를 선언했으면, 더이상 선언할 수 없다.
    

        let obj2 = {kor,eng,total(){   //  키와 값이 같다면 하나만 써도 된다. 함수를 이렇게 바로 쓸 수도 있다.
            return this.kor + this.eng;
        }};

        function print2(exam){  
            let {kor, eng, aa=9} = exam;      // 변수 선언만 한 것이다. 앞의 변수를 간단하게 만들어주는 것과 같다.
                                              // 또한, 이런 식으로 aa가 선언되면서 초기화 할 수 있다.
                                              
            console.log(aa);                  // 9가 출력
            
            
            console.log("kor: "+ kor);
            console.log("eng: "+ eng);
            // console.log("total: "+ total());
        }


        print2(obj2);

        // *** ES6 정리(23.01.03) : 클래스를 정의하고 기존 프로토 타입의 속성을 함수로 재구성
        // 변수 const, let 등장!!
        // 데이터를 뽀개는 방식으로 지역 변수 선언 방법이 새로 생김.



3. ES6 JS 정리 : [23.01.04]

  • 이제는 ES6에서는 이진법 사용 가능(8진수 , 2진수 등등)

  • 지역변수의 사용 가능(let, const) : 블록안에서만 라이프 사이클

  • var는 함수 안에서 어디서든 사용할 수 있었다.


1) let, const 사용

// ----------------------------------------- 
console.log(x);
var x = 3;              // undefined

// console.log(y);         // let으로 선언하면 그 변수에는 초기화되지 않는 쓰레기값이 들어간다.
// let y = 3;              // let은 선언과 상관없이 준비가 되어있기 때문이다.


2) 심볼의 개념 : 숫자는 의미가 없기 때문에 심볼로 의미를 부여 해준다.

// const로 사용하며, 대문자를 이용하여 변수명을 작성한다.

const N = 7;



3) Template String(문자를 출력할 때, 덧붙이는 과정) : 백틱(`) 기호 사용

let year = 2023;
let month = 1;
let day = 4;

let regdate = year+"-"+month+"-"+day;
console.log(regdate);

let template = `${year}-${month}-${day}`;     // 백틱에 의해서 감싸야지 템플릿 이용 가능(어금 기호?)
console.log(template);

// ** ES6 위주로 공부하기(ES5는 핵심적인 것만) // 익숙해지는 것이 중요하다.


4) 위의 템플릿을 써야하는 이유? : 싱글 코테이션 사용에 의해서

let product = '<section class="p-elect"></section>'       // ''이 ""와 섞여 있어서 복잡하다.

// JS 코드에서 내려쓰기 하고 싶으면, ``(백틱)을 이용해야 한다. (+를 사용하면, 코드가 복잡해진다.)
// 나중에 html 코드에 꽂아 넣을 때, 이용한다.*********


let className = 'p-elect';
let title = '스마트폰';

let product1 = `<section class="`+className+`">   // 불편한 방식
                    <h1>?</h1>
                </section>`;       
console.log(product1);                
                
let product2 = `<section class="${className}">  // 편한 방식()
                    <h1>${title}</h1>
                </section>`;       
               
console.log(product2); 

let product3 = String.raw`<section class="${className}">\n\n\n  // \n\n\n 그대로 출력할 수 있다.
                    <h1>${title}</h1>                           // String.raw 이용!                           //
                </section>`;     
               
console.log(product3); 

// ************ 암기하기!!



5) 향상된 JSON 객체 표현식 #1


이미지

  • function 키워드를 생략 가능



이미지
이미지

  • Computed Property? : ES6에서는 변수명을 왼쪽에 넣어버리는 경우도 발생하고 연산도 가능하다.


  • 진도를 빨리나가는 이유? : 게임에 적용해보려고


  • 실습!!
let attName = "kor";

// *** 유형 1
let exam = {
    [attName]: 10,
    eng: 20,
    math: 30
};

console.log(`kor:${exam.kor}`);


6) Object Destructuring

  • 데이터 구조는 중첩을 낮춰서 사용할 것 (.을 최대한 적게 사용해야 한다.)

  • 추가로 공부할 것 : 객체 속성에 그 객체 내부의 속성을 덧붙여서 출력하는 것(유튭에 있는 내용)

let {kor, eng:english, math=0} = exam;       // 뽀개는 것도 원하는 것만 뽑을 수도 있다.(굳이 3개 전부 뽑을 필요가 없다.)
                                             // 뽀개는 것을 원래 속성과 다르면 별칭으로도 사용 가능!(eng:english)

console.log(kor, english);       			// 기존의 eng에서 english로 속성을 바꾸면 undefined로 나오는데


// *** 유형 2
// JS에서는 객체가 중첩인 경우도 엄청 많다.

let exam1 = {
    [attName]:10,
    eng:20,
    math:30,
    student:{
        name:'newlec',
        phone:'010-2222-3333'
    }
};

console.log(`kor:${exam1.kor}`);

let {kor, eng:english, math=0, student:{name, phone}} = exam1;  // 위에서 객체의 중복 속성 표시 가능!!(이것도 중괄호가 중복됨) 

// let {kor, eng:english, math=0, student} = exam1;             // 이런식으로 중복 속성 쪼개기 가능 *****
// let {name, phone} = student;                                 // 중복 속성을 2줄에 나눠서 쪼개기 가능 


console.log(kor)
console.log(english)
console.log(phone)

//////// ** 정리 : 
 


  • ES6 함수 정의 방법 : Arrow Function
    • 자기 this가 없으니까 밖의 this를 사용한다. (자기만의 영역이 없다. 지역화가 없다.)
    • 자기만의 this가 없어서 bind를 사용하지 않는다.
	window.setTimeout(()=>{
		this.run();    
	},1000);

  • game study

    • app.js
      <script>
          window.addEventListener("load", function(){
    
          var gameCanvas = new GameCanvas();  // 다른 파일을 가져오는 방식이 자바스크립트에서는 없다.
                                              // 그래서 사용할 모듈을 html에게 요청한다.
    	
    	
                                              // 에러 : GameCanvas 객체를 생성시, 참조는 소문자에 넣어준다. 
          gameCanvas.run(); 
    	        
          // run()은 무한 loop해야 한다. UI 스레드는 별도의 흐름을 갖게 해야 한다.      
          // vscode에선 F12로 함수 선언과 이동 가능
    	 
      });
      </script>
    


    • game-canvas.js
      class GameCanvas {        // 클래스로 변경
    
      	constructor(){
          this.dom = document.querySelector(".game-canvas");  // 여기서 this는 GameCanvas의 new된 결과이다.
                                                              // dom이 canvas이다. canvas가 this의 멤버로 들어온 것이다.
                                                              // 원래 java에서는 canvas가 canvas를 갖는 것이 이상하다. 
                                                              // 그래서, JS는 틀로 쓸 수 있는 것이 아니다.(프레임워크가 아니였다.)
    
                                                              // 앞으로는 this의 dom의 기능을 이용한다.
        
          /** @type {CanvasRenderingContext2D} */ 
            
          this.ctx = this.dom.getContext('2d');   // var ctx가 아니라 this.ctx이다.(Boy의 ctx이므로)         
            
          this.boy = new Boy(100, 100);
    
          this.gameover = false;      // setTime을 위한 상태값!!****, 게임 오버는 아니고 애니메이션만 
                                      // 멈추게 해야 한다. 즉, 멈추면 상태값이 전부 날아가는 것이 
                                      // 아니라 정지인 상태이다.(배경과 적들은 움직임)
    
          this.pause = false;         // 그래서, 게임이 끝나는 것이 아니라 일시정지가 되어야 한다.
            
    
    
          // **** 다른 애가 불러내서 위임되는 함수라고 부르며 "call back function"이라고 부른다. 
          // 사용자가 클릭해달라고 위임했기 때문이다. call back function은 지금 실행하는 것이 아니라 나중에 실행된다.(위임해야지 나중에 전화할 수 있기 때문이다.(나중에 실행))
          // 전화하는 애가 주체가 된다. 
          // dom이 다시 호출해준다? 
          //this.dom.onclick = this.clickHandler;                   // ()처럼 함수를 호출한 결과 값 넣지 말기
    
          this.dom.onclick = this.clickHandler.bind(this);  		 // bind(this)의 this는 이벤트 핸들러를 위한 객체를 불러온다. 원래 this는 gamecanvas이다.
      }												 // 사용자가 클릭해달라고 요청하는 이벤트 핸들러이다.
        
        
              // 이렇게 사용하면 위의 코드와 비교해서 dom은 이전의 생성자에서 넘겨받아서 this가 dom이 아니다.
              //console.log(this)
    
    
      run(){
          if(this.pause)
              return;
    
          // 60프레임으로 화면을 다시 그리는 코드
          this.update();
          this.draw();
    
          // // 게임 만들 때, 심박동을 위해서 setTimeOut, setInterval, AnimationFrame 심박동을 우리가 맞출 수 없다.(조정이 불가능)
          // // setTimeOut - 1회성, setInterval - 반복성, AnimationFrame - 프레임 1초에 60회로 고정
          // window.setTimeout(function(){
          //     console.log("time out");    
          // },1000);
    
    
          // // this를 알게 하기 위해서 호출하는 주체를 봐야 한다.
          // window.setTimeout(function(){
          //     this.run().bind(this);      // 여기서 함수를 호출하는 것이 window라고 앞에 적혀 있어서    
          // },1000);                        // 그래서 우리는 여기 있는 run을 실행하기 위해서 bind를 사용한다!!
    
          // // ************ ES6 함수 정의 방법 : Arrow Function ************
          // // 자기 this가 없으니까 밖의 this를 사용한다. (자기만의 영역이 없다. 지역화가 없다.)
          // // 자기만의 this가 없어서 bind를 사용하지 않는다.****  
          // window.setTimeout(()=>{
          //     this.run();    
          // },1000);
    
          window.setTimeout(()=>{                 // 알람 맞추는 것과 같다.(60 프레임에 맞추어서)
              this.run();    
          },17);
    
          // 여기까지 하면, 무한히 반복한다. 멈추고 싶으면? 이 안에서 멈추는 도구가 필요하다. 그래서 상태변수가 필요하다.
    
      }
    
          // canvas는 UI이고 UI에 의해서 꼭두각시처럼 컨트롤된다.
    
      update(){   // update는 단위 계수마다 잘게 쪼개서 움직여야 한다.
                  // 시간에 국한에서 잘게 짜르려면 frame을 나누어야 한다. 
                  // 이동하는 속도가 일정하려면, 
    
          this.boy.update();               // 이동하고 
      }
        
      draw(){
          this.boy.draw(this.ctx);        // 다시 그리고
        
      }
    
      pause(){
            
      }
        
      // ------------------------------- event handler***** --------------------------------
      // 이벤트가 발생했다!! 사용자 입력에 의해서 이벤트가 발생 // 사건의 구체적인 정보를 알아야내 한다.(어떤 위치에 마우스를 클릭했는지, 어떤 키보드를 눌렀는지)
    
      clickHandler(e){         // 클릭하면, 타이머가 멈춘다!!
          //this.pause = true; // 같은 함수 내부라서 함수 호출 시, 
                               // 함수를 사용하는 것이 아니라 값을 입력해준다.
    
          // this.boy.move(2);    // 마우스로 move하는 것은 옳지 않고 방향키로 나중에 구현할 것
                                  // 마우스는 사용자 입력이다.
    
          this.boy.moveTo(e.x,e.y);   // 이동할 때, 대각선 길이를 잘게 쪼개는데 가로 세로는 그 값이 얼마였든지간에 비율로 대각선 길이로 나눈다.
                                      // 나중에 배우자! 이벤트 함수 : screen x, event x, osell x
    
    
          // 화면 지우기
        
          this.boy.draw(this.ctx);    // 함수 호출자는 dom이다. 하지만, 우리는 this가 GameCanvas였으면 좋겠다. 서로 역할이 엇갈림.
                                      // 그래서 우리는 보따리를 쌓아 줘야한다.(위에서 bind로 묶어줌.) *******
        
        
          console.log(this);      // 위의 dom이 앞에서 위임받아서 dom이 아니라 이전 생성자이다.
          }
                
      }
    
    	
    


    • boy.js
              // 원래 웹에서는 파일명을 가장 앞에는 소문자로 구분하고 대쉬(-)로 구분한다
    	
      // 중요!!!!**** 
      // 캡슐화 vs 캡슐(데이터를 구조화한 것) 
      // 캡슐화란 - 책임을 부여하는 것, 역할을 나누는 것, 기능을 부여하는 것
      // 객체지향은 그림을 그릴 수 밖에 없고, 그 그림은 설계가 된다.
    	
      class Boy {                     // *******
    	
          constructor(x,y){
              this.x = x || 200;      // 6. 오버로드 생성자로 사용자가 넘겨줄 값이 있을 때, 가능하게 하려고 이렇게도 가능하다.
              this.y = y || 100;      // 사용자 입력이 없을 때는, x와 y는 || 뒤의 기본값이 들어간다.
    	        
              this.vx = 0;            // 단위 위치(잘게 조개진 값)
              this.vy = 0;            
    	        
              this.dx = 0;            // 목적지 위치
              this.dy = 0;            
    	        
    	
              this.ix = 1;
              this.iy = 2;
    	    
              this.sw = 106;
              this.sh = 148.25;
              this.sx = this.sw * this.ix;
              this.sy = this.sh * this.iy;   
          }
    	
          draw(ctx){
              var img = new Image();	
              img.src = './image/boy.png';
    	
              img.onload = function(){    // 이렇게 만들면 에러가 나는 이유?
    	
                  ctx.drawImage(img, this.sx, this.sy, this.sw, this.sh,   
                  this.x, this.y, 106, 148.25);                           // sx는 source(원본의 x크기), dx(display의 x크기)
    	          
              }.bind(this);               // 여기서는 Boy에서 img로 이벤트가 위임 되었다!!(중요)
    	                                    
                                          // 개념적인 부분 : 위임? 다시 이벤트 위임 공부, Function.prototype.call()은 모든 함수에서 call이 있어야 한다.
    	        
                                          // 중요!! 함수가 호출한 주체가 this가 된다. apply, call
              console.log(this);
          }
    	
          update(){
              this.x += this.vx;                  // 여기서 목적직 위치까지 단위 위치로 더하면서 업데이트 시킴
              this.y += this.vy;
          }
    	
          moveTo(dx,dy){
              let w = dx - this.x;                // 목적지 위치 - 현재 위치(업데이트되고나서) = 남은 위치 거리 계산 
              let h = dy - this.y;
    	
              let d = Math.sqrt(w*w+h*h);         // 대각선 길이 공식
    	
              this.vx = w / d;                    // 여기 this도 주의해서 적기, 그 남은 위치 거리를 다시 잘게 쪼개진 비율로 나누어서 이동한다.
              this.vy = h / d;
          }
    	
          move(dir){
    	
              switch(dir){
                  case 1: // 북쪽
                      this.y -= 1;       // 브라우저 기준으로 시작을 생각해보면 된다.
                  break;
    	
                  case 2: // 동쪽
                      this.x += 1;
                  break;
    	            
                  case 3: // 남쪽
                      this.y += 1;
                  break;
    	            
                  case 4: // 서쪽
                      this.x -= 1;
                  break;
    	            
              }
          }
         }