TIL 82 | 코어자바스크립트(2) this 2
시대의 명작 코어 자바스크립트를 읽고 자바스크립트 마스터에 도전합니다...
명시적으로 this를 바인딩하기
상황별로 this에 값이 바인딩 되는 규칙을 깨고 별도의 대상을 바인딩 하는 방법도 있다.
call 메서드
Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])
call 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령어이다. 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
// 메서드로 호출할 때
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
apply 메서드
apply 메서드는 call 메서드와 거의 동일하다고 볼 수 있다. 다만 apply 메서드는 두번째 인자를 배열로 받아 호출한 함수의 매개변수로 지정한다.
Function.prototype.apply(thisArg, [, argsArray])
call / apply 메서드의 활용
유사배열객체에 배열 메서드를 적용
유사배열객체는 call, apply 메서드를 사용해서 배열 메서드를 차용할 수 있다.
유사배열객체 : 키가 0 또는 양의 정수인 프로퍼티가 존재하고, length 프로퍼티의 값이 0 또는 양의 정수인 객체
1) 유사배열객체
var obj = {
0: 'a',
1: 'b',
length: 2
};
Array.prototype.push.call(obj, 'c');
console.log(obj); // { 0: 'a', 1: 'b', 2: 'c', length: 3 }
var arr = Array.prototype.slice.call(obj);
console.log(arr); // [ 'a', 'b', 'c' ]
2) arguments
function aaa () {
var args = Array.prototype.slice.call(arguments);
args.forEach(function (arg) {
console.log(arg);
});
}
aaa(1, 2, 3); // 1 2 3
3) NodeList
: querySelectorAll, getElementsByClassName 등의 Node 선택자로 선택한 결과
document.body.innerHTML = '<div>a</div><div>b</div><div>c</div>';
var nodeList = document.querySelectorAll('div');
var nodeArr = Array.prototype.slice.call(nodeList);
nodeArr.forEach(function (node) {
console.log(node);
// <div>a</div>
// <div>b</div>
// <div>c</div>
});
4) 문자열
* 문자열은 length 프로퍼티가 읽기 전용**이기 때문에 원본에 변경을 가하는 메서드 (push, pop, shift, unshift, splice ...)를 사용하면 에러가 뜬다. concat처럼 대상이 반드시 배열이여야 하는 경우에도 제대로 된 결과를 얻을 수 없다.
var str = 'mia lee';
Array.prototype.push.call(str, ' pushed string');
// 에러 발생!
Array.prototype.concat.call(str, 'zzang');
// [String {'mia lee'}, 'zzang']
// 원본에 변경을 가하지 않는 경우
Array.prototype.every.call(str, function(char) { return char !== ' ' }); // false
Array.prototype.some.call(str, function(char) { return char === ' ' }); // true
var newArr = Array.prototype.map.call(str, function(char) { return char + '!'; });
console.log(newArr); // ['m!', 'i!', 'a!', 'l!', 'e!', 'e!']
var newStr = Array.prototype.reduce.apply(str, [
function(string, char, i) { return string + char + i; }, ''
]);
console.log(newStr); // 'm0i1a2 3l4e5e6'
생성자 내부에서 다른 생성자를 호출
생성자에 공통된 내용이 있을 경우에 call
이나 apply
를 사용해서 중복을 줄일 수 있다.
function Person(name, age) {
this.name = name;
this.age = age;
}
function Student(name, age, school) {
Person.call(this, name, age);
this.school = school;
}
function Employee(name, age, company) {
Person.call(this, name, age);
this.company = company;
}
var jh = new Student('지형', '26', '홍익대');
var mia = new Employee('미아', '36', '구글');
여러 인수를 묶어 하나의 배열로 전달하고 싶을때 - apply 활용
배열에서 최대/최소값을 구하는 경우를 비교해보자.
직접 구현하기
var numbers = [10, 20, 3, 18, 93];
var max = min = numbers[0];
numbers.forEach(function(number) {
if (number > max) {
max = number;
}
if (number < min) {
min = number;
}
});
console.log(max, min);
Math.max/Math.min 메서드에 apply 적용하기
var numbers = [10, 20, 3, 18, 93];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
console.log(max, min);
와우..! 쏘 이지!!!
스프레드 연산자 (ES6+)
var numbers = [10, 20, 3, 18, 93];
var max = Math.max(...numbers);
var min = Math.min(...numbers);
console.log(max, min);
bind 메서드
Function.prototype.bind(thisArg[, arg1[, arg2[, ...]]])
call
메서드와 거의 유사하지만, 바로 호출하지 않고 새로운 함수를 반환한다. 인자도 지정하여 부분으로 적용된 함수를 구현할 수 있다.
var func = function (name, surname) {
console.log(this, name, surname);
};
func('dongwon', 'kang'); // Window{ ... } dongwon kang
// this를 지정
var person1 = func.bind('great');
person1('mia', 'lee'); // great mia lee
// this를 지정하고 부분 적용 함수를 구현
var person2 = func.bind('great', 'jieun');
person1('lee'); // great jieun lee
name 프로퍼티
bind 메서드를 적용해서 만든 함수에는 name 프로퍼티에 bound라는 접두어가 붇는다. 코드를 추적할때 유용하게 사용할 수 있다.
console.log(func.name); // func
console.log(person1.name); // bound func
상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달
self 등의 우회법 없이, call, apply, bind 메서드로 상위 컨텍스트의 this를 그대로 바라보게 할 수 있다.
// call 메소드 사용
var obj = {
outer: function() {
console.log(this);
var innerFunc = function () {
console.log(this);
};
innerFunc.call(this);
}
}
// bind 메소드 사용
var obj = {
outer: function() {
console.log(this);
var innerFunc = function () {
console.log(this);
}.bind(this);
innerFunc();
}
}
화살표 함수의 예외사항
화살표 함수를 사용하면 스코프체인상 가장 가까운 this에 접근하게 된다. 화살표 함수를 사용한다면 굳이 this를 우회하거나 위의 메소드들을 사용하지 않아도 된다.
var obj = {
outer: function() {
console.log(this);
var innerFunc = () => {
console.log(this);
};
innerFunc();
}
}
별도의 인자로 this를 받는 경우(콜백 함수 내에서의 this)
콜백 함수를 인자로 받는 메서드 중 일부는 this로 지정할 객체를 직접 인자로 지정할 수 있는 경우도 있다.
콜백 함수와 함께 thisArg를 인자로 받는 메서드
Array.prototype.forEach(callback[, thisArg])
Array.prototype.map(callback[, thisArg])
Array.prototype.filter(callback[, thisArg])
Array.prototype.some(callback[, thisArg])
Array.prototype.evert(callback[, thisArg])
Array.prototype.find(callback[, thisArg])
Array.prototype.findIndex(callback[, thisArg])
Array.prototype.flatMap(callback[, thisArg])
Array.prototype.from(arrayLike[, callback[, thisArg]])
Set.prototype.forEach(callback[, thisArg])
Map.prototype.forEach(callback[, thisArg])
이렇게 쓴다!
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); // 여기 두번째 인자로 전해준 this!
},
average: function () {
return this.sum / this.count;
}
};
Author And Source
이 문제에 관하여(TIL 82 | 코어자바스크립트(2) this 2), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@hyounglee/TIL-82저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)