call 메소드는 메소드의 호출 주체인 함수를 즉시 실행하도록 하는 명령이다. 첫 번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 함수의 매개변수로 합니다. 함수를 그냥 실행하면 this가 전역객체를 참조하겠지만, call메소드를 사용하면 임의의 객체를 this로 지정할 수 있다.
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
메소드일때도 객체의 매소드를 그냥 호출하면 this는 객체를 참조하지만, call을 사용하면 임의의 객체를 this로 지정할 수 있다.
var obj = {
a: 1,
method: function(x,y){
console.log(this.a, x, y)
}
};
obj.method(2,3) // 1,2,3
obj.method.call({a:4},5,6) // 4,5,6
위에서 봤던 call과 기능적으로 완전 동일하지만, apply메소드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다는 차이만 있다.
var func = function(a,b,c){
console.log(this, a, b, c)}
func.apply({x:1},[4,5,6]) // x:1 4 5 6
var obj = {
a: 1,
method: function(x,y){
console.log(this.a, x, y)
}
};
obj.method.apply({a:4},[5,6]) //4,5,6
var obj = {
0:'a', 1:'b', 2:'c', length:3
}
Array.prototype.push.call(obj,'d')
console.log(obj)
//{0: "a", 1: "b", 2: "c", 3: "d", length: 4}
var arr = Array.prototype.slice.call(obj)
//slice에게 매개변수를 아무것도 넘기지 않을 경우 원본 배열의 얕은 복사본을 반환한다.
console.log(arr)
//["a", "b", "c", "d"]
console.dir(arr)
/*
Array(4)
0: "a"
1: "b"
2: "c"
3: "d"
length: 4
*/
객체에는 배열 메소드를 사용할 수 없지만, 키가 0 혹은 양의 정수인 프로퍼티가 존재하고, length 프로퍼티의 값이 0 또는 양의 정수인 객체(유사배열객체)의 경우에는 call / apply 메소드를 사용할 수 있다.
ES6에서는 유사배열객체 또는 순회가능한 모든 종류의 데이터 타입을 배열로 전환하나는 Array.from 메소드를 도입했다.
var obj={0:'a', 1:'b', 2:'c', length:3}
var arr = Array.from(obj)
console.log(arr) //['a','b','c']
생성자 내부에서 다른 생성자와 공통된 내용이 있을 경우 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
}
function Employee(name, gender, company){
Person.apply(this, [name, gender])
this.company = company
}
var jm = new Student('절미', 'female', '사과대')
var cg = new Employee('홍창기','male','LG Twins')
은 ES6에서 나온 spread 연산자를 사용하자구!
const numbers = [9, 6, 33, 41, 17]
const max = Math.max(...numbers)
const min = Math.min(...numbers)
console.log(min, max) //6 41
call/apply는 명시적으로 별도의 this를 바인딩 하면서 함수 혹은 메소드를 실행하는 훌륭한 방법이지만, 이로인해 this를 예측하기 어렵게 만들어서 코드해석을 방해 할 수도 있다. 레거시한 환경에서는 어쩔 수 없는 선택으로 활용되고 있다..
bind메소드는 ES5에서 추가된 기능이다. call과 비슷하지만 바로 호출하는 것이 아니라 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 한다. 다시 새로운 함수를 호출할 때 인수를 넘기면 그 인수들은 기존 bind메소드를 호출할 때 전달했던 인수들의 뒤에 이어서 등록이 된다. 즉 함수에 this를 미리 적용하는 것과 부분 적용 함수를 구현하는 두 가지 목적이 있는 것이다.
var func = function(a,b,c,d){
console.log(this, a,b,c,d)}
func(1,2,3,4) // Window{...}, 1, 2, 3, 4
var bindFunc1 = func.bind({x:1})
bindFunc1(5,6,7,8) //{x:1},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프로퍼티에 동사 bind의 수동태인 bound라는 접두어가 붙는다는 점이다. bound + 원본
var func = function(a,b,c,d){
console.log(this, a,b,c,d)}
var bindFunc1 = func.bind({x:1},4,5)
console.log(func.name) //func
undefined
console.log(bindFunc1.name) //bound func
메소드의 내부함수에서 메소드의 this를 그대로 바라보기 위해선 self등의 변수를 활용해 우회하였지만, call, apply, bind를 사용해 좀 더 깔끔하게 처리할 수 있다.
var obj = {
outer: function () {
console.log(this);
var innerFunc = function () {
console.log(this);
};
innerFunc.call(this);
},
};
obj.outer();
var obj = {
outer: function () {
console.log(this);
var innerFunc = function () {
console.log(this);
}.bind(this);
innerFunc();
},
};
obj.outer();
또한 콜백함수를 인자로 받는 함수나 메소드 중에서 기본적으로 콜백 함수 내에서의 this에 관여하는 함수 또슷 메소드에 대해서도 bind를 활용해서 유저의 입맛에 맞게 this를 바꿀 수 있다.
var obj = {
logThis: function () {
console.log(this);
},
logThisLater1: function () {
setTimeout(this.logThis, 500);
},
logThisLater2: function () {
setTimeout(this.logThis.bind(this, 1000));
},
};
obj.logThisLater1(); //Window{...}
obj.logThisLater2(); // {logThis: ƒ, logThisLater1: ƒ, logThisLater2: ƒ}
화살표 함수는 this를 바인딩하는 과정이 제외되어서 해당 함수 내부에는 this가 아예 없으며, 접근하고자하면 스코프체인상 가장 가까운 this에 접근하게 된다
var obj = {
outer: function () {
console.log(this); // {outer: ƒ}
var innerFunc = () => {
console.log(this); // {outer: ƒ}
};
innerFunc();
},
};
obj.outer();
콜백함수를 인자로 받는 메소드 중 일부는 추가로 this로 지정할 객체를 인자(thisArg)로 지정할 수가 있다.이러한 메소드의 thisArg 값을 지정하면 콜백 함수 내부에서 this값을 원하는 대로 변경 할 수 있다. 이런 형태는 여러 내부 요소에 대해 같은 동작을 반복 수행하는 배열 메소드에 많이 포장 되어있고, Set, Map등의 메소드에서도 일부 존재한다.
var report = {
sum: 0,
count: 0,
add: function () {
var args = Array.prototype.slice.call(arguments);
args.forEach(function (entry) {
this.sum += entry;
++this.count;
}, this);
},
average: function () {
return this.sum / this.count;
},
};
report.add(60, 85, 95);
console.log(report.sum, report.count, report.average());
//240 3 80