[JS] 자바스크립트의 this

jiny·2025년 1월 17일

기술 면접

목록 보기
27/78

🗣️ 자바스크립트의 this가 어떤 값을 가지는지 각 상황을 가정하여 설명해주세요.

  • 의도: 각 상황에 따라 this의 변화를 이해하고 있는지 확인하기 위한 질문

  • 나의 답안

    this는 자바스크립트에서 함수가 호출되는 방식에 따라 값이 결정되는 특별한 키워드입니다.
    즉, this는 함수의 선언 위치가 아니라 실행 시점에 동적으로 결정됩니다.

    전역 범위에서 this는 일반적으로 전역 객체를 참조합니다.
    브라우저 환경에서는 window 객체, Node.js 환경에서는 global 객체를 참조합니다.

    일반 함수 호출에서는 this가 기본적으로 전역 객체를 참조합니다.
    다만, use strict 모드에서는 thisundefined가 됩니다.

    메서드를 호출할 때, this메서드를 가지고 있는 객체를 참조합니다.

    생성자 함수에서의 this새로 생성된 빈 객체를 참조합니다.

    화살표 함수렉시컬 컨텍스트에서 this를 결정합니다.
    렉시컬 컨텍스트는 자바스크립트 코드가 작성된 물리적인 위치에 따라 변수와 함수의 스코프가 결정되는 방식입니다.
    즉, 화살표 함수의 this선언된 위치의 상위 스코프에서 this를 가져옵니다.

    이벤트 핸들러에서 this이벤트를 바인딩한 DOM 요소를 참조합니다.
    이때, 일반 함수가 아닌 화살표 함수를 사용하면 this상위 스코프를 참조합니다.

    마지막으로, 클래스에서의 this생성된 인스턴스 객체를 참조합니다.

  • 주어진 답안 (모범 답안)

    this는 호출되는 상황마다 서로 다른 값을 참조하고 있어 혼동하기 쉬운 값입니다.

    우선 일반 함수를 호출할 때는 전역 객체인 window를 뜻합니다.
    그리고 객체의 메서드를 호출할 때에는 해당 객체를 의미합니다.

    이어서 콜백 함수는 전달하는 곳은 상관이 없고, 함수가 실제로 호출되는 상황에 따라 달리집니다.
    만약 호출하는 상황이 일반 함수라면 전역 객체를 가리킵니다.

    마지막으로 화살표 함수생성된 함수의 스코프에 따라 달라집니다.
    예를 들어 객체의 메서드에서 화살표 함수를 만들어서 this를 호출했다면 생성된 함수의 스코프는 객체 스코프이므로 여기서 this는 객체를 뜻하게 됩니다.

    이러한 this의 특징 때문에 call, apply, bind 같은 것으로 this를 고정시켜주기도 하지만, 저는 저 포함 동료분들의 혼선을 피하기 위해 this의 사용을 좀 지양하는 편입니다.


📝 개념 정리

this는 자바스크립트에서 함수가 호출되는 방식에 따라 값이 결정되는 특별한 키워드이다.
이 값은 함수의 실행 문맥(Execution Context)을 참조한다.
상황에 따라 다르게 동작하기 때문에 이해가 중요하다.

🌟 this의 동작 원리

this의 값은 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다.
즉, this는 함수의 선언 위치가 아니라 실행 시점에 결정된다.


🌟 this가 결정되는 방식

  1. 전역 컨텍스트에서의 this
    전역 범위에서 this는 일반적으로 전역 객체를 참조한다.
    • 브라우저 환경에서는 window 객체
    • Node.js 환경에서는 global 객체
      console.log(this); // 브라우저: window, Node.js: {}
  1. 함수 호출에서의 this
    일반 함수 호출에서는 this가 기본적으로 전역 객체를 참조한다.
    use strict 모드에서는 thisundefined가 된다.

    function globalFunction() {
      console.log(this); // 브라우저: window, Node.js: global
    }
    globalFunction();
    
    'use strict';
    function strictFunction() {
      console.log(this); // undefined
    }
    strictFunction();
  1. 메서드 호출에서의 this
    메서드를 호출할 때, this메서드를 소유하는 객체를 참조한다.

    const obj = {
      name: 'Object',
      method() {
        console.log(this.name);
      }
    };
    
    obj.method(); // "Object"
  2. 생성자 함수에서의 tihs
    생성자 함수는 new 키워드와 함께 호출되며, this새로 생성된 빈 객체를 참조한다.

    function Person(name) {
      this.name = name;
    }
    
    const person = new Person("Alice");
    console.log(person.name); // "Alice"
  3. this를 명시적으로 설정하는 이유
    자바스크립트에서는 call, apply, bind 메서드를 사용하여 this를 명시적으로 설정할 수 있다.

    • callapply: 함수를 호출하면서 this를 특정 값으로 설정한다.

      • call: 인수를 개별적으로 전달
      • apply: 인수를 배열로 전달
      function greet(greeting) {
        console.log(`${greeting}, ${this.name}`);
      }
      
      const user = { name: "Alice" };
      greet.call(user, "Hello"); // "Hello, Alice"
      greet.apply(user, ["Hi"]); // "Hi, Alice"
    • bind: 새로운 함수를 반환하며, this를 영구적으로 설정한다.

      const boundGreet = greet.bind(user);
      boundGreet("Hey"); // "Hey, Alice"
  4. 화살표 함수에서의 this
    화살표 함수는 렉시컬 컨텍스트에서 this를 결정한다.
    즉, 화살표 함수의 this선언된 위치의 상위 스코프의 this를 사용한다.

    const obj = {
      name: "Object",
      arrowMethod: () => {
        console.log(this.name); // 화살표 함수의 this는 전역 객체 참조
      },
      regularMethod() {
        console.log(this.name); // 일반 함수의 this는 obj 참조
      }
    };
    
    obj.arrowMethod(); // undefined (strict mode) 또는 window.name
    obj.regularMethod(); // "Object"
    • 렉시컬 컨텍스트(Lexical Context)

      렉시컬 컨텍스트는 자바스크립트 코드가 작성된 물리적인 위치에 따라 변수와 함수의 스코프가 결정되는 방식이다.
      이 개념은 자바스크립트의 스코프 체인(Scope Chain)클로저(Closure)와 밀접하게 관련되어 있으며, 특히 화살표 함수에서 this가 동작하는 방식의 기초가 된다.

      ⭐ 렉시컬 컨텍스트의 핵심 개념

      1. 렉시컬 스코프(Lexical Scope)
        렉시컬 스코프는 코드가 작성된 위치에 따라 변수와 함수의 유효 범위가 결정되는 것을 의미한다.
        자바스크립트는 정적 스코핑(static scoping)을 사용하며, 실행 시점이 아니라 코드 작성 시점에 스코프가 결정된다.

        function outer() {
          const outerVar = "I am from outer";   
          
          function inner() {
            console.log(outerVar); // outerVar를 inner에서 참조 가능
          }     
          
          inner();
        }
        
        outer(); // "I am from outer"
        • inner 함수는 작성된 위치에 따라 바로 바깥의 스코프outer 함수의 변수를 참조할 수 있다.
        • outerVarinner 함수 내부에서 사용할 수 있지만, 반대로는 불가능하다.
      2. 렉시컬 컨텍스트와 실행 컨텍스트
        렉시컬 컨텍스트는 코드가 작성된 물리적 구조를 기반으로 스코프 체인을 정의하는 반면, 실행 컨텍스트는 함수 호출 시 만들어지는 실행 환경을 나타낸다.

        • 렉시컬 컨텍스트는 컴파일 시 결정되며 변경되지 않는다.
        • 실행 컨텍스트는 함수 호출 시 동적으로 생성되고, this와 같은 실행 정보를 포함한다.
      3. 화살표 함수와 렉시컬 컨텍스트
        화살표 함수는 this를 포함한 컨텍스트를 렉시컬하게 결정한다.
        즉, 화살표 함수의 this는 선언된 위치의 상위 스코프에서 this를 가져온다.

        const obj = {
          name: "Alice",
          regularFunction: function () {
            console.log(this.name); // "Alice"
          },
          arrowFunction: () => {
            console.log(this.name); // 상위 스코프의 this (전역 객체)
          }
        };
        
        obj.regularFunction(); // "Alice"
        obj.arrowFunction(); // undefined (strict 모드) 또는 window.name
        • 일반 함수의 this는 호출 시점에 결정된다.
        • 화살표 함수의 this는 선언된 위치에서 렉시컬하게 상속받는다.

      ⭐ 렉시컬 컨텍스트의 특징

      1. 변수의 유효 범위 결정
        함수 내부에서 외부 스코프의 변수에 접근할 수 있지만, 반대는 불가능하다.
        function outer() {
          const outerVar = "Outer Variable";
          
          function inner() {
            console.log(outerVar); // 접근 가능
          }
          
          inner();
        }
        
        console.log(outerVar); // ReferenceError: outerVar is not defined
      2. 중첩된 함수와 클로저
        함수가 자신이 선언된 위치의 렉시컬 스코프를 기억하고, 이를 기반으로 클로저를 생성한다.
        function createCounter() {
          let count = 0;
          
          return function () {
            count++;
            return count;
          };
        }
        
        const counter = createCounter();
        console.log(counter()); // 1
        console.log(counter()); // 2

      ⭐ 렉시컬 컨텍스트와 this의 차이
      렉시컬 컨텍스트스코프와 관련된 개념이고, this실행 컨텍스트에 따라 동적으로 설정되는 개념이다.
      화살표 함수는 렉시컬 컨텍스트에서 this를 결정한다는 점에서 렉시컬 컨텍스트와 연결된다.

  5. DOM 이벤트 핸들러에서의 this
    이벤트 핸들러에서 this이벤트를 바인딩한 DOM 요소를 참조한다.

    const button = document.querySelector("button");
    button.addEventListener("click", function () {
      console.log(this); // 클릭된 button 요소
    });

    화살표 함수를 사용하면 this상위 스코프를 참조한다.

    button.addEventListener("click", () => {
      console.log(this); // 전역 객체 (window)
    });
  6. 클래스에서의 this
    클래스에서의 this는 생성된 인스턴스 객체를 참조한다.

    class Person {
      constructor(name) {
        this.name = name;
      }
      sayName() {
        console.log(this.name);
      }
    }
    
    const person = new Person("Alice");
    person.sayName(); // "Alice"

🌟 일반적인 함정

  1. 객체 메서드 참조 분리
    객체의 메서드를 변수에 할당하거나 다른 함수에 전달하면 this가 달라질 수 있다.

    const obj = {
      name: "Object",
      method() {
        console.log(this.name);
      }
    };
    
    const detached = obj.method;
    detached(); // undefined (전역 객체의 this)

    해결 방법: bind를 사용하여 this를 고정한다.

    const boundMethod = obj.method.bind(obj);
    boundMethod(); // "Object"
  2. 화살표 함수와 this
    화살표 함수는 렉시컬 스코프의 this를 사용하므로 객체에서 사용할 때 주의해야 한다.

    const obj = {
      name: "Object",
      arrowMethod: () => {
        console.log(this.name); // undefined
      }
    };
    
    obj.arrowMethod();
  3. 이벤트 핸들러에서의 this
    화살표 함수로 이벤트 핸들러를 정의하면 this가 의도와 다르게 전역 객체를 참조할 수 있다.

    document.body.innerHTML = `
      <button id="myButton">Click Me</button>
    `;
    
    const button = document.getElementById("myButton");
    
    const obj = {
      label: "Button Label",
      handleClick: () => {
        console.log(this.label); // 여기서의 this는 obj가 아님
      },
    };
    
    button.addEventListener("click", obj.handleClick);
    • 위 코드를 실행하면 버튼을 클릭했을 때 this.labelundefined로 출력된다.
    • 화살표 함수 내부의 this이벤트 핸들러를 등록한 객체(obj)가 아닌 전역 객체(window 또는 undefined(strict mode))를 참조하기 때문이다.

    해결 방법
    1) 일반 함수로 this 바인딩

    const obj = {
      label: "Button Label",
      handleClick: function () {
        console.log(this.label); // this는 obj를 참조
      },
    };
    
    button.addEventListener("click", obj.handleClick);

    2) bind로 명시적으로 this 설정

    const obj = {
      label: "Button Label",
      handleClick: function () {
        console.log(this.label); // this는 obj를 참조
      },
    };
    
    button.addEventListener("click", obj.handleClick.bind(obj));

    3) 화살표 함수 내부에서 명시적으로 this를 사용하지 않기

    const obj = {
      label: "Button Label",
      init: function () {
        const self = this; // obj를 참조하는 self
        button.addEventListener("click", () => {
          console.log(self.label); // 화살표 함수 안에서도 self를 사용
        });
      },
    };
    
    obj.init();

🌟 정리

  • this함수 호출 방식에 따라 값이 동적으로 결정된다.
  • 전역, 함수, 메서드, 생성자 함수, 명시적 호출, 화살표 함수 등 각 상황에서 다르게 동작한다.
  • 화살표 함수는 상위 스코프의 this를 사용하며, bind, call, apply를 통해 명시적으로 설정할 수 있다.
  • 코드 작성 시 this의 동작을 정확히 이해하고, 상황에 맞는 패턴과 메서드(bind, 화살표 함수 등)를 사용해 예기치 않은 동작을 방지하는 것이 중요하다.

0개의 댓글