목표

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]
*/ 
  1. 먼저 전역 컨텍스트가 생성이되고 object 객체를 생성한다.
    → 12 번째줄object객체의 메서드 outer() 실행
  2. outer() 실행 컨텍스트가 생성되면서 this는 호출한 주체인 object 를 바인딩 한다.
  3. inner() 실행 컨텍스트가 생성되고 this는 window 를 바인딩 한다.
    → 4 번째줄 console 실행 : inner object object
    → 6 번째줄 console 실행 : outer object object

self라는 변수에 outer의 호출 객체를 바인딩한 this(object)를 할당함으로 inner함수에도 스코프 체인에 의해 this(object)에 접근할 수 있게 된다.

화살표 함수를 이용한 this 우회

var object = {
    outer : function() {
        var inner = () => {
            console.log('inner' + self);
        }
        console.log('outer' + this);
        inner();
    }
}

object.outer();
  1. 먼저 전역 컨텍스트가 생성이되고 object 객체를 생성한다.
  2. 12 번째줄object객체의 메서드 outer() 실행
  3. outer() 실행 컨텍스트가 생성되면서 this는 호출한 주체인 object 를 바인딩 한다.
  4. inner() 실행 컨텍스트가 생성되면서 this는 바인딩 되지 않는다.
  5. this가 바인딩 되어 있지않으므로 상위 스코프의 this에 접근한다. → 4 번째줄 console 실행 : inner object object
  6. 6 번째줄 console 실행 : outer object object

call, apply, bind 메서드를 활용한 this 우회

1. call

call 메서드는 주어진 this값 및 각각 전달된 인수와 함께 호출합니다. 출처 MDN

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
*/
<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
*/
  1. document.querySelectorAll(“div”)는 유사배열 객체이다.
  2. 따라서 배열메서드인 map이 동작하지 않는다.
  3. 이에따라 document.querySelectorAll(“div”)를 배열로 변환.
  4. Array.prototype.slice를 이용해 배열을 반환받는다.
  5. 인풋값이 유사배열 객체 이므로 call() 함수를 사용하여 배열로 변환.
  6. 배열함수인 map을 이용하여 data-number의 값을 가져올수 있게 된다.
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
*/
  1. arguments는 배열객체가 아니므로 forEach메서드가 없다.
  2. slice.call(arguments)을 사용해여 배열로 변환한다.
  3. forEach를 사용해 순회한다.

2. apply

apply 메서드는 주어진 this값과 배열(또는 유사배열 객체)로 제공되는 arguments로 함수를 호출합니다. 출처 MDN

let func = function ( a, b, c) {
  console.log(this, a,b,c);
}

func.apply({ a : 1}, [1,2,3]);

/* 실행결과 
 {a:1}, 1, 2, 3
*/
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

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);