명시적으로 this를 바인딩하는 방법

신은수·2023년 4월 28일
0

VanillaJS

목록 보기
7/11

1) call 메서드

Function.prototype.call(thisArg[,arg1[,arg2[, …]]])

  • call 메서드는 (call)메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령
  • 메서드의 첫번째 인자를 this로 바인딩, 이후의 인자들을 호출할 함수의 매개변수로 함
var func = function(a,b,c){
	console.log(this, a,b,c);
}

func(1,2,3); // Window{...} 1 2 3
func.call({x: 1}, 4,5,6); // {x: 1} 4 5 6

2) apply 메서드

Function.prototype.apply(thisArg[, argsArray])

  • call 메서드와 기능적으로 완전히 동일
  • apply 메서드는 두번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정
var func = function(a,b,c){
	console.log(this,a,b,c);
}

func.apply({x: 1}, [4,5,6]); // {x: 1} 4 5 6

3) call / apply 메서드의 활용

a) 유사배열 객체에 배열 메서드를 적용

  • 객체에는 배열 메서드를 직접 적용할 수 없음

  • 하지만 키가 0 또는 양의 정수인 프로퍼티가 존재하고, length 프로퍼티의 값이 0 또는 양의 정수인 객체, 즉 유사배열 객체의 경우 call 또는 apply 메서드를 이용해 배열 메서드를 차용 가능

    • 예제1)

      var obj = {
      	0: 'a',
      	1: 'b',
      	2: 'c',
      	length: 3
      }
      
      // object에 push메서드를 쓰는 것은 원래는 안 되는데 
      // call을 쓰면 array의 push메서드를 object에 쓰는 것이 가능해진다.
      Array.prototype.push.call(obj, 'd'); 
      console.log(obj); // {0:'a', 1:'b', 2:'c', 3:'d', length: 4}
      
      // slice에 인자가 없으면 얕은 복사를 하겠다라는 의미.
      var arr = Array.prototype.slice.call(obj);
      console.log(arr); // ['a','b','c','d']
    • 예제2)

      // 함수 내부에서 접근할 수 있는 arguments 객체도 유사배열객체임
      function a(){
      	console.log(arguments);
      	var argv = Array.prototype.slice.call(arguments);
      	argv.forEach(function (arg){
      		console.log(arg); 
      	}
      }
      
      a(1,2,3);
      • 정답!

        -> 대괄호([ ]) 있는데 배열 아니야?
        -> [ ]로 둘러싸여있다고 다 배열이 아니야
        -> Array.isArray(arguments) → false나옴
    • 예제3)

      document.body.innerHTML = '<div>a</div><div>b</div><div>c</div>'
      var nodeList = document.querySelectorAll('div');
      console.log(nodeList)
      console.log(Array.isArray(nodeList)) // false 따라서 NodeList 도 유사배열 객체
      
      var nodeArr = Array.prototype.slice.call(nodeList);
      nodeArr.forEach(function(node){
          console.log(node);
      })
      
      // nodeList.forEach(item=>console.log(item)); 은 될까 안될까?

      • 정답
        됨!!

        오잉 유사배열객체에는 배열의 메서드 쓰려면 call 메서드 써야하는 것 아니었남?

        ⇒ nodes는 프로토타입에 forEach가 있어서 된다.

        => 반면, document.getElementsById 와 같은 HTMLCollection은 call메서드를 써야한다.

b) 생성자 내부에서 다른 생성자를 호출

  • 생성자 내부에서 다른 생성자와 공통된 내용이 있을 경우 call 또는 apply를 이용해 다른 생성자를 호출하면 간단하게 반복을 줄일 수 있음
    function Person(name,gender){
    	this.name = name;
    	this.gender = gender;
    	this.sleep = function(){console.log(`${this.name}가 잠을 잔다`);}
    }
    function Student(name,gender,school){
    	Person.call(this, name, gender);
    	this.school = school;
    }
    const eunsu = new Student("eunsu", "female", "HUniversity");
    eunsu.sleep() // eunsu가 잠을 잔다                         
    // class의 상속과 비슷한 기능일까?

c) 여러 인수를 묶어 하나의 배열로 전달하고 싶을 때 - apply활용

  • 예제 Math.max()

    var numbers = [10,20,30,40];
    var max = Math.max.apply(null, numbers); // 여기서 null이 의미하는 것은 무엇일까?

4) bind 메서드

  • call 과 비슷하지만 즉시 호출하지는 않고, 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드
    var func = function(a,b,c,d){
    	console.log(this,a,b,c,d);
    }
    
    func(1,2,3,4); // 
    func.call({x:1}, 1,2,3,4}) 
    
    var bindFunc1 = func.bind({x:1})
    bindFunc1(5,6,7,8); 
  • (bind메서드를 사용하여) 상위 컨텍스트의 this를 내부함수나 콜백함수에 전달하기

    var obj = {
        outer: function(){
            console.log(this); // {outer: …} 
            var innerFunc = function(){
                console.log(this); // Window {…} 
            }
            innerFunc();
        }
    };
    
    obj.outer();
    var obj ={
        outer: function(){
            console.log(this); 
            var innerFunc = function(){
                console.log(this); // {outer: …} 
            }.bind(this)
            innerFunc();
        }
    }
    
    obj.outer();

    호출주체가 없을 때 자동으로 전역객체를 바인딩하지 않고, 호출 당시 주변환경의 this를 그대로 상속받아 사용할 수 있다면 좋겠다.(p.74) ⇒ 대표적인 방법 2가지(화살표함수, 변수활용)


5) 별도의 인자로 this를 받는 경우

  • forEach에 별도의 인자를 넣지 않은경우
    - 콜백함수의 제어권을 갖는 forEach함수가 this를 무엇으로 할 지 결정하는데 지정안하면 this는 전역객체가 됨

    var report = {
        sum: 0,
        count: 0,
        add: function(){
            var args = Array.prototype.slice.call(arguments);
            args.forEach(function(entry){
                this.sum += entry; // 여기서 this는? -> Window
                ++this.count;
            });
        }
    }
    
    report.add(60,85,95);
    console.log(report.sum);
  • forEach의 두번째 인자에 this를 지정한 경우

    var report = {
        sum: 0,
        count: 0,
        add: function(){
            var args = Array.prototype.slice.call(arguments);
            args.forEach(function(entry){
                this.sum += entry;
                ++this.count;
            }, this);
        }
    }
    
    report.add(60,85,95);
    console.log(report.sum);
  • 콜백함수와 함께 thisArg를 인자로 받는 메서드

    Array.prototype.forEach(callback[, thisArg])
     Array.prototype.map(callback[, thisArg])
     Array.prototype.filter(callback[, thisArg])
     Array.prototype.some(callback[, thisArg])
     Array.prototype.every(callback[, thisArg])
    
    ...
profile
🙌꿈꾸는 프론트엔드 개발자 신은수입니당🙌

0개의 댓글