[ Function.prototype.call() | MDN ]
call()
메서드는 주어진 this
값 및 각각 전달된 인수와 함께 함수를 호출한다. 이 함수 구문은 apply()
와 거의 동일하지만, call()
은 함수에 전달될 인수 리스트(argument list)를 받는데 비해, apply()
는 인수들의 단일 배열(single array of arguments)을 받는다는 점이 다르다.
객체 whiteCat
에는 하얀 고양이 만두를 소개하는 함수 introduce()
가 있다. 그 안에는 색상값을 가져오는 this.color
가 있지만, 현재 this.color
의 this
값인 객체 whiteCat
에 color
라는 key
가 존재하지 않으므로 함수를 실행하면 undefined
가 반환된다.
const snow = {
color : '하얀색'
}
const whiteCat = {
introduce: function() {
return `만두는 ${this.color} 고양이입니다.`;
}
}
whiteCat.introduce();
// "만두는 undefined 고양이입니다."
이 때 call()
을 이용하여 this
값으로 객체 snow
를 할당하면, 객체 snow
에 속한 key
인 color
값을 읽어올 수 있다.
const snow = {
color : '하얀색'
}
const whiteCat = {
introduce: function() {
return `만두는 ${this.color} 고양이입니다.`;
}
}
whiteCat.introduce.call(snow);
// "만두는 하얀색 고양이입니다."
[ Function.prototype.apply() | MDN ]
apply()
메서드는 주어진 this
값과 배열(또는 유사 배열 객체)로 전달된 arguments
와 함께 함수를 호출한다.
push()
메서드를 사용하여 배열 A에 배열 B를 넣으면 배열 B가 통째로 배열 A에 들어간다.
const array = ['a', 'b'];
const elements = [0, 1, 2];
array.push(elements);
console.log(array);
// ["a", "b", [0, 1, 2]]
하지만 apply()
메서드를 이용하면 배열 B의 값이 하나씩 차례로 들어간다.
const array = ['a', 'b'];
const elements = [0, 1, 2];
// array를 this값으로 지정
array.push.apply(array, elements);
console.log(array);
// ["a", "b", 0, 1, 2]
[ Function.prototype.bind() | MDN ]
bind()
메서드는 함수를 바로 호출하지 않고, 새로운 함수(바인딩 함수)를 생성한다. bind()
의 첫번째 매개변수로 대상 함수(target function)가 실행될 때 취할 this
값을 넣으면 호출 방법과 관계없이 특정 this
값으로 호출되는 함수를 만들 수 있다.
bind()
를 closure의 속성을 이용한 함수 호출법과 비슷하게 사용할 수 있다. 현재 연도에서 원하는 연도를 빼 나이를 구하는 함수를 clouser의 속성을 이용하여 작성한다면 아래와 같다.
function getAge(thisyear) {
return function(year) {
return thisyear - year;
}
}
const getYourAge = getAge(2019);
getYourAge(2000);
// 19
getYourAge(1980);
// 39
getYourAge(1965);
// 54
bind()
를 사용한다면?
function getAge(year) {
return this.year - year;
}
const thisYear = { year: 2019 };
const getYourAge = getAge.bind(thisYear);
getYourAge(2000);
// 19
getYourAge(1980);
// 39
getYourAge(1965);
// 54
'...call, apply, bind 메서드를 이용해 다른 객체의 prototype 메서드를 빌려서 사용할 수 있다. 예를 들어 Array 객체의 slice메서드를 String객체에 사용하거나, Math.max 메서드에 매개변수로 숫자로 이루어진 배열을 넣어 배열의 값 중에서 가장 큰 값을 찾을 수 있다.' - [ Call, Apply, Bind - The Basic Usages | Alexander antoniades ]
Math.max()
메서드에 매개변수로 배열을 전달하면 NaN
이 반환된다. 하지만 apply()
를 이용하면 배열을 매개변수로 전달해도 올바른 결과를 얻을 수 있다.
const numArray = [1,2,3,4,5];
Math.max(numArray);
// NaN
// 특정한 this가 필요치 않으므로 this값으로는 null을 전달한다.
const max = Math.max.apply(null, numArray);
// 원하는 결과를 얻을 수 있다.
console.log(max);
// 5
String
에는 Array.prototype.filter()
와 같은 동작을 하는 메서드가 없다. 하지만 call()
을 이용하여 Array.prototype.filter()
메서드를 빌려와 문자열에도 사용할 수 있다.
const letters = 'JzazvzazSzczrzizpztz';
// 'z'가 아닌 글자만 반환하기
const filteredLetters = Array.prototype.filter.call(letters, letter => letter !== 'z');
console.log(filteredLetters);
// ["J", "a", "v", "a", "S", "c", "r", "i", "p", "t"]
const answer = filteredLetters.join('');
console.log(answer);
// "JavaScript"
arguments
객체는 배열이 아닌 유사배열이기에 map()
, forEach()
같은 배열 내장 메서드를 사용하지 못하지만, call()
을 통해 그 메서드들을 빌려와 사용할 수 있다.
// arguments는 유사배열이기 때문에 map() 메서드를 사용할 수 없다.
function makeItOld() {
return arguments.map(val => val + ' ^_^)/~');
}
makeItOld('안녕하세요','반갑습니다');
// Uncaught TypeError: arguments.map is not a function
// 하지만 call() 메서드를 이용하면 가능하다.
function makeItOld() {
return Array.prototype.map.call(arguments, val => val + ' ^_^)/~');
}
makeItOld('안녕하세요','반갑습니다');
// ["안녕하세요 ^_^)/~", "반갑습니다 ^_^)/~"]
문자열형 숫자를 받아서 곱하는 함수와 JSON.stringfy()
메서드를 비슷하게 구현하는 함수를 짜면서 String
타입을 사칙연산 했을 때 일어나는 현상을 관찰했다.
문자열과 문자열이 아닌 객체끼리 더하기 연산자를 사용하여 연산하면, 문자열이 아닌 객체에 toString()
메서드가 자동으로 적용되어 결과값이 문자열로 반환된다.
const arr = [1,2,3];
console.log('{' + arr + '}');
// "{1,2,3}"
const obj = {'c' : 3 };
console.log('[' + obj + ']');
// "[[object Object]]"
문자열로 표현된 숫자끼리 더하면, 그대로 이어 붙어 문자열로 반환된다. 하지만 빼기, 곱하기, 나누기는 연산 가능하며 결과는 숫자형으로 반환된다. 숫자형와 문자형을 연산해도 마찬가지이다.
'12' + '2'
// "122"
'12' - '2'
// 10
'12' * '2'
// 24
'12' / '2'
// 6
'12' + 2
// "122"
'12' - 2
// 10
'12' * 2
// 24
'12' / 2
// 6
숫자가 아닌 문자열의 연산 : 더하기 이외에는 NaN
이 반환된다.
'Java' + 'Script'
// "JavaScript"
'Java' - 'Script'
NaN
'Java' * 'Script'
NaN
'Java' / 'Script'
NaN
사람들의 정보가 담긴 배열에서 10대만을 뽑아, 그 이름을 배열에 넣어 반환하는 함수를 여러 가지 방법으로 작성했다. 정보가 담긴 배열 littleWomen은 아래와 같다. 소설 '작은 아씨들'의 주인공 캐릭터들을 넣어보았다.
const littleWomen = [
{
"name": {
"first": "Josephine",
"last": "March"
},
"age": 15
},
{
"name": {
"first": "Meg",
"last": "March"
},
"age": 16
},
// .....
function getTeenager(characters) {
const result = [];
for (let i = 0; i < characters.length; i++) {
const person = characters[i];
if (10 <= person.age && person.age < 20) {
result.push(`${person.name.first} ${person.name.last}`);
}
}
return result;
}
function getTeenager(characters) {
const result = [];
characters.forEach(person => {
if (10 <= person.age && person.age < 20) {
result.push(`${person.name.first} ${person.name.last}`);
}
})
return result;
}
function getTeenager(characters) {
return characters.reduce((teenager, person) => {
if (10 <= person.age && person.age < 20) {
teenager.push(`${person.name.first} ${person.name.last}`);
}
return teenager;
}, []);
}
function getTeenager(characters) {
return characters
.filter(person => 10 <= person.age && person.age < 20)
.map(person => `${person.name.first} ${person.name.last}`);
}