JavaScript_ this 바인딩과 화살표 함수

Adela·2020년 4월 29일
0

JavaScript

목록 보기
6/17
post-thumbnail

this

5 PATTERNS OF BINDING

  1. Global : window
this;   //window
  1. 함수 호출 : window
function foo(){
  console.log(this);
}

foo();
// this가 출력되는데
// this는 window 객체이므로 window가 출력된다.
  1. method 호출 : object
    객체의 property로
var obj={
  fn: function(){ console.log(this); }
}

obj.fn();
// 객체의 키값을 실행하면 this가 출력되는데
// this는 obj를 가리키므로 obj인 {fn: ƒ}가 출력된다.
  1. construction mode : 새로생성된 객체
    new 연산자로 생성된 function영역의 this
    OOP - Class 키워드
function Car() {
  // this는 클래스Car의 instance
  this.name = name;
  this.color = color;
}

//construction
var benz = new Car('benz');
var avante = new Car('avante');
  1. .call .apply 호출 : call, apply의 첫번째 인자로 명시된 객체
    call과 bind 둘다 첫번째로 this를 넘기는데, 단 한가지 차이점이 있다.
    .call은 바로 실행
    .bind this를 가지고 있는 채로 함수를 리턴한다.
function foo(){
  console.log(this);
}

foo.call({a:1});  // this는 첫번째 argument


var obj = {
  foo: function(){
    console.log(this);
  }
}

var obj2 = {
  foo: obj.foo
}

obj.foo.call(obj2);   // obj2

Execution Context Call Stack

(실행 컨텍스트 콜스택)
this 키워드는 execution context의 구성요소 중 하나이다.

함수가 실행될 때 this가 결정된다.(Call Time에 결정된다.)
= 코드만 봐서는 this가 무엇을 가리키고 있는지 알기 어렵다.

Binding problem

function getSalaryFromServer (callback){
  setTimeout(function (){
    callback(100000);
  }, 1000);
}  // 1초 후에 callback을 실행, 100000을 넘긴다.

function member (){
  return {
    first: 'Adela',
    last: 'K',
    age: 40,
    printDetail: function (){
      getSalaryFromServer(function (salary){
        console.log(`Name: ${this.first} ${this.last}`);
        // 여기서 this는?
        console.log(`Salary: ${salary}`);
      });
    }
  }
}

var adela = member();
adela;    // {first: "Adela", last: "K", age: 40, printDetail: ƒ}

adela.printDetail();
/* 1초 후에 console에 아래처럼 찍힌다.
Name: undefined undefined
Salary: 100000
*/

console.log(`Name: ${this.first} ${this.last}`);
// 여기서 가리키는 this는????????
  • member함수 안에서 this는 5가지 패턴 중 2번째
    free function invocation
    callback(10000)로 함수를 실행시켰기 때문에 this가 상위 객체를 따라가지않고 window가 된다.

아래처럼 getSalaryFromServer로 부터 받아온게 아니라면 this는 상위 객체가 될 것이다.

function memberSync (){
  return {
    first: 'Adela',
    last: 'K',
    age: 40,
    printDetail: function (){
      // getSalaryFromServer(function (salary){
      console.log(`Name: ${this.first} ${this.last}`);
      // 여기서 this는???????
        // console.log(`Salary: ${salary}`);
   // });
    }
  }
}

let me = memberSync();

me.printDetail();  // Name: Adela K

콜백 안의 this를 상위객체로 해서 콘솔에 Adela K를 찍게하고 싶다면?
(= 맨 위의 member 함수처럼 비동기적으로 받아오면서 this가 window가 아닌 상위객체를 가리키게 하고싶다면?)

SOLUTION1 USE .bind

bind를 써볼 수 있다.
bind는 this를 갖고있는 채로 함수를 리턴한다.

function getSalaryFromServer (callback){
  setTimeout(function (){
    callback(100000);
  }, 1000);
}  // 1초 후에 callback을 실행, 100000을 넘긴다.

function member (){
  return {
    first: 'Adela',
    last: 'K',
    age: 40,
    printDetail: function (){
      getSalaryFromServer(function (salary){
        console.log(`Name: ${this.first} ${this.last}`);
   // 콜백 안의 this를 상위객체로 해서 콘솔에 Adela K를 찍게하고 싶다면 ?
        console.log(`Salary: ${salary}`);
      }.bind(this));
      // 1. printDetail에 있는 this라고 명시해주면 this는 객체가 된다.
    }
  }
}

var adela = member();
adela;
// {first: "Adela", last: "K", age: 40, printDetail: ƒ}

adela.printDetail();
/* 1초 후에 console에 아래처럼 찍힌다.
Name: Adela K
VM500:11 Salary: 100000
*/

SOLUTION2 USE ARROW FUNCTION

화살표 함수는 문제를 좀 더 간단하게 만들어 준다.

function member (){
  return {
    first: 'Adela',
    last: 'K',
    age: 40,
    printDetail: function (){
      getSalaryFromServer((salary) => {
        console.log(`Name: ${this.first} ${this.last}`);
        // 여기서 this는??????
        console.log(`Salary: ${salary}`);
      });
    }
  }
}

let adela = member();

adela.printDetail();
/* 1초 후에 console에 아래처럼 찍힌다.
Name: Adela K
VM500:11 Salary: 100000
*/

화살표 함수의 특징은 this가 화살표 함수 안의 새로운 실행컨텍스트를 만들지 않고, this는 그대로 lexical한 구조(member함수의 리턴된 객체)를 따라가게 된다.
???어렵다ㅠ

binding이 꼬일 것 같다면 화살표 함수를 쓰면 더 간단해진다.
실제로 화살표 함수를 ES5로 변환해주는 툴을 쓰면 solution1처럼 출력된다.

ES6의 Arrow function

ES5의 this는 어디에서보다 어떻게호출되는 지가 더 중요했다.
ES6의 arrow function을 사용한 함수는 어디에서 호출되는지만 고려하면 된다.

arrow function만의 execution context를 만들지 않는다.

function foo(){
  let that = this;
  let f =() => {
    console.log(that == this)
  }
  
  f();
}

foo();  // true

따라서 arguments라는 키워드도 없고,
(대신 Rest Parameter를 쓰면 된다.)

function foo(){
  let that = this;
  let args = arguments;
  let f =() => {
    console.log(that === this)  // true
    console.log(args === arguments)  // true
  }
  
  f.call({a:1});
  // call은 무시된다. f()와 똑같이 취급
}

foo();

.call, .apply로 화살표함수를 실행하게되면 this바인딩은 무시된다. 의미가 없다.
function이라고 정의된 {}안의 범위까지만 execution context를 만들고
화살표함수는 실행컨텍스트를 만들지 않는다.

어떤 경우에 call, apply와 화살표 함수를 쓸까?

let obj= {
  i: 10,
  b: () => console.log(this.i, this),    // ? undef, window
  c: function() {
    console.log(this.i, this)    // ? 10, obj
  }
}

obj.b의 화살표 함수는 실행 컨텍스트를 만들지 않는다.
=> undefined와 window

obj.b의 lexical한 this는 window이다. global의 this는 window이기 때문.

obj.c 의 function은 실행될 때 실행 컨텍스트를 만든다.
따라서 method invocation형식에 따라 this는 상위객체 obj가 된다.

특징

let foo = () => {};
let foo = new Foo();    // Foo in not a constructor

화살표 함수 foo는 construction mode를 아예 할 수 없다.
따라서 new키워드가 필요할 때 화살표함수를 쓰면 안된다. function을 쓰거나 class 키워드를 써야한다.

lexical한 this를 가지고 싶을 때 = 독자적인 execution context를 만들고싶지 않을 때
=> 화살표 함수를 사용하는 것이 훨씬 더 직관적이다.

profile
👩🏼‍💻 SWE (FE) 🚀 Be GRIT!

0개의 댓글