JavaScript 의 this

yiwoojung·2024년 6월 8일
0

JavaScript

목록 보기
3/4
post-thumbnail

JavaScript 의 this 는 다른 언어와는 다르게 동작한다.
JavaScript에서는 대부분의 경우 this 의 값은 함수를 호출한 방법에 의해 결정된다.
실행 중에는 값을 할당으로 설정할 수 없고 함수를 호출할 때 마다 값이 달라질 수 있다.

ES5(2009)는 함수를 어떻게 호출했는지 상관하지 않고 this 값을 설정할 수 있는 bind 메서드 를 도입했고,
ES6(ES2015)는 스스로의 this 바인딩을 제공하지 않는 화살표 함수를 추가했다.

this 의 값은 function, class 또는 global 중 어느 context 에 나타나는지에 따라 달라진다.
또한 엄격 모드와 비엄격 모드에서도 일부 차이가 있다.



🤯 냅다 결론부터

this 의 값은 함수 호출 방식과 함수가 정의된 위치에 따라 결정된다.

  1. 전역 context 에서 this전역 객체를 가리킨다. 즉, 브라우저 환경에서는 window 객체를 의미한다.
  2. 화살표 함수가 아닌 일반 함수 내에서는 this함수를 호출한 시점의 객체를 가리킨다.
  3. 화살표 함수는 자신만의 this 바인딩을 가지지 않고, 정의 시점의 this 를 상속받는다.
  4. 메서드들을 사용하여 this 의 값을 명시적으로 설정할 수 있다. (bind, call, apply)
  5. new 키워드를 사용하여 생성자 함수를 호출하면, this는 새로 생성된 객체를 가리킨다.
  6. 객체의 메서드를 호출할 때, this는 해당 메서드를 호출한 객체를 가리킨다.


1. 전역 컨텍스트 Global Context

전역 컨택스트에서 this 는 엄격 모드 여부에 관계없이 전역 객체를 참조한다.
브라우저 환경에서는 window 객체가, Node.js 환경에서는 global 객체가 전역 객체이다.


console.log(this); // window

a = 37;
console.log(window.a); // 37

2. 함수 호출 Function Context

함수 내부에서 this 의 값은 함수를 호출한 방법에 의해 달라진다.
비엄격 모드에서는 전역 객체를 가리키지만, 엄격 모드(strict mode)에서는 undefined가 된다.


2-1. 비엄격 모드 호출

비엄격 모드에서 this 는 전역 객체를 가리킨다.

function f1() {
  return this;
}

// 브라우저
f1() === window; // true

// Node.js
f1() === global; // true

2-2. 엄격 모드 호출

반면, 엄격 모드에서 this 는 실행 문맥에 진입하며 설정되는 값을 유지하므로, undefined 가 된다.

function f2() {
  "use strict"; // 엄격 모드
  console.log(this);
}

f2(); // undefined

3. 화살표 함수 Arrow Function

화살표 함수는 자신만의 this 바인딩을 가지지 않고, 정의될 때의 상위 스코프의 this 를 상속받는다.

const obj = {
  name: 'Eve',
  getName: function() {
    const arrowFunc = () => console.log(this.name); // this는 obj를 가리킴
    arrowFunc(); 
  }
};

obj.getName(); // 'Eve'


🫨 일반 함수와 화살표 함수에서의 this 의 차이

일반 함수와 화살표 함수에서의 this 의 차이가 더 궁금해서 GPT-4o한테 물어봤다...!

결론적으로 일반 함수는 호출 시점에 this가 결정되지만, 화살표 함수는 정의 시점의 this를 상속받는다는 차이가 있다고 한다.

const obj = {
    value: 42,
    regularFunction: function() {
        console.log('regularFunction this:', this);
        setTimeout(function() {
            console.log('setTimeout regularFunction this:', this);
        }, 1000);
    },
    arrowFunction: function() {
        console.log('arrowFunction this:', this);
        setTimeout(() => {
            console.log('setTimeout arrowFunction this:', this);
        }, 1000);
    }
};

obj.regularFunction();
// regularFunction this: obj
// setTimeout regularFunction this: Window / global

obj.arrowFunction();
// arrowFunction this: obj
// setTimeout arrowFunction this: obj
  1. 일반 함수
    a. 내부 this 는 obj 객체를 가리킨다.
    b. 그러나 setTimeout 내부의 일반함수는 자신만의 this 를 가지므로, 이 경우 전역 객체(window / global 객체) 를 가리킨다.

  2. 화살표 함수
    a. 내부 this 는 obj 객체를 가리킨다.
    b. setTimeout 내부의 화살표 함수는 자신만의 this 바인딩을 가지지 않으므로, 정의된 위치의 상위 스코프의 this 를 상속받아 obj 객체를 가리킨다.
    (상위 스코프의 함수가 객체의 메서드로써 호출되기 때문에 this 는 메서드를 호출한 객체를 가리킴.)



4. 명시적 바인딩 (메서드 사용)

call, apply, bind 메서드들을 사용하여 this 의 값을 명시적으로 설정할 수 있다.



🤔 bind, call, apply 의 차이

이것도 궁금해서 GPT-4o 한테 물어봤습니다.. 예시까지 친절하게 알려주더군요...
예시를 보면 이해가 더 쉽게 됩니다. 예시는 아래에 있습니당

  • call : 함수를 즉시 호출하면서 this와 args를 개별적으로 전달.
  • apply : 함수를 즉시 호출하면서 this와 args를 배열로 전달.
  • bind : this일부 args를 설정하여 새로운 함수를 반환함. 함수 호출은 나중에 이루어짐.


4-1. call

함수를 호출하면서 this 값을 명시적으로 설정하고, arguments 를 개별적으로 전달한다.

사용법

function.call(thisArg, arg1, arg2, ...)

예시

function greet(greeting, punctuation) {
    console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Eve' };

// greet.apply(thisArg, greeting, punctuation)
greet.call(person, 'Hello', '!'); // Hello, Eve!

4-2. apply

함수를 호출하면서 this 값을 명시적으로 설정하고, arguments 를 배열로 전달한다.

사용법

function.apply(thisArg, [arg1, arg2, ...])

예시

function greet(greeting, punctuation) {
    console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Eve' };

// greet.apply(thisArg, [greeting, punctuation])
greet.apply(person, ['Hello', '!']); // Hello, Eve!

4-3. bind

함수의 this 값을 명시적으로 설정한 새로운 함수를 반환한다.
반환된 함수는 arguments 를 개별적으로 전달할 수 있다.

this 와 일부 arguments 설정하여 새로운 함수를 반환하고, 호출은 나중에 이루어진다.

사용법

function.bind(thisArg, arg1, arg2, ...)

예시1

function greet(greeting, punctuation) {
    console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Eve' };

// greet.bind(thisArg, greeting)
// 여기에서 두번째 인자를 넣어줘도 된다. 만약 넣는다면 여기에 넣은 인자2가 먹는다.
const boundGreet = greet.bind(person, 'Hello'); 

// 인자를 이렇게 따로 전달하는 것도 가능하다.
// 호출을 따로 해줘야 한다.
boundGreet('!'); // Hello, Eve!

예시2

function test1() {
  return this.x;
}

const aaa = test1.bind({ x: "apple" });
console.log(aaa()); // apple

const bbb = aaa.bind({ x: "banana" }); // bind는 한 번만 동작함!
console.log(bbb()); // apple

const object1 = { x: 2024, func: test1, first: aaa, second: bbb };
console.log(object1.x, object1.func(), object1.first(), object1.second()); 
// 2024, 2024, apple, apple
  • bind 는 한번만 동작한다.
  • 이렇게 bind 로 호출한 새 함수의 this 는 호출방식과 상관없이 영구적으로 bind() 의 첫번째 매개변수로 고정된다.
  • 그 이유는func.bind(obj) 이렇게 호출하면 func 과 같은 코드 및 범위를 가졌지만 this 는 원본 함수를 가진 새로운 함수를 생성하기 때문이다.

5. 생성자 호출 constructor

new 키워드를 사용하여 생성자 함수를 호출하면, this새로 생성된 객체를 가리킨다.

function Person(name) {
    this.name = name;
}

const person = new Person('Eve');
console.log(person.name); // 'Eve'

6. 객체 메서드 호출

함수가 객체의 프로퍼티 값이면 메서드로서 호출된다.
객체의 메서드를 호출할 때, this 는 해당 메서드를 호출한 객체를 가리킨다.

const obj = {
  name: 'Eve',
  getName: function() {
    console.log(this.name); // this는 obj를 가리킴
  }
};

obj.getName(); // 'Eve'

이렇게 정리해보니까 JavaScript 에서의 this 의 값을 결정하는 방법이 다양하기 때문에
this 는 최대한 명시적으로 바인딩해서 사용하는 것이 좋아보이네요..!



참고

profile
의견이 있으시거나 잘못된 내용을 발견하셨다면 재빨리 댓글 주세요 。•◡•。

0개의 댓글