[JavaScript] JS_ 6. This

# INDEX

1. Values & Data type
2. Operators
3. Control flow
4. Scope & Hoisting
5. Object & Array
6. This
7. Prototype & Inheritance
8. Function
9. Callback function
10. Closuer
11. Class
12. Others

6. This

6-1. this에 대한 기본 개념

  • JS에서 함수는, 매개변수로 전달되는 인자값 이외에, arguments 객체와 this 객체를 암묵적으로 전달 받기 때문에, 모든 함수 내부에서는 (화살표함수 제외) argumentsthis를 사용할 수 있음
  • JS의 this 키워드는 항상 함수 내부에서 사용됨
  • this 값은 this가 사용된 함수가 "어떻게 실행되었는지"에 따라 동적으로 결정됨
  • 따라서, this 값을 판별하기 위해서는 함수의 실행문을 찾고 + 해당 함수가 어떻게 실행되었는지를 판별해야 함

6-2. 함수 호출(실행)방식

let foo = function () {console.dir(this);};

1) "일반 함수 호출" (`Regular function call`) 
	foo(); 		// window // window.foo();

2) "메소드 호출" ('Dot Notation (Object Method Call)')
	let obj = {foo : foo};
	obj.foo(); 	// object // {foo: ƒ}

3) "생성자 함수 호출" ('"new" Keyword')
	let instance = new foo();	// instance // foo {}

4) "call / apply / bind 호출" (`call / apply / bind`)
	let jazz = { name: 'delilah' };
	foo.call(jazz);   // Object // name: "delilah"
	foo.apply(jazz);  // Object // name: "delilah"
	foo.bind(jazz)(); // Object // name: "delilah"

6-3. (1) 일반 함수 호출_Regular function call

  • 일반 함수 실행 방식의 경우 : 해당 함수의 this 값은 "Global Object" (브라우저에서는 window객체)
  • 보통의 경우, this가 window가 되는 것은 버그... (굳이 이렇게 사용하지 않음)

Non Strict Mode : Global Object

          var age = 30;                         //글로벌 객체   // 5. 이 경우에는 this가 window
          var person = {
            age: 20,
            printAge: function () {
              bar();                            // 3. 해당 함수의 실행문 찾기     // 4. 실행문의 형태에 따라 this의 값 결정
            }
          };

          function bar () {                     // 2. this가 포함된 함수 찾기
            console.log(this.age);      //30    // 1. 먼저 this찾기
          }
          person.printAge();

Strict Mode : undefined

          'use strict';
          var name = 'delilah';
          function foo () {
            console.log(this.name);     //can not read property 'name' of undefined at foo
          }
          foo();

6-4. (2) 메소드 호출_Dot Notation (Object Method Call)

  • dot notation 을 이용하여 함수를 실행할 경우 : 해당함수 내부의 this는 함수실행문에서 "Dot앞에 놓인 객체"를 뜻함
  • tip!! dot notation은 실행문을 보고 하나하나 찾아나가는 것이 핵심!
        var age = 100;
        var delilah = {
          age : 35,
          foo : function foo () {
            console.log(this.age);    //console.log(delilah.age) 와 같은 의미   //35
          }
        };
        delilah.foo();                //함수 실행문 here! => 실행문 앞에 .(dot)이 위치하며, 그 부분은 객체 delilah 되므로 this는 delilah
        --------------------------------------------------------------------------------------------
        var age = 100;
        var delilah = {
          age : 35,
          foo : function bar () {
            console.log(this.age); 	// 아래 호출방식에 따라 값이 달라짐
          }
        };
        var willy = {
          age : 31,
          foo : delilah.foo
        };
        var foo = delilah.foo; 		// 이거는 함수

        delilah.foo();           //35
        willy.foo();             //31
        foo();                   //100 (window)
      --------------------------------------------------------------------------------------------
        // 어렵지만 차근차근... ㅎㅎㅎ
      
      	var age = 100;
        function verifyAge () {             // 2.함수 verifyAge의 내부함수이다
          return this.age > 21;             // 1.this를 찾는다  //8. 20>21의 return value = false
        }
        var delilah = {
          age : 20,                         // 7.delilah(.dot)age는 20
          verifyAge : verifyAge
        };
        var sevenEleven = {
          sellBeer : (customer) => {        // 5.this는 매개변수 customer
            if (!(customer.verifyAge())) {  // 3.verifyAge의 실행문을 찾는다   // 4.customer(매개변수)가 this객체가 된다
              return "No beer";             // 9.verifyAge가 false => 의 반대는 => true => 따라서, 출력1 ("No beer")
            } else {
              return "Beer";
            }
          }
        }
        sevenEleven.sellBeer(delilah);          // 6.변수sevenEleven의 프로퍼티 sellBeer에서 매개변수 delilah   // 10.따라서, return "Beer"
        --------------------------------------------------------------------------------------------
        function makePerson (name, age) {   // 2. 이 makePerson의 실행결과는 {}객체 => 이것이 delilah가 된다
          return {
            name,
            age,
            verifyAge: () => {              // 4. 여기!   // 6. BUT! 여기는 현재 '화살표함수' 따라서, this가 없음
              return this.age > 21;         // 4. 여기!   // 6. this를 찾아 올라가면 함수 makePerson을 찾을 수 있다
            }                               // 9. 따라서, 이 this는 global obj  // 10. 결과적으로 undefined>21 는 false
          };
        }
        
        const delilah = makePerson("delilah", 30);  // 1. delilah라는 변수에 담기는 것은? 함수 makePerson의 실행결과   // 7. makePerson의 실행   // 8. 어떻게 실행? 일반함수
        
        if (delilah.verifyAge()) {              // 3. delilah.verifyAge 가 가르키는 것은?   // 5. 4번의 함수를 여기서 실행!
          alert("Yes, Beer!");
        } else {
          alert("No, Beer!");               // 11. 따라서, false값을 출력! ("No, Beer!")
        }
        --------------------------------------------------------------------------------------------

6-5. (3) 생성자 함수 호출_new Keyword

  • 프로토타입과 이어지는 문맥
  • 새로운 빈 객체가 this로 할당됨
  • 생성자 호출은 객체.메소드() 과 같이 객체 내에 메소드를 호출하는 방법과 비슷하지만, 객체가 new 키워드를 이용해서 만들어졌다는 것이 다름. 이 때의 객체를 인스턴스라고 함 / 인스턴스.메소드()
	class Counter {
	  constructor() {
	    this.value = 0; // 생성자 호출을 할 경우, this는 new 키워드로 생성한 Counter의 인스턴스입니다
	  }
	  increase() {
	    this.value++
	  }
	  decrease() {
	    this.value--
	  }
	  getValue() {
	    return this.value
	  }
	}

	let counter1 = new Counter() // 생성자 호출
	counter1.increase()
	counter1.getValue() // 1
-----------------------------------------------------------------------
      function foo () {
        // this = {}; =>라고 생각하면 쉬움
        console.log(this) // foo {}
      }
      new foo();      	  // foo () = {}
-----------------------------------------------------------------------
      function foo () {
        // this = {}; =>라고 생각하면 쉬움
        this.age = 30;
        this.logName = function () {console.log("name")}; // 처럼 만들어줄 수도 있음
        console.log(this)
      }
      new foo();      // foo () = {}
      

6-6. (4) call / apply / bind 호출

  • 모든 JS 객체에는 내장되어 있는 메소드를 쓸 수 있음 (toString, slice, push, pop 등등)
  • 함수도 객체, 배열도 객체이기 때문에 메소드를 쓸 수 있음
  • 함수에도 내장되어 있는 메소드가 따로 있음 (.call .apply .bind 도 그 중에 하나)
  • 함수는 단순히 소괄호를 열고 닫는 방법 () 외에도, 메소드를 이용해 실행할 수 있음 (ex. foo( ) or foo.call( ) )

1) Funcion.prototype.call

  • .call, .apply 호출은 명시적으로 this를 지정하고 싶을 때 사용
  • 첫 번째 인자로 받은 값을 해당 함수의 this로 설정하여 함수를 실행
  • 두 번째 인자부터 ~ 나머지 인자들은, 해당 함수의 인자로 전달
  • .call 메소드는 받을 수 있는 인자의 갯수 제한이 없음
  • 메소드가 사용된 함수를 실행시킴
    function foo() {}   // foo함수 생성
    foo();              // foo함수 실행
    foo.call();         // foo함수에 call이라는 메소드를 이용해 호출
    foo.call(person);   // foo함수를. 실행하려고 하는데. this를 person값으로 해!
    
      // ex)
      function logAge () {
        console.log(this.age);  // 20
      }
      const person = {
        age: 20
      };
      logAge.call(person);  	// call메소드가 실행된 함수logAge를 실행하는데, 인자를 person으로 지정
    
            위의 예제에서 logAge.call(person) 구문의 2가지 기능!!
            (1) logAge 함수의 this, 첫 번째 인자로 받은 person으로 설정한다.
            (2) logAge 함수를 실행한다. (함수 내부 구문들이 실행된다.)
------------------------------------------------------------------
      // ex)
      let status = "선글라스";
      setTimeout(()=> {
        const status = "하트";
        const data = {
          status : "아보카도",
          getStatus : function () {
            return this.status;
          }
        };
        console.log(data.getStatus());            
        	//"아보카도" //data.status가 된다
        console.log(data.getStatus.call(this));   
        	//"선글라스" 
        	//getStatus함수의 this를 인자this로 설정한 후 실행. 
        	//but, 해당함수는 화살표인자라 undefined가 되어, 밖으로 나가 global obj로!
      },1000);

2) Function.prototype.apply

  • .call, .apply 호출은 명시적으로 this를 지정하고 싶을 때 사용
  • 첫 번째 인자로 받은 값을 해당 함수의 this로 설정하여 함수를 실행
  • 두 번째 인자는 반드시 "배열"이여야 하며, 해당 배열의 요소들이 해당 함수의 인자로 전달
  • 기본적으로 .call 메소드와 매우 유사하지만, 단지 "두개의 인자"만을 받을 수 있음
  • call보다 동적인 상황에 좀 더 어울림
  • 메소드가 사용된 함수를 실행시킴
    (인자의 개수가 매개변수보다 모자란 경우, NaN)
    (인자의 개수가 매개변수보다 많은 경우, 마지막 인자는 무시)
  • spread operator의 도입으로 굳이 apply를 이용할 필요가 없어짐
    ex. Math.max(...[5,4,1,6,2]) // 6
// 간단한 call과 apply의 쓰임의 차이점
	function add(x,y) {
		return this+x+y;
	}
	add.call(1,2,3)// 6
----------------------------------------
	function add(x,y) {
		return this+x+y;
	}
	add.call(1,[2,3])// 6

3) Function.prototype.bind

  • bind가 일반함수호출보다 strong
  • 첫번째 인자는 this, 두번째 인자부터는 필요한 파라미터를 전달
    function.bind (this값, 인자1, 인자2, ...)
  • .bind는 .call과 유사하게 this 및 인자를 바인딩하나, 당장 실행하는 것이 아닌 바인딩된 함수를 리턴하는 함수
  • bind메소드는 원본 함수를 복제한 "새로운 함수"를 반환
  • call 메소드와 유사하게, 받을 수 있는 인자의 갯수에 대한 제한이 없음
      function foo () {
        console.log("hello");
      }
      const bar = foo.bind();   // .bind 메소드는 새로운 "함수"를 반환 O && 실행 X
      foo.bind()();             // 위와 동일값 / 조금 이상... 따라서 보통 변수를 따로 설정해준다
      bar();                    // 이제 bar에는 함수가 담겼기 때문에, bar()로 실행해주어야 한다
      bar;                      // function foo () {console.log("hello")} 를 반환
                                // 복제를 한 후, 복제된 함수를 실행해야, 기존의 foo 함수를 실행 할 수 있다.
------------------------------------------------------------------------------------------------
      function foo (a,b,c) {
        console.log(this.age);   // 35   // this에 delilah // console.log(delilah.age);
        console.log(a+b+c);      // 6
      }
      const delilah = {
        age : 35
      };
      const bar = foo.bind(delilah,1);   // bar = function foo 이며, this에 delilah이 들어가고, 인자로 1
      bar(2,3);
------------------------------------------------------------------------------------------------ 
      function foo (a,b,c,d,e,f) {
        console.log(this.age);
        console.log(a+b+C+d+e+f);
      }
      const delilah = {
        age : 35
      };                                 // 1->a, 2->b, 3->c.
      const bar = foo.bind(delilah,1,2,3);   // 변수bar에는 함수foo가 담기며, this는 delilah, 인자로는 abc에 각각 123.
      bar(4,5,6);                        // 4->d, 5->e, 6->f.

** bind / new / 인스턴스 / prototype

좋은 웹페이지 즐겨찾기