[JS] Dynamic Scope, this binding

colki·2021년 5월 7일
2

Udemy_JavaScript: The Advanced JavaScript Concepts (2021) 강의를 바탕으로 메모한 내용입니다.

this

This is the object that the function is a property of. 함수가 속성인 객체

function a() {
  console.log(this);
}

a(); // window

/* the function a is a property of the window object. */
a() === window.a();

'use strict';
function b() { 
  console.log(this);
}

b(); // undefined

this 장점

  1. Gives methods access to their object.

    특정 객체의 프로퍼티에 접근할 수 있게 된다.

  2. Execute same code for multiple object.

    반복되는 코드를 줄일 수 있다.

    ex. 같은 속성을 가지고 있는 객체a, b가 가 있다면 a.속성 or b.속성 이렇게 반복하는 것이 아니라 this.속성 으로 재사용할 수 있다.


left ot the dot

const obj = {
  name: 'Billy',
  sing() {
    return 'lalala' + this.name;
  },
  singAgain() {
    return this.sing() + '!'; 
    // = return 'lalala' + this.name + '!';
  }
}

obj.singAgain(); //lalalaBilly!

whatever to the left of the dot which is the object that the function is a property of.
프로퍼티(메서드) 앞에 this가 가리키길 원하는 객체 + . 을 붙여서 호출하면 된다.

const obj = {
  name: 'Billy',
  sing() {
    console.log('a', this); // a obj{name: "Billy", sing: ƒ}
    var anotherFunc = function () {
      console.log('b', this); // b window{...}
    }

    anotherFunc();
  }
}

obj.sing();

호출하고자하는 메서드가 포함된 객체뒤에 점 붙이면 되는구나.
obj밖에서는 obj.sing으로 호출해주고~
anotherFunc 실행문은 객체 내부에 있으니까 바로 호출하면 될 것 같은데..
this도 anotherFUnc안에 있고
잉 window가 출력되네. 아 쩜이 없나? 아..근데 정확히 왜 이렇게 나올까?


바로 this의 호출방식에 문제가 있다.
어떻게 무엇을 호출해야 잘했다고 칭찬받을까? 답은 다음에 나오는 Dynamic Scope에 있다.

Dynamic Scope vs Lexical Scope

this와 Dynamic Scope에 대해 얘기하기 전에 다이나믹 스코프가 무엇인지 감을 잡아보자.

아래 예제를 보면 콘솔에 x의 값을 출력하는 함수는 foo 뿐이다.
bar내부에서도 foo를 호출하고 있으니, foo나 bar나 결국엔 같은 console.log(x); 에서 값을 출력한다. foo나 bar나 호출한 위치는 같은데 값이 달라졌다.

foo는 전역변수 x= 10을 그대로 출력하지만, bar내부에서 x값을 10으로 재할당했기 때문에 bar가 불러낸 foo는 15를 출력하게 된다.

function foo() {
  console.log(x);
}

function bar() {
  x = 15;
  foo();
}

var x = 10;
foo(); // 10
bar(); // 15

console.log(x)가 동적으로 바뀌는 것 같은 느낌적인 늬낌.

함수를 호출하는 실행문의 위치가 아닌, 바로
어떻게! 어떤 걸! 호출하느냐 에 따라서 동적으로 값이 바뀌는데 이런 현상을 두고 Dynamic Scope에 따라 결정되었다고 한다.

앞서 Static scope로 정리했던 Lexical Environment에서의 Scope 즉, Lexical Scope와 대조되는 Scope의 특징 Dynamic Scope 되시겠다.


다시 this가 window를 가리키던 문제의 예제를 확인해보자.

const obj = {
  name: 'Billy',
  sing() {
    console.log('a', this); // a obj{name: "Billy", sing: ƒ}
    var anotherFunc = function () {
      console.log('b', this); // b window{...}
    }

    anotherFunc();
  }
}

obj.sing();

obj 객체 안에 anotherFunc가 들어가 있으니까, obj꺼 아닌가? 싶지만

사실 anotherFunc은 여기 들어있는게 아니라 메모리 어딘가에 자기 주소를 가지고 있는 애다.
그저 sing이라는 함수의 컨텍스트가 생성될때 우리에게 저 자리에 들어 있는 것처럼 보일 뿐이다.
그렇기 때문에 그 안에 있는 this또한 위치 기준인 렉시컬스코프가 아닌 동적인 스코프를 가지게 된다.

우리 눈에 코드 1-2줄 위에있으니까 그까이꺼 대충 여기에 쓰면 바로 윗놈 호출되겄찌? 하면 오산인것!

this를 사용할 때도 마찬가지이다.

정확히 콕 찝어서 거기 지금 프로퍼티 갖고 있는 것 같은 너말야, 그래 너 이동식!😳 하고 짚어줘야 한다.

this keyword is actually dynamically scope that is it doesn't matter
where it's run it matters how the function was called.

바로 위에있든 옆에 있든 그 프로퍼티를 품고 있는 객체의 신원을 정확히 밝혀줘야 한다.
= 누가 지금 프로퍼티를 호출하였어? 🧐
= 누가 가지고 있는 프로퍼티를 호출할 것인가.
= 누가 호출주체인가.


💡 this solution

그렇다면 객체의 메서드내에서 this가 전역객체(window)를 바인딩하지 않고
호출 당시 주변 환경 즉, 자신이 속한 객체를 가리키게 하려면 어떤 방법이 있을까.

=> this를 그대로 상속받아 사용할 수 있도록
=> this 역시 현재 컨텍스트에 바인딩된 대상이 없으면 상위(직전) 컨텍스트의 this를 바라볼 수 있도록

1. Arrow Function () => {}


일반 함수와 달리 화살표 함수에는 고유한 this가 없으며 인자를 바인딩할 수 없다.

화살표함수를 둘러싼 Lexical Scope를 따르기 때문에 자신이 선언된 프로퍼티의 위치를 기준으로 this가 수동적으로 정해진다.

그러니까 화살표함수의 this는 자신이 들어있는 프로퍼티를 가지고 있는 객체 {}가 된다.

가끔 문제를 풀면서 this와 가까이에 있는 변수랑 헷갈렸었는데,

그건 변수나 함수이름일 뿐이다. 잊지말자! this는 프로퍼티의 주체가 되는, this가 포함된 변수나 함수등을 감싸고 있는 대빵 객체! 를 가리킨다

const obj = {
  name: 'Billy',
  sing() {
    console.log('a', this); //a obj{name: "Billy", sing: ƒ}

    var anotherFunc = () => {
      console.log('b', this); 
      // b obj{name: "Billy", sing: ƒ}
      // this를 가진 프로퍼티가 속한 obj객체를 가리킨다.
    }

    anotherFunc();
  }
}

obj.sing();

2. bind(this)

Bind makes sense now we were able to store the value of this.

Dynamically scoped this keyword we're able to bind another function to use the this keyword

Bind is useful for us to call functions later
on with a certain context or certain this keyword.

Colki velog_ this binding


ex1

const obj = {
  name: 'Billy',
  sing() {
    console.log('a', this); 
    //a obj{name: "Billy", sing: ƒ}
  
    var anotherFunc = function () {
      console.log('b', this); 
    //b obj{name: "Billy", sing: ƒ}
    }

    return anotherFunc.bind(this);
  }
}

obj.sing()();

ex2

var obj = {
  outer: function () {
    console.log(this);
    var innerFunc = function () {
      console.log(this);
    }.bind(this);

    innerFunc();
  } 
};

obj.outer();

3. const self = this


내부함수에서의 this 우회: 상위스코프의 this를 변수에 저장해서 내부 함수에서 활용할 수 있다.

다른 변수명을 써도 상관없지만 보통 self 를 많이 쓴다.

We're going to maintain that reference to the object so that here we can use self inside of the another function.

const obj = {
  name: 'Billy',
  sing() {
    console.log(this); //a obj{name: "Billy", sing: ƒ}
    const self = this;
    var anotherFunc = function () {
      console.log(self); //b obj{name: "Billy", sing: ƒ}
    }

    return anotherFunc;
  }
}

obj.sing()();
profile
매일 성장하는 프론트엔드 개발자

0개의 댓글