목표
- this를 우회하는 3가지 방법을 이해한다.
- 변수를 활용하여 this를 우회한다.
- 화살표 함수를 이용하셔 this를 우회한다.
- call, apply, bind 메서드를 이용하여 this를 우회한다.
this 우회 배경
- 상황에 따라 this가 결정된다.
- this는 기본적으로 실행 컨텍스트가 생성될때 결정된다.
- 함수 내부에서 this는 전역 객체로 결정된다.
- 메서드 내부에서 this는 호출한 주체의 정보로 결정된다.
- 콜백 함수의 경우 제어권을 가지는 함수가 결정한다.
- 생성자 함수에서 this는 class 객체 즉 자기자신이 된다.
- 특정 상황에 맞는 this를 할 수 있게 된다.
- 함수 내부에서는 this가 전역 객체로만 결정되는 오류를 해결 할 수 있다.
변수를 활용한 this 우회
var object = {
outer : function() {
var self = this;
var inner = function(){
console.log('inner' + self);
}
console.log('outer' + this);
inner();
}
}
object.outer();
/* 실행결과
outer[object Object]
inner[object Object]
*/
- 먼저 전역 컨텍스트가 생성이되고 object 객체를 생성한다.
→ 12 번째줄object객체의 메서드 outer() 실행 - outer() 실행 컨텍스트가 생성되면서 this는 호출한 주체인 object 를 바인딩 한다.
- inner() 실행 컨텍스트가 생성되고 this는 window 를 바인딩 한다.
→ 4 번째줄 console 실행 : inner object object
→ 6 번째줄 console 실행 : outer object object
self라는 변수에 outer의 호출 객체를 바인딩한 this(object)를 할당함으로 inner함수에도 스코프 체인에 의해 this(object)에 접근할 수 있게 된다.
화살표 함수를 이용한 this 우회
- ES6에서는 this가 전역객체를 바라보는 문제를 해결하고자, this를 바인딩하지 않는 화살표 함수가 생성
- ES6가 지원된다면 화살표 함수로 간단하게 구현하자
var object = {
outer : function() {
var inner = () => {
console.log('inner' + self);
}
console.log('outer' + this);
inner();
}
}
object.outer();
- 먼저 전역 컨텍스트가 생성이되고 object 객체를 생성한다.
- 12 번째줄object객체의 메서드 outer() 실행
- outer() 실행 컨텍스트가 생성되면서 this는 호출한 주체인 object 를 바인딩 한다.
- inner() 실행 컨텍스트가 생성되면서 this는 바인딩 되지 않는다.
- this가 바인딩 되어 있지않으므로 상위 스코프의 this에 접근한다. → 4 번째줄 console 실행 : inner object object
- 6 번째줄 console 실행 : outer object object
call, apply, bind 메서드를 활용한 this 우회
1. call
call 메서드는 주어진 this값 및 각각 전달된 인수와 함께 호출합니다. 출처 MDN
- call 메서드는 this도 함께 전달한다.
- call 메서드를 활용하여 this 명시적으로 변경한다.
- call 메서드는 형변화에 유용하다.
let func = function ( a, b, c) {
console.log(this, a,b,c);
}
func.call({ a:1 }, 1,2,3); // this를 {a:1}로 명시적으로 변경
/* 실행결과
{a:1}, 1, 2, 3
*/
- call 메서드는 유사배열객체 NodeList 배열변환시 많이 사용된다.
- ES6에서는 Array.from()를 사용해 변활 할 수 있다.
- ES6에서는 펼침연산자( … )를 사용해 변환 한다.
<div data-number="5"> 5 </div>
<div data-number="15"> 15 </div>
<div data-number="25"> 25 </div>
// call 메서드를 이용한 배열 변환
var aDiv = Array.prototype.slice.call(document.querySelectorAll("div")) //변환
.map( function(ele){ return Number(ele.dataset.number)});
// Array.from()을 사용한 배열 변환
var aDivFrom = Array.from( document.querySelectorAll("div") )
.map( function(ele){ return Number(ele.dataset.number)});
// 펼침연산자 ... 를 사용한 배열 변환
var aDivSpread = [ ...document.querySelectorAll("div") ]
.map( function(ele){ return Number(ele.dataset.number)});
console.log(aDiv, aDivFrom, aDivSpread);
/* 실행결과
5
15
25
*/
- document.querySelectorAll(“div”)는 유사배열 객체이다.
- 따라서 배열메서드인 map이 동작하지 않는다.
- 이에따라 document.querySelectorAll(“div”)를 배열로 변환.
- Array.prototype.slice를 이용해 배열을 반환받는다.
- 인풋값이 유사배열 객체 이므로 call() 함수를 사용하여 배열로 변환.
- 배열함수인 map을 이용하여 data-number의 값을 가져올수 있게 된다.
- call 메서드를 활용하여 전달인자(arguments)를 배열로 변환할수 있다.
- ES6에서는 Array.from()를 사용해 변활 할 수 있다.
- ES6에서는 펼침연산자( … )를 사용해 변환 한다.
function func(){
// call 메서드를 이용한 배열 변환
var argv = Array.prototype.slice.call(arguments);
argv.forEach( ele => {
console.log(ele);
});
// Array.from()을 사용한 배열 변환
var argvFrom = Array.from(arguments);
argvFrom.forEach( ele => {
console.log(ele);
});
// 펼침연산자 ... 를 사용한 배열 변환
var argvSpread = [ ...arguments];
argvSpread.forEach( ele => {
console.log(ele);
});
}
func(1,2,3,4,5);
/* 실행결과
1
2
3
4
5
*/
- arguments는 배열객체가 아니므로 forEach메서드가 없다.
- slice.call(arguments)을 사용해여 배열로 변환한다.
- forEach를 사용해 순회한다.
2. apply
apply 메서드는 주어진 this값과 배열(또는 유사배열 객체)로 제공되는 arguments로 함수를 호출합니다. 출처 MDN
- apply 메서드도 call과 동일하게 this도 함께 전달한다.
- apply 메서드는 call메서드와 다르게 인자로 배열을 전달한다.
- 인자로 배열을 넘길 때는 apply 함수가 유용하다.
- apply메서드는 형변화에 유용하다.
- ES6에서는 Array.from()를 사용해 변활 할 수 있다.
- ES6에서는 펼침연산자( … )를 사용해 변환 한다.
let func = function ( a, b, c) {
console.log(this, a,b,c);
}
func.apply({ a : 1}, [1,2,3]);
/* 실행결과
{a:1}, 1, 2, 3
*/
- apply 함수를 이용한 최댓값, 최솟값 찾기
let values = [1, 5, 2, 3, 9];
// 굳이 this를 바인딩 할 필요가 없으므로 null값을 입력
let max = Math.max.apply(null, values);
let min = Math.min.apply(null, values);
// 펼침연산자 ... 를 사용한 배열 변환
let maxSpread = Math.max(...values);
let minSpread = Math.min(...values);
console.log(max,min);
console.log(maxSpread,minSpread);
/* 실행결과
9, 1
*/
3. bind
메서드가 호출되면 새로운 함수를 생성, 첫 인자는 this 키워드를 설정 출처 MDN
- bind 메서드는 this인자로 넘겨 새로운 함수를 반환한다.
let func = function(a,b,c){
console.log(this, a,b,c);
}
func(1,2,3,4);
let bindFunc = func.bind({ x : 1});
bindFunc(1,2,3,4);