[ 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}`);
}