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

Jay ·2022년 4월 1일
0

상황에 따라 this에 어떤 값이 바인딩되는지는 명확하다. 하지만 이러한 규칙을 깨고 this에 별도의 대상을 바인딩하는 방법도 있다.

1.call method

ex) call method(1)

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

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

ex)call method(2)

var obj = {
	a:1,
    method: function (x,y) {
    	console.log(this.a, x, y);
        }
};

obj.method(2,3);
obj.method.call({a:4}, 5, 6)

2.apply method

apply 메서드는 call 메서드와 기능적으로 완전히 동일하다.
단, call 메서드는 첫 번째 인자를 제외한 나머지 모든 인자들을 호출할 함수의 매개변수로 지정하는 반면, apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다는 점에서만 차이가 있다.

ex)apply method

var func = function (a,b,c) {
	console.log(this, a,b,c);
};
func.apply({x: 1}, [4,5,6]);

3. call / apply의 적용사례?

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

var obj = {
	0: 'a',
    1: 'b',
    2: 'c',
    length: 3
};
Array.prototype.push.call(obj, 'd')
// => { 0: 'a', ~~ , 3: 'd', length: 4}
var arr = Array.prototype.slice.call(obj);
// => [ 'a', 'b', 'c', 'd']

하지만, 이런 방식은 본래의 의도, 'this를 원하는 값으로 지정해서 호출한다'라는 본래의 메서드의 의도와는 동떨어진 활용법이라 할 수 있다.
이에 ES6에서는 유사배열객체 또는 순회 가능한 모든 종류의 데이터 타입을 배열로 전환하는 Array.from 메서드를 새로 도입함.

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

생성자 내부에 다른 생성자와 공통된 내용이 있을 경우 call 또는 apply를 이용해 다른 생성자를 호출하면 간단하게 반복을 줄일 수 있다.

function Person(name, gender) {
	this.name = name;
    this.gender = gender;
}
function Student(name, gender, school) {
	Person.call(this, name, gender);
    this.school = school;
}
let by = new Student('보영', 'female', '단국대');
//Student {name: '보영', gender: 'female', school: '단국대'}

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

예를 들어 배열에서 최대/최솟값을 구해야 할 경우

1) for문
2) call/apply(ES5)

let numbers = [10,20,3,16,45];
let max = Math.max.apply(null, numbers);

3) spread operater (ES6)

const numbers = [10, 20, 3, 16, 45];
const max = Math.max(...numbers);

=> call/apply 메서드는 명시적으로 별도의 this를 바인딩하면서 함수 또는 메서드를 실행하는 훌륭한 방법이지만 이로 인해 this를 에측하기 힘들게 만들어 해석이 어려워진다는 단점이 존재.
그럼에도 불구하고 ES5 이하의 환경에서는 최선의 대안으로 실무에서는 광범위하게 활용되고 있다.

4. bind 메서드 (ES5~)

call과 비슷하지만 즉시 호출하지 않고 넘겨받은 this 및 인수들을 바탕으로 새로훈 함수를 반환만 하는 메서드. 다시 새로운 함수를 호출할 때 인수를 넘기면 그 인수들을 기존 bind 메서드를 호출할 때 전달했던 인수들의 뒤에 이어서 등록된다.

즉 bind 메서드는 함수에 this를 미리 적용하는 것과 부분 적용 함수를 구현하는 두 가지 목적을 모두 지닌다.

Function.prototype.bind(thisArg[, arg1[, arg2[, ...]]])

var func = function (a,b,c,d) {
	console.log(this, a, b, c, d);
};
func(1, 2, 3, 4);

var bindFunc1 = func.bind({x: 1});
bindFunc1(5, 6, 7, 8);

var bindFunc2 = func.bind({x: 1}, 4, 5);
bindFunc2(6,7);  // {x:1} 4 5 6 7 
bindFunc2(8,9);  // {x:1} 4 5 8 9

1) name 프로퍼티

bind 메서드를 통해 생성된 함수는 name 프로퍼티가 bound 가 된다.

console.log(func.name) // func
console.log(bindFunc.name) // bound func

2) 상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달하기

var Obj = {
  Outer: function () {
  	console.log(this);
    var innerFunc = function () {
        console.log(this); 
    }.bind(this);
    innerFunc();
  }
};
Obj.Outer();

3) 화살표 함수에선?

별도의 call/apply/bind가 필요없다.

4) 별도의 인자를 this로 받는 경우(콜백 함수 내에서의 this)

콜백 함수를 인자로 받는 메서드 중 일부는 추가로 this로 지정할 객체(thisArg)를 인자로 지정할 수 있는 경우가 있다.

배열 메서드(foreach, map, filter, some, every, find)에 해당된다. 또한 ES6의 Set,

var report = {
  sum: 0,
  count: 0,
  add: function () {
		// arguments 를 배열로 변환해서 args 변수에 담는다
    var args = Array.prototype.slice.call(arguments); 
		// 해당 배열(args)를 순회하면서 콜백 함수 실행
    args.forEach(function (entry) {
      this.sum += entry;
      ++this.count;
    }, this); // 콜백 함수 내부에서의 this가 해당 this로 바인딩 됨! 
  },
  average: function () {
    return this.sum / this.count;
  },
};
report.add(60, 85, 95);
console.log(report.sum, report.count, report.average()); // 240 3 80

정리

  1. call, apply 메서드는 this를 명시적으로 지정하면서 함수 또는 메서드를 호출한다.

  2. bind 메서드는 this 및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만든다.

  3. 요소를 순회하면서 콜백 함수를 반복 호출하는 내용의 일부 메서드는 별도의 인자로 this를 받기도 한다.

profile
Jay입니다.

0개의 댓글