TIL37, JS: call, apply, bind 가 mojo?

tonyk0901·2021년 1월 23일
0

call / apply

Function.prototype.call

MDN에서는 다음과 같이 설명한다.

The call() method calls a function with a given this value and arguments provided individually.

즉, 함수의 내장 메소드인데, 함수가 실행될 때의 this 를 인자로 주어진 값으로 바꾸어서 실행한다는 말이다.

Syntax는 아래와 같다.

func.call([thisArg[, arg1, arg2, ...argN]])

예로,

const obj = {
  testString: 'test',
  sayTestString: function() {
    console.log(this.testString);
  }
};

const obj2 = {
  testString: 'test2'
};

obj.sayTestString(); // 'test';
obj.sayTestString.call(obj2); // 'test2'

처음에 obj.sayTestString 이라는 메소드를 실행했을 때, thisobj 이기 때문에 test 가 출력된다.

두번째의 경우 call 메소드에 obj2 를 인자로 넘겨주는 것으로 sayTestString 메소드가 실행될 때의 thisobj2 로 바꾸어 주었다. 그렇기 때문에, obj2testStringtest2 가 로그에 출력된다.


만약, call 을 통하여 실행하는 메소드가 인자를 받는다면, call 메소드에 인자로 넘겨주면 된다. 예로,

const obj = {
  testString: 'test',
  sayTestString: function(str1, str2) {
    console.log(`${this.testString} ${str1} and ${str2}`);
  }
};

const obj2 = {
  testString: 'I received:',
};

obj.sayTestString.call(obj2, 'abc','def'); // 'I received: abc and def'

Function.prototype.apply

apply 메소드는 call 과 동일하다. 다만, 넘겨지는 인자를 하나의 배열에 담아서 넘겨준다는 것에서 차이가 있다. Syntax 는 아래와 같다.

func.apply(thisArg, [ argsArray])

const obj = {
  testString: 'test',
  sayTestString: function(str1, str2) {
    console.log(`${this.testString} ${str1} and ${str2}`);
  }
};

const obj2 = {
  testString: 'I received:',
};

obj.sayTestString.apply(obj2, ['abc','def']); // 'I received: abc and def'

언제 call, apply 를 사용하나?

그렇다면 call, apply 는 언제 사용하는 걸까?

주로 배열 메소드를 유사 배열 객체(array like object)에 사용할 때를 들 수 있다.
유사배열 객체란, 배열의 형태를 가진 객체를 말한다. 객체가 유사배열로 취급되기 위해서는 2가지 조건이 충족되어야 한다.

  1. length 라는 key 값이 반드시 있어야 한다. length 의 value 값으로는 객체 내용물의 갯수가 담긴다.
  2. 객체안 나머지 내용물의 key 값이 0,1,2,3 과 같은 인덱스의 형태이다. 이 조건은 필수는 아니다. 하지만, 인덱스의 형태가 아닐경우 원치않는 결과가 나올 수 있다.

예시를 보자.

위의 조건에 따르면, 유사 배열 객체는 아래와 같이 구성된다.

const obj = {
	0: ‘a’,
	1: ‘b’,
	2: ‘c’,
	length: ‘3’
}

각각의 내용물에 접근하기 위해서는, obj[0] 와 같이 접근하면 된다. 그 길이를 알기 위해선 obj.length 를 하면 된다. 분명히 객체이지만, 마치 배열 처럼 접근하는 것을 볼 수 있다.

하지만, 유사배열 객체에 배열 메소드를 사용하면 실행이 되지 않는다. 당연하게도 그 이유는, 이 객체가 유사배열이지 진짜 배열은 아니기 때문이다.

const obj = {
	0: 'a',
	1: 'b',
	2: 'c',
	length: '3'
}

obj.push('d'); // TypeError: obj.push is not a function

이러한 상황에서, call 혹은 apply 를 사용하면, 배열 메소드를 유사배열에 사용할 수 있다.

const obj = {
	0: 'a',
	1: 'b',
	2: 'c',
	length: '3'
}

Array.prototype.push.call(obj, ‘d’); 
// { '0': 'a', '1': 'b', '2': 'c', '3': 'd', length: 4 }

여기서 주목해야할 점이 하나 있다. 새로 넣어준 d3 이라는 key값으로 들어갔다는 것.

위와 같이 유사 배열 객체에 배열 메소드를 사용하여 객체를 정보를 추가할 때,length 에 따라서 다음 index 값이 새로운 key 값이 된다.

그렇기 때문에, 만약 유사 배열에 들어있는 내용물의 key들이 인덱스의 형태가 아닐 경우, 문제의 가능성이 생긴다.

const obj = {
	a: ‘a’,
	b ‘b’,
	c: ‘c’,
	length:3}

Array.prototype.push.call(obj, ‘d’);
// { '3': 'd', a: 'a', b: 'b', c: 'c', length: 4 }

위와 같이 여전히 3 이라는 키값으로 들어가기 때문에, 키값의 통일성이 사라지고, 배열의 순서 특성을 잃게 된다.

유사배열 객체 예시

유사배열 객체는 에서 종종 나타나는데, 예로 html collection 이 있다.

const arrLike = document.querySelectorAll(‘div’);

bind

bind 메소드에 대해서 MDN 에서는 다음과 같이 설명한다.

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

즉, 특정 함수의 this 를 인자로 넘겨진 값으로 지정해준다.

예로,

const obj = {
  testString: 'test',  
  testFunction: function() {
    console.log(this.testString);
  }
}

const getFunction = obj.testFunction;
getFunction(); // undefined

const getFunctionBinded = getFunction.bind(obj);
getFunctionBinded(); // test

getFunction 이 처음 호출 되었을 때, thisWindow 이다. 그렇기 때문에, testString 을 찾지 못해 undefined 를 반환한다.

obj 안에 있는 testString 에 접근하기 위해서는, bind 메소드를 통해 thisobj 로 지정해 주어야 한다. bind 로 지정해 준 이후, 함수를 호출하면 test 라고 로그에 찍히는 것을 확인할 수 있다.

1개의 댓글

comment-user-thumbnail
2021년 1월 25일

정독했습니다. 코딩 잘하시네요

답글 달기