본문 바로가기
JavaScript 기초

[JavaScript] apply(), call(), bind()와 this 키워드

by 개미는뚠뚠딴 2020. 10. 1.
반응형

오늘은 .apply(), .call(), .bind() 함수와 this 키워드에 대해서 알아보았다. 

 

apply() 메서드는 주어진 this 값과 배열 (또는 유사 배열 객체)로 제공되는 arguments로 함수를 호출해준다. call()과 기능이 거의 동일하지만 call()은 인수 목록을 받는 반면에 apply()는 인수 배열 하나를 받는다.

 

bind() 메서드는 호출되면 새로운 함수를 생성한다. bind()의 첫 인자는 this 키워드로 설정하고, 이어지는 인자들은 바인드 된 함수의 인수에 제공된다. 

 

여기서 this는 함수가 실행되는 동안 자동적으로 생성되는 특수한 식별자로 함수의 범위를 나타낸다고 보면된다. (또는 부모 객체)

설정이 안 되어있는 경우 기본적으로는 window 객체를 가리킨다고 한다. (전역 범위)

 

js의 this는 다른 언어에서 사용되는 의미와는 조금 다른 의미라 헷갈리는 개념이기도 했다. 이 부분은 추후에 보강해야겠다.

 

어쨌든 .apply()와 .call(), .bind()를 사용하기 위해서는 this를 이해해야 하는데 부모 객체 또는 함수가 호출되는 범위로 생각하면 어느 정도 이해가 갔다.

 

 

함수를 실행시키는 방법은 여러 가지(new 키워드를 이용한 호출, function 호출,... )가 있는데 함수 메서드 .call()과 .apply(), .bind()를 이용한 방법에 대해서 알아보겠다.

 

function add(x, y){
  this.val = x + y;
  console.log(this.val);
}

let obj = { val: 0 };

// 둘의 기능은 유사하지만 apply는 인수 배열 하나를 인자로 받고, call은 인수의 목록을 인자로 받는다.
add.apply(obj, [2, 8]); // 10
add.call(obj, 2, 8); // 10

 

.bind()는 .call(), .apply() 와는 다르게 함수를 바로 실행시키지 않고, this 값이 바인딩된 함수를 리턴해준다.

 

function add(x, y){
  this.val = x + y;
  console.log(this.val);
}

let obj = { val: 0 };

let boundFn = add.bind(obj, 2, 8); // -> 함수
boundFn(); // 실행, 10;

 

apply와 call을 이용하면 메서드와 인스턴스의 순서를 바꿀 수도 있는데 이러한 패턴은 유사 배열 객체에 적용시킬 때 유용하다. 또한 배열이 적용 안 되는 내장 함수들에도 적용할 수 있다.

 

let num = [2, 3, 1, 5, 7, 8];

Math.min(num); // NaN 에러 발생

// apply()의 첫번째 인자가 가리키는 값이 없으므로 null
Math.min.apply(null, num); // 1
let list = document.querySelectorAll('li'); // NodeList -> 배열처럼 보이지만 배열 메소드는 사용할 수 없다.

list.filter(function(el){ // not a function Error 발생
  return el.classList.contains('className'); // li안에 className이라는 class를 가지고 있는지 확인 
});

// 메서드를 먼저 기술하고, 그 뒤에 인스턴스(this)를 넘겨줌
Array.prototype.filter.call(list, function(elList){
  return elList.classList.contains('className');
});

 

또한 bind는 특정 함수가 this값을 바꿔버렸을 때를 방지할 수 있다.

 

function Num(n){
  this.n = n;
  
  this.getSquared = function(){
    return this.n * this.n;
    }
  this.printSquared = function(){
    console.log(this.getSquared());
  } 
}

let myNum = new Num(8);
myNum.printSquared(); // 64

setTimeout(myNum.printSquared, 5000); // 5초후 함수 실행 -> not a function Error

// setTimeout의 경우 인자로 넘기는 함수의 this 값은, 기본적으로 window 객체가 바인딩됨.

setTimeout(myNum.printSquared.bind(myNum), 5000); // bind()를 이용해 this값을 인스턴스 myNum으로 지정

 

그리고 bind를 이용해 currying을 구현할 수도 있는데 

 

function template(name, age){
  return `<h2>${name}</h2> <span>${age}</span>`;
}

// 인자를 여러개 받는 함수를 인자 하나만 받는 함수로 바꿈
// template 함수안에는 this 값을 사용하지 않으므로 다른 값을 넣어주지 않아도 된다.
let tmplSteve = template.bind(null, 'Steve');
tmplSteve(22); // <h2>Steve</h2> <span>22</span>

let tmplJonn = template.bind(null, 'Jonn');
tmplJonn(25); // <h2>Jonn</h2> <span>25</span>
tmplJonn(30); // <h2>Jonn</h2> <span>30</span>

// 인자를 모두 넣어주는것도 가능
let tmplNick = template.bind(null, 'Nick', 17);
tmplNick(); // <h2>Nick</h2> <span>17</span>

 

아직은 개념이 좀 헷갈리지만 함수와 인스턴스의 위치를 바꿀 수 있고 유사 배열 객체에 배열 함수를 적용할 수 있다는 점이 참 유용한 것 같다. 또한 템플릿처럼 사용이 가능하니 사용법을 꼭 익혀둬야겠다.

반응형

댓글