Skip to main content

Command Palette

Search for a command to run...

[3장] this - 북 스터디

코딩 마을 방범대 북 스터디 DAY 3

Updated
8 min readView as Markdown
[3장] this - 북 스터디
W

안녕하세요! 프론트엔드 개발자 woodstock입니다.

저는 매일 조금씩 발전하고자 하는 마음으로 개발공부를 시작했고, 이 블로그는 그 과정에서 배우고 성장하는 이야기를 담고 있습니다.

여러분의 피드백과 조언은 언제나 환영합니다! 함께 배우고 성장하는 과정을 즐길 수 있기를 기대합니다.

🤓 스터디원 🤓
강지윤 @eeeyooon
이예솔 @lulla-by
이성령 @sryung1225
이에스더 @Stilllee
채하은 @chaehaeun



코딩 마을 방범대 : 3장. this

질문과 답변

1. 함수 내부의 this 바인딩을 변경하는 방법

함수 내부의 this 바인딩을 변경하는 방법

var a = "global!";
var obj = {
  a: "object!",
  b: function() {
    console.log(this.a); // object!

    function c() {
      console.log(this.a); // global!
    }
    c();
  }
}
obj.b();

위 코드에서 메서드 bthisobj객체를 가리킵니다. 그러나 b메서드 내부에 정의된 c함수는 일반 함수로 호출되므로, this가 전역 객체를 가리키게 됩니다.

만약, cthis가 전역객체가 아닌 obj를 바라보게 하려면 어떻게 해야할까요?

여러 방법 중 한 가지를 선택하고 예시코드와 함께 설명해주세요.

chaehaeun
c를 화살표 함수로 정의하게 되면 c 함수 내부의 thisobj 객체를 가리키게 됩니다.

var a = "global!";
var obj = {
  a: "object!",
  b: function() {
    console.log(this.a);

    const c = () => {
      console.log(this.a);
    }
    c();
  }
}
obj.b();
화살표 함수는 자신만의 this를 가지지 않기 때문에 자신이 선언된 렉시컬 스코프의 this 값을 상속 받습니다. 따라서 화살표 함수로 선언된 cbthis를 상속 받아 obj를 가리키게 됩니다.
eeeyooon
제가 선택한 방법은 bind 메서드를 사용하는 것입니다. bind는 새로운 함수를 생성하며, 이 함수의 this를 메서드에서 전달된 첫 번째 인수로 설정합니다. 따라서 bind를 사용하면 내부 함수 c의 this 값을 b의 this과 동일하게 obj를 바라보게 할 수 있습니다.

var a = "global!";
var obj = {
  a: "object!",
  b: function() {
    console.log(this.a); // object!

    var c = function() {
      console.log(this.a); // object!
    }.bind(this);

    c();
  }
}
obj.b();
lulla-by
위에 답변해주신 분들께서 화살표 함수와 bind 메서드를 활용하는 방법을 설명해주셔서 저는 변수를 활용하여 우회하는 케이스로 접근해봤습니다.

해당 방식은 es5까지 자체적으로 내부 함수에 this를 상속할 방법이 없을 때 사용한 방식으로 살짝 허무하지만 기대한대로 작동합니다.

var a = "global!";
var obj = {
  a: "object!",
  b: function() {
    console.log(this.a); // object!

    var self = this;

    function c() {
      console.log(self.a); // object!
    }
    c();
  }
}
obj.b();

출제자 : Stilllee

1. 변수명을 이용한 우회
this를 다른 변수에 할당하고 내부 함수에서 이 변수를 사용하는 방식으로, this의 값이 함수가 선언될 때 결정되는 것을 활용합니다. 일반적으로는 self, that, _this, _ 등의 변수명을 사용합니다.

var a = "global!";
var obj = {
  a: "object!",
  b: function() {
    var self = this;
    console.log(this.a); // object!

    function c() {
      console.log(self.a); // object!
    }
    c();
  }
}
obj.b();


2. 화살표 함수
ES6에서 도입된 화살표 함수는 자체적인 this바인딩을 갖지 않고, 상위 스코프의 this를 그대로 사용합니다.

var a = "global!";
var obj = {
  a: "object!",
  b: function() {
    console.log(this.a); // object!

    const c = () => {
      console.log(this.a); // object!
    }
    c();
  }
}
obj.b();


3. call / apply 메서드
call 또는 apply 메서드를 사용하여 함수를 호출하면서 this 값을 명시적으로 바인딩 할 수 있습니다.

var a = "global!";
var obj = {
  a: "object!",
  b: function() {
    console.log(this.a); // object!

    function c() {
      console.log(this.a); // object!
    }
    c.call(this);
  }
}
obj.b();


4. bind 메서드
bind메서드를 사용하여 함수의 this를 고정시킬 수 있습니다. 이렇게 하면 함수가 어디에서 호출되더라도 thisbind에 전달된 객체를 가리킵니다.

var a = "global!";
var obj = {
  a: "object!",
  b: function() {
    console.log(this.a); // object!

    function c() {
      console.log(this.a); // object!
    }.bind(this);
    c();
  }
}
obj.b();




2. 메서드의 this와 메서드 내부의 콜백 함수의 this

메서드의 this와 메서드 내부 콜백 함수의 this 불일치 문제를 해결 후 설명해주세요.

const myObj = {
  myProperty: 'Hello, world!',
  outerFunc: function() {
    console.log(this.myProperty); // 'Hello, world!'
    setTimeout(function innerFunc() {
      console.log(this.myProperty); // undefined
    }, 1000);
  }
};

myObj.outerFunc();

현재 innerFunc 함수 내부에서의 this.myPropertyundefined를 출력합니다. 이는 innerFuncmyObj 객체의 컨텍스트를 this로 가지지 않기 때문입니다.

innerFunc 내에서 this.myPropertymyObj.myProperty를 정확히 참조하도록 하려면 코드를 어떻게 수정해야 할까요?

코드를 수정한 후 (1)innerFunc가 왜 undefined를 출력했는지(2)이 문제를 해결한 방법, 두 가지에 대해 설명해주세요.

eeeyooon

const myObj = {
  myProperty: 'Hello, world!',
  outerFunc: function() {
    console.log(this.myProperty); // 'Hello, world!'
    setTimeout(() => { //
      console.log(this.myProperty); // 'Hello, world!'
    }, 1000);
  }
};

myObj.outerFunc();
(1) 기존 코드 innerFunc에서 this.myProperty가 undefined를 출력한 이유는 setTimeout에 의해 호출된 일반 함수 내에서의 this는 전역 객체를 가리키기 때문입니다. 이때 myProperty는 전역 객체가 아니기 때문에 undefined가 됩니다.

(2) setTimeout의 콜백 함수를 일반 함수(innerFunc)에서 화살표 함수로 변경하여 해결했습니다. 이 화살표 함수는 outFunc의 this를 상속받으므로, this.myProperty는 myObj의 myProperty를 참조하게 됩니다.
Stilllee
(1) : innerFunc에서 undefined 출력의 이유
  • 문제 코드에서 innerFuncsetTimeout내의 콜백 함수로 사용되었습니다.
  • 자바스크립트에서 setTimeout과 같은 내장 함수 내에서 일반 함수로 호출되는 콜백함수의 this는 기본적으로 전역 컨텍스트를 가리킵니다.
  • 따라서, 전역 스코프에는 myProperty라는 이름의 속성이 존재하지 않기 때문에 undefined를 반환하는 것입니다.


  • (2) : bind메서드로 문제 해결
    
    const myObj = {
      myProperty: "Hello, world!",
      outerFunc: function () {
        console.log(this.myProperty);
        setTimeout(
          function innerFunc() {
            console.log(this.myProperty);
          }.bind(this),
          1000
        );
      },
    };
    
    myObj.outerFunc();
    
  • bind메서드를 사용하여 innerFuncthisouterFuncthismyObj객체로 명시적으로 설정했습니다.
  • .bind(this)를 호출함으로써 innerFunc내의 this는 항상 outerFunc가 호출될 때의 컨텍스트인 myObj객체를 가리키게 됩니다.
  • 따라서, myObjmyProperty속성을 정확히 참조하여 "Hello, world!"를 출력합니다.
  • lulla-by
    (1) innerFunc가 왜 undefined를 출력했는지
    콜백 함수 내부에서의 this는 콜백함수의 제어권을 가진 함수가 this를 어떻게 정했는지에 따라 this가 달라집니다. 만약 this를 설정하지 않은 상황이라면 콜백함수는 기본적으로 전역 객체를 바라봅니다. 콜백함수 내부에서 this.myProperty를 호출했는데 이때 this는 전역 객체를 바라봅니다. 따라서 미리 전역 객체에 myProperty를 선언 및 할당하지 않는 이상 undefined가 출력됩니다.

    (2) 이 문제를 해결한 방법
    저는 apply 메서드를 사용하여 setTimout 내부의 콜백 함수가 myObjthis로 바라보도록 설정했습니다.
    
    const myObj = {
      myProperty: 'Hello, world!',
      outerFunc: function() {
        console.log(this.myProperty); // 'Hello, world!'
        setTimeout(function innerFunc() {
          console.log(this.myProperty); // 'Hello, world!'
        }.apply(this), 1000);
      }
    };
    
    myObj.outerFunc();
    

    출제자 : chaehaeun

    const myObj = {
      myProperty: 'Hello, world!',
      outerFunc: function() {
        console.log(this.myProperty); // 'Hello, world!'
        setTimeout((function innerFunc() {
          console.log(this.myProperty); // 'Hello, world!'
        }).bind(this), 1000);
      }
    };
    
    myObj.outerFunc();
    

    1. innerFunc가 undefined를 출력한 이유:
    setTimeout 내부에서 호출되는 함수는 기본적으로 전역 컨텍스트에서 실행됩니다. 따라서 this는 전역 객체를 가리키게 되고, myObj와는 무관한 컨텍스트가 되기 때문에 this.myPropertyundefined를 반환합니다.

    2. 이 문제를 해결한 방법
    bind를 사용하여 innerFunc 함수의 thisouterFuncthis에 명시적으로 바인딩했습니다. .bind(this)outerFunc가 호출될 때의 this, 즉 myObjinnerFuncthis에 고정시킵니다. 이렇게 하면 setTimeout 내부에서 실행되더라도 innerFuncthismyObj를 정확히 가리키게 되어 this.myProperty가 정상적으로 'Hello, world!'를 출력하게 됩니다.




    3. this

    다음에서 제시하는 this 관련 단답형 문제에 대해 간단한 답변을 작성해주세요. 책 내용을 바탕으로 간단하게 작성해주세요.

    1. this는 언제 결정되나요?
    2. 프로그래밍 언어에서 함수와 메서드는 미리 정의한 동작을 수행하는 코드 뭉치입니다. 이 둘을 구분하는 유일한 차이는 무엇일까요? 또한, "함수로서의 호출"과 "메서드로서 호출"을 어떻게 구분하는지 말씀해주세요.
    3. 생성자(클래스)와 인스턴스는 무엇인가요?
    4. 화살표 함수의 this는 어떤 특징을 가지고 있나요?
    chaehaeun
    1. 함수나 메서드가 호출될 때 결정됩니다.

    2. 함수와 메서드의 유일한 차이는 메서드가 클래스의 인스턴스나 객체에 종속되어 있음입니다. "함수로서의 호출"은 독립적으로 호출되며, "메서드로서의 호출"은 객체의 속성으로 호출됩니다.

    3. 생성자 함수는 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수입니다. 그리고 그 생성자 함수를 통해 만든 객체를 인스턴스라고 합니다.

    4. 화살표 함수는 자신만의 this가 없기 때문에 언제나 상위 스코프의 this를 참조하는 특징을 가지고 있습니다.
    Stilllee
    1. 자바스크립트에서 this는 실행 컨텍스트가 생성될 때 즉, 함수가 호출될 때 결정되며 어떻게 호출되는지에 따라 this의 값이 달라집니다.

    2. 함수가 변수나 상수에 할당되어 독립적으로 호출되는 것은 함수로서의 호출이고, 객체의 프로퍼티에 할당되어 해당 객체의 컨텍스트에서 호출되는 것을 메서드로서의 호출입니다.

    3. 클래스는 어떤 공통된 성질을 지니는 객체들을 생성하는데 사용하는 함수이고, 클래스를 통해 만든 객체를 인스턴스라고 합니다.

    4. 화살표 함수는 자신만의 this를 생성하지 않고, 자신이 생성된 그 스코프의 this를 사용합니다. 즉, 화살표 함수 내에서의 함수는 독립적인 기능을 수행하는 반면, 메서드는 객체와 연관된 동작을 수행합니다. this는 항상 외부 함수의 this와 같습니다.
    lulla-by
    1. this는 함수를 호출할 때 결정됩니다.

    2. 둘을 구분하는 차이는 독립성입니다. 함수는 함수 그 자체로서 호출되는 것이지만 메서드는 자신을 호출한 대상에 대하여 함수를 실행하게 됩니다. 이 둘을 구분하는 대표적인 방법으로는 .과 대괄호 표기법으로 함수 앞에 자신을 호출한 객체가 있는 경우 메서드가 됩니다.

    3. class는 구체적인 인스턴스를 만들기 위한 일종의 틀입니다. 인스턴스는 생성자 함수를 사용하여 생성된 객체입니다.

    4. 화살표 함수는 this 바인딩의 과정 자체가 생략되어 있습니다. 따라서 내부에 this값이 존재하지 않으며 접근하고자 할 경우 스코프체인상 가장 가까운 this에 접근하게 됩니다.

    출제자 : eeeyooon

    1. this는 언제 결정되나요?
    기본적으로 실행 컨텍스트가 생성될 때 함께 결정됩니다. 실행컨텍스트는 함수를 호출할 때 생성되므로, 바꿔말하면 this는 함수를 호출할 때 결정된다고 할 수 있습니다.

    2. 프로그래밍 언어에서 함수와 메서드는 미리 정의한 동작을 수행하는 코드 뭉치입니다. 이 둘을 구분하는 유일한 차이는 무엇일까요? 또한 "함수로서의 호출"과 "메서드로서 호출"을 어떻게 구분하는지 말씀해주세요.
    독립성. 함수는 그 자체로 독립적인 기능을 수행하는 반면, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행합니다. 자바스크립트는 상황 별로 this 키워드에 다른 값을 부여하게 함으로써 이를 구현했습니다.

    ‘함수로서 호출’과 ‘메서드로서 호출’을 점(.)의 유무로 구별할 수 있습니다. 앞에 점이 없으면 함수로서 호출한 것(func(1);)이고, 앞에 점이 있으면 메서드로서 호출한 것(obj.method(2);)입니다. 덧붙여, 대괄호 표기법에 따른 경우(obj[’method’](2))도 메서드로서 호출한 것입니다.

    정리하자면, 점 표기법이든 대괄호 표기법이든 어떤 함수를 호출할 때 그 함수 이름(프로퍼티명) 앞에 객체가 명시되어 있는 경우에는 메서드로 호출한 것이고, 그렇지 않은 모든 경우는 함수로 호출한 것입니다."

    3. 생성자(클래스)와 인스턴스는 무엇인가요?
    클래스를 통해 만든 객체를 인스턴스라고 합니다. 생성자는 구체적인 인스턴스를 만들기 위한 일종의 틀입니다. 이 틀에는 해당 클래스의 공통 속성들이 미리 준비되어있고, 여기에 구체적인 인스턴스의 개성을 더해 개별 인스턴스를 만들 수 있습니다.

    • 생성자 = 기본 붕어빵 반죽, 굽기에 알맞은 온도와 시간이 설정된 붕어빵 틀
    • 인스턴스의 개성 = 피자치즈, 슈크림, 팥
    • 개별 인스턴스 = 슈붕, 팥붕, 피붕

    4. 화살표 함수의 this는 어떤 특징을 가지고 있나요?
    화살표 함수는 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠지게 되어, 상위 스코프의 this를 그대로 활용할 수 있습니다. (ES5 환경에서는 화살표 함수를 사용할 수 X)

    More from this blog

    💥 new Array(length).map()으로 배열 초기화 시 콜백이 실행되지 않는 문제

    JavaScript에서 배열을 초기화하려고 new Array(4).map(() => [])을 사용했는데 의도대로 동작하지 않았다. 원인을 찾아보니 JavaScript의 빈 슬롯과 undefined의 차이, 그리고 map() 메서드가 빈 슬롯에 대해 콜백을 실행하지 않는다는 특성때문이었다. 문제 상황 특정 크기의 배열을 만들고 각 요소를 []로 초기화 하려고 했다. const result = new Array(4).map(() => []); co...

    Jun 28, 20252 min read17

    💥 에러 메시지와 스택 트레이스 분석을 통해 문제 해결하기

    JavaScript로 카드 게임 로직을 구현하던 중 TypeError: Cannot read properties of undefined라는 에러를 만났다. 코드가 복잡하고 짐작가는 부분이 단번에 떠오르지 않아 막막했는데, 에러 메세지와 스택 트레이스를 차근차근 분석해가며 원인을 찾아낸 과정을 기록해본다. 에러 발생 게임 로직을 테스트하던 중 특정 입력에서 다음과 같은 에러가 발생했다. TypeError: Cannot read properties...

    Jun 25, 20254 min read11

    🧐 적절한 함수명과 단일 책임으로 코드 개선하기

    이 글에서는 JavaScript로 구현된 보드게임의 로직을 개선하는 과정을 다루며, 개선 과정은 함수명 개선 → 중복 제거 → 책임 분리의 단계로 이루어져있다. 작동하지만 개선이 필요한 코드 처음에 작성한 코드는 기능적으로는 문제없이 동작했지만, 더 나은 방식을 고민하다 보니 여러 개선점을 찾을 수 있었다. function checkLadder(position) { switch (position) { case 4: return 14;...

    Jun 23, 20253 min read23

    작심삼십일 - TypeScript 강의를 완강하다.

    유데미 작심삼십일 챌린지에 참여하다 작심삼십일 챌린지는 유데미의 한국 런칭 3주년을 기념한 이벤트 중 하나로, 30일 동안 유데미 강의를 들으며 인증을 하는 챌린지이다.취업 준비를 하면서 여러가지 요인으로(주로 심리적이지만..) 학습 루틴이 망가졌던 찰나에 이 이벤트를 보게되었고, 마침 기존에 사두고 볼 엄두가 안 났던 타입스크립트 강의가 있었기에 옳다구나! 하고 신청했었다. 챌린지를 통해 무엇을 배웠나? 챌린지의 목표로 정한 강의는 유데미에...

    Nov 18, 20242 min read58
    작심삼십일 - TypeScript 강의를 완강하다.

    woodstock's devlog

    215 posts

    안녕하세요! 프론트엔드 개발자 woodstock입니다.

    제 블로그에서는 프론트엔드 개발에 대한 다양한 주제를 다룹니다.

    JavaScript, TypeScript, React, Next.js 등의 기술을 활용하여 실제 프로젝트를 진행하면서 얻은 경험과 지식을 공유하고자 하며, 컴퓨터 과학(CS)의 기본 개념과 알고리즘, 자료구조 등에 대해서도 다룹니다.