본문 바로가기
Programming/자바스크립트

[자바스크립트] 명시적 this 바인딩

by 코딩하는 랄로 2023. 11. 3.
728x90

명시적 this 바인딩

저번 포스팅에서는 this의 기본 개념과 this가 어디에서 호출되는 지에 따라 어떤 객체가 바인딩되는지에 대해서 알아보았다. 이번 포스팅에서는 this를 임의로 바인딩 해주는 명시적 바인딩에 대해서 알아보겠다.

 

명시적 바인딩을 하기 위한 메소드로는 함수 객체(Function Object)의 정적 메소드로 apply(), call(), bind() 메소드를 사용할 수 있다. 아래의 예제를 통해 각각의 메소드의 사용방법에 대해서 알아보자.

 

https://codingralro.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-This

 

[자바스크립트] this - 개념

this란? 프로그래밍을 공부하다보면, this라는 키워드를 많이 마주하게 치게 된다. this는 영어로 이것이라는 의미로 프로그래밍 언어에서는 this를 호출한 '이것'을 의미한다. 그렇기 때문에 this는

codingralro.tistory.com

 

 

 

call()

함수는 선언 한 후 호출하면 실행이된다. 이 때에 함수를 대게 함수 뒤에 ()를 붙여서 호출하지만, 또 다른 방법으로는 함수 객체의 정적 메소드인 call을 사용하여 호출할 수 있다.

 

call을 사용하는 방법은 다음과 같다.

const callEx = function(a, b) {
    console.log(a + b);
}

// ()를 이용하여 호출
callEx(1, 2); //3


// call 메소드를 이용하여 호출
callEx.call(null, 1, 2); //3

 

두 번째 인자부터 해당 함수의 인자로 넘겨줄 변수를 지정하면 똑같은 결과를 출력하는 것을 볼 수 있다. 그렇다면 null로 지정해준 첫번째 인자는 무엇일까?

 

첫번째 인자가 바로 this를 지정해줄 수 있는 인자이다. call 메소드의 첫번째 인자를 통해 명시적으로 내가 원하는 특정 객체를 this에 바인딩하는것이 가능해지는 것이다.

const obj = {
    name : 'java',
    printName : function() {
        console.log(this.name);
    }
}

const obj2 = {
    name : 'javascript',
}

obj.printName(); // java
obj.printName.call(obj2) // javascript

 

 

기존의 obj 객체의 printName 메소드를 호출하면 호출한 obj의 객체가 바인딩 되지만, call을 통해 obj2를 this에 바인딩해주었더니 obj 내의 메소드이지만 obj2의 이름을 출력하는 것을 볼 수 있다.

 

이렇게 call 메소드를 사용하여 함수를 호출하게 되면, 다른 객체의 프로퍼티나 메소드를 자기 것마냥 사용할 수 있다. 위의 예시에서도 obj2는 printName 메소드를 가지고 있지 않지만 obj의 printName 메소드를 자기것 마냥 사용하여 호출한 것을 알 수 있다.

 

 

 

call() 활용하기

call을 활용한 다른 객체의 메소드를 사용하는 예제로는 무엇이 있을까? 흔히 많이 사용되는 상황으로는 함수의 arguments를 조작하는 경우이다. 함수의 arguments는 함수가 가지고 있는 숨겨진 속성인데, 바로 파라미터로 넘겨받은 값들을 가지고 있는 유사 배열 객체이다.

function callEx (a, b) {
    console.log(arguments)
}

callEx('javasciprt', 'call'); // [Arguments] { '0': 'javasciprt', '1': 'call' }

 

 

유사 배열 객체는 배열과 유사하지만 배열이 아니기 때문에 배열의 메소드를 사용할 수 없다. 그렇기 때문에, Array 객체의 정적 메소드인 from을 사용하여 배열을 반환받아 사용하기도 하지만 위에서 배운 call을 사용하면 배열로 변환하지 않아도 마치 배열처럼 다룰 수 있게 된다.

function callEx (a, b) {
    console.log(arguments.join()) // 배열의 메소드를 사용할 수 없음
}

callEx('javasciprt', 'call'); // TypeError: arguments.join is not a function

// call을 사용하여 Array 의 메소드 빌려쓰기
function callEx (a, b) {
    console.log(Array.prototype.join.call(arguments, ' ')) // 배열의 메소드를 사용할 수 없음
}

callEx('javasciprt', 'call'); // javasciprt call

 

 

join 이외에도 이러한 방식으로 유사 배열 객체를 배열로 바꾸지 않고 배열의 여러 메소드인 slice, concat 등을 사용할 수 있게 된다. 하지만, Array의 메소드를 사용하기 위해서는 바인딩 되는 객체가 유사 배열이어야 한다.

 

 

 

apply()

apply 또한 call과 사용방법이 똑같다. 한 가지 다른 점으로는 apply는 함수의 인자를 배열의 형태로 넘겨주는 것이다.

const applyEx = function(a, b) {
    console.log(a + b);
}

// ()를 이용하여 호출
applyEx(1, 2); //3


// apply 메소드를 이용하여 호출
applyEx.apply(null, [1, 2]); //3

 

 

apply 메소드 또한 call 메소드와 마찬가지로 첫번째 인자로 this에 바인딩하고 싶은 객체를 넘겨줄 수 있다.

const obj = {
    name : 'java',
    printName : function() {
        console.log(this.name);
    }
}

const obj2 = {
    name : 'javascript',
    printName : function() {
        console.log(this.name);
    }
}

obj.printName(); // java
obj.printName.apply(obj2) // javascript

 

 

 

apply() 활용하기

apply 메소드의 대표적인 용도는 위의 call 활용 예제에서의 arguments 객체와 같은, 유사 배열 객체에 배열 메소드를 사용하는 경우이다. 즉, call이랑 활용도가 똑같은 것이다. (사실상, 함수의 매개변수를 배열의 형태로 받냐 안 받냐의 차이일뿐 둘의 동작 방식은 똑같다.)

 

이번 예제에서는 apply를 이용하여 유사 배열 객체의 slice메소드를 사용해보겠다. 

const obj = {
    0 : 'kim',
    1 : 26,
    2 : 'javascript',
    length : 3
}

function applyEx() {
    // slice는 특정 배열의 부분을 추출하여 새로운 배열로 반환하는 메소드
    // 처음 두개만 추출
    return Array.prototype.slice.apply(obj, [0, 2])
}

console.log(applyEx()) //[ 'kim', 26 ]

 

 

 

bind()

bind 메소드는 이 전의 두 메소드와는 달리 this의 바인딩 객체만 바꾸고 함수는 호출하지 않는다. 더 정확하게는 bind 함수는 해당 함수의 this를 정의한 새로운 함수를 리턴한다. 

const obj = {
    name : 'java',
    printName : function() {
        console.log(this.name);
    }
}

const obj2 = {
    name : 'javascript',
}


obj.printName.bind(obj2)(); //javascript
obj.printName(); //java

 

bind를 통해 obj2를 this로 가지는 새로운 함수를 바로 호출하여 주면(bind는 함수의 호출X => ()를 통해 호출) javascript가 출력이 되다. 이후의 obj의 printName 메소드는 기존의 출력 결과인 java를 출력한다.

 

 

 

결론

call, apply, bind는 모두 함수의 this를 조작하는 메소드이고 이를 통해 다른 객체의 메소드도 사용할 수 있다는 장점을 가지고 있다. 하지만 bind는 함수의 호출 없이 this를 조작한 새로운 함수를 반환만 한다는 점이 this를 조작한 새로운 함수를 바로 호출하는 call과 apply 함수와의 차이점이다.

 

call과 apply의 차이점은, 단순히 함수의 인자를 배열로 받냐 아니냐의 차이이다.

728x90