JavaScript 의 this
는 다른 언어와는 다르게 동작한다.
JavaScript에서는 대부분의 경우 this
의 값은 함수를 호출한 방법에 의해 결정된다.
실행 중에는 값을 할당으로 설정할 수 없고 함수를 호출할 때 마다 값이 달라질 수 있다.
ES5(2009)는 함수를 어떻게 호출했는지 상관하지 않고 this
값을 설정할 수 있는 bind 메서드
를 도입했고,
ES6(ES2015)는 스스로의 this
바인딩을 제공하지 않는 화살표 함수를 추가했다.
this
의 값은 function, class 또는 global 중 어느 context 에 나타나는지에 따라 달라진다.
또한 엄격 모드와 비엄격 모드에서도 일부 차이가 있다.
this
의 값은 함수 호출 방식과 함수가 정의된 위치에 따라 결정된다.
this
는 전역 객체를 가리킨다. 즉, 브라우저 환경에서는 window
객체를 의미한다.this
는 함수를 호출한 시점의 객체를 가리킨다.this 바인딩
을 가지지 않고, 정의 시점의 this
를 상속받는다.this
의 값을 명시적으로 설정할 수 있다. (bind, call, apply)new
키워드를 사용하여 생성자 함수를 호출하면, this
는 새로 생성된 객체를 가리킨다.this
는 해당 메서드를 호출한 객체를 가리킨다.전역 컨택스트에서 this
는 엄격 모드 여부에 관계없이 전역 객체를 참조한다.
브라우저 환경에서는 window
객체가, Node.js 환경에서는 global
객체가 전역 객체이다.
console.log(this); // window
a = 37;
console.log(window.a); // 37
함수 내부에서 this
의 값은 함수를 호출한 방법에 의해 달라진다.
비엄격 모드에서는 전역 객체
를 가리키지만, 엄격 모드(strict mode)에서는 undefined
가 된다.
비엄격 모드에서 this
는 전역 객체를 가리킨다.
function f1() {
return this;
}
// 브라우저
f1() === window; // true
// Node.js
f1() === global; // true
반면, 엄격 모드에서 this
는 실행 문맥에 진입하며 설정되는 값을 유지하므로, undefined
가 된다.
function f2() {
"use strict"; // 엄격 모드
console.log(this);
}
f2(); // undefined
화살표 함수는 자신만의 this 바인딩
을 가지지 않고, 정의될 때의 상위 스코프의 this
를 상속받는다.
const obj = {
name: 'Eve',
getName: function() {
const arrowFunc = () => console.log(this.name); // this는 obj를 가리킴
arrowFunc();
}
};
obj.getName(); // 'Eve'
일반 함수와 화살표 함수에서의 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
일반 함수
a. 내부 this
는 obj 객체를 가리킨다.
b. 그러나 setTimeout 내부의 일반함수는 자신만의 this
를 가지므로, 이 경우 전역 객체(window / global 객체) 를 가리킨다.
화살표 함수
a. 내부 this
는 obj 객체를 가리킨다.
b. setTimeout 내부의 화살표 함수는 자신만의 this
바인딩을 가지지 않으므로, 정의된 위치의 상위 스코프의 this
를 상속받아 obj 객체를 가리킨다.
(상위 스코프의 함수가 객체의 메서드로써 호출되기 때문에 this
는 메서드를 호출한 객체를 가리킴.)
call, apply, bind 메서드들을 사용하여 this
의 값을 명시적으로 설정할 수 있다.
이것도 궁금해서 GPT-4o 한테 물어봤습니다.. 예시까지 친절하게 알려주더군요...
예시를 보면 이해가 더 쉽게 됩니다. 예시는 아래에 있습니당
call
: 함수를 즉시 호출하면서 this
와 args를 개별적으로 전달.apply
: 함수를 즉시 호출하면서 this
와 args를 배열로 전달.bind
: this
와 일부 args를 설정하여 새로운 함수를 반환함. 함수 호출은 나중에 이루어짐.함수를 호출하면서 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!
함수를 호출하면서 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!
함수의 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
로 호출한 새 함수의 this
는 호출방식과 상관없이 영구적으로 bind()
의 첫번째 매개변수로 고정된다.func.bind(obj)
이렇게 호출하면 func
과 같은 코드 및 범위를 가졌지만 this
는 원본 함수를 가진 새로운 함수를 생성하기 때문이다.new
키워드를 사용하여 생성자 함수를 호출하면, this
는 새로 생성된 객체를 가리킨다.
function Person(name) {
this.name = name;
}
const person = new Person('Eve');
console.log(person.name); // 'Eve'
함수가 객체의 프로퍼티 값이면 메서드로서 호출된다.
객체의 메서드를 호출할 때, this
는 해당 메서드를 호출한 객체를 가리킨다.
const obj = {
name: 'Eve',
getName: function() {
console.log(this.name); // this는 obj를 가리킴
}
};
obj.getName(); // 'Eve'
이렇게 정리해보니까 JavaScript 에서의 this
의 값을 결정하는 방법이 다양하기 때문에
this
는 최대한 명시적으로 바인딩해서 사용하는 것이 좋아보이네요..!