상황에 따라 this에 어떤 값이 바인딩되는지는 명확하다. 하지만 이러한 규칙을 깨고 this에 별도의 대상을 바인딩하는 방법도 있다.
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)
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]);
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 메서드를 새로 도입함.
생성자 내부에 다른 생성자와 공통된 내용이 있을 경우 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: '단국대'}
예를 들어 배열에서 최대/최솟값을 구해야 할 경우
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 이하의 환경에서는 최선의 대안으로 실무에서는 광범위하게 활용되고 있다.
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
bind 메서드를 통해 생성된 함수는 name 프로퍼티가 bound 가 된다.
console.log(func.name) // func
console.log(bindFunc.name) // bound func
var Obj = {
Outer: function () {
console.log(this);
var innerFunc = function () {
console.log(this);
}.bind(this);
innerFunc();
}
};
Obj.Outer();
별도의 call/apply/bind가 필요없다.
콜백 함수를 인자로 받는 메서드 중 일부는 추가로 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
call, apply 메서드는 this를 명시적으로 지정하면서 함수 또는 메서드를 호출한다.
bind 메서드는 this 및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만든다.
요소를 순회하면서 콜백 함수를 반복 호출하는 내용의 일부 메서드는 별도의 인자로 this를 받기도 한다.