보통 프로그래밍 언어에서 this는 자기 자신을 가리키는, 정확히는 자기 자신의 메모리를 가리키는 역할로 쓰인다.
아마 일반적으로 인스턴스의 필드명을 명시하기 위해 생성자에서 많이 사용했을 것이다.
키워드
함수 호출 방법에 따라 다름
단, 자바스크립트의 this는 이것과는 좀 다르게 동작한다. 특히 주목할 부분이 “자기 자신” 인데 자바스크립트의 경우, 함수 호출 마다 “자기 자신” 일수도 “자기 자신”이 아닐수도 있다. 정확히는 함수 호출마다 실행컨텍스트가 일어나면, 실행 컨텍스트의 this 바인딩이 실행될 때 this 값이 결정이 된다. 이부분이 함수가 어떻게 호출되냐에 따라 this 바인딩이 가지는 값이 달라진다.
우리가 자주 사용하는 함수 호출방식이다.
이 때 this 는 전역 객체인 window를 가리킨다.
단, strict-mode 를 사용하고 있다면 이 때의 this는 undefined 을 나타낸다.
window.color = "pink";
function foo() {
console.log(this.color);
}
foo() // pink
'use strict';
window.color = "pink";
function foo() {
console.log(this.color);
}
foo() // TypeError: Cannot read property 'color' of undefined
노드는 런타임이므로 DOM 관련 객체는 없다. 실제 Node에서 this를 확인하면 빈 객체가 나오게 되는데, Node 환경에서 기본 호출 시 this는 module.export 를 가리키기 때문이다. 단 함수코드 안에서 호출한 경우에는 global 객체를 표시한다.
console.log(this === global); // false
console.log(this === module.exports, this === exports); //true, true
function a() {
console.log(this); //Object[global]
console.log(this === exports); //false
}
암시적 호출이란, 함수가 객체의 메서드로서 호출되는 상황을 의미한다. 이 때 this 는 해당 함수를 호출한 객체를 가리킨다.
const foo = {
a: 20,
bar: function () {
console.log(this.a);
}
}
foo.bar(); // 20
단, 조심해야 할 부분이 있다. this 는 렉시컬 스코프를 참조하지 않는다. 해당 상황을 살펴보자.
// 스코프 참조 x
function foo() {
var a = 1;
bar();
}
function bar() {
console.log(this.a); // undefined
}
foo();
// 변수 선언에 의한 함수 복제
function foo() {
console.log(this.a);
}
const obj = {
a:1,
foo: foo
}
const foo2 = obj.foo;
foo2(); // undefined
// 매개변수(콜백)에 의한 함수 복제
setTimeout ( obj.foo , 100 ); // undefined
일반적으로 스코프는 어디에서 선언하냐에 따라, 자식 스코프가 부모의 스코프를 참조하는 것이 가능하다는 것을 알고 있다. this는 이와는 조금 다르다. this는 선언 시점이 아닌 런타임 시점에서 바인딩이 이루어지면 함수 호출 당시 상황에 따라 참조하는 컨텍스트가 결정된다. 즉 this는 함수를 어떻게 호출했냐에 주안점을 두는 것이다.
또한 이러한 경우에는 정상적으로 바인딩 된다. (복잡하다…)
// 체이닝에 의한 바인딩
function foo() {
console.log(this.name);
}
var person = {
name: '철수'
};
person.foo = foo;
person.foo();
// 철수
// 체이닝에 의한 바인딩2 : 가장 마지막 (최하위)에 참조한 객체가 this로 바인딩된다.
function foo() {
console.log(this.text);
}
var name = {
text: '미애',
foo: foo
};
var person = {
text: '철수',
name: name
};
person.name.foo(); // 마지막으로 참조한 name 객체가 foo() 함수에 바인딩된다.
// 미애
위의 암시적 바인딩은 this 행방을 알 수 없는 경우가 다양하다. 특히 서드 파티 API 를 빌려오는 경우, API 개발자가 this에 어떤 영향을 가게 하는 작업을 했는지 일일이 확인하기 어렵다. 이럴 때는 명시적 바인딩을 이용하면 원하는값으로 this를 변경하는 것이 가능하다. 자바스크립트의 객체 내장 함수인 call()
, apply()
, bind()
를 사용하여 함수를 호출하는 방식이 명시적 호출 방식이다.
첫 번째 인자로, 바인딩할 객체를 전달하고, 두 번째 인자부터는 기존 함수에 전달할 인자를 전달한다.
첫 번째 인자로, 바인딩할 객체를 전달하고, 두 번째 인자에 기존 함수에 전달할 인자를 배열로 전달한다.
call()
, apply()
는 두 번째 인자부분을 어떻게 집어넣냐만 다를 뿐, 나타내는 결과는 동일하다.
명시적 바인딩을 래핑 함수로 덮는 경우, 다시 명시적 바인딩을 한다 하더라도 this의 참조가 변경되지 않게 된다. 이를 하드 바인딩이라 하는데, 이러한 하드 바인딩을 사용하여 다른 함수에서 this가 변경되는 일을 미연에 방지하도록 했다. ES5 에서 부터는 하드 바인딩을 위한 자바스크립트 내장 함수로 bind()
를 제공하였다.
function foo() {
console.log(this.name);
}
var person = {
name: '철수'
};
// 하드 바인딩
var bar = function () {
foo.call(person);
};
bar(); // 철수
setTimeout(bar, 1000); // 철수
bar.call(window); // 철수, foo에는 명시적 바인딩이 적용되지 않는다.
new 키워드와 함께 호출하는 경우에 해당 함수에서는 다음과 같은 과정이 일어난다. (생성자 함수 호출과정)
이 때, this는 새롭게 생성된 객체를 바인딩한다.
function foo() {
this.name = '철수';
}
const bar = new foo();
console.log(bar.name);
new 바인딩 > 암시적 바인딩 > 명시적 바인딩 > 기본 바인딩
화살표 함수는 일반적인 함수와 여러 차이가 있다. 이는 this도 마찬가지 이다. 일반적인 함수는 위의 4가지 바인딩으로 모두 통용된는데 화살표 함수는 아니다.
화살표 함수에서 this 는 선언된 위치의 상위 환경과 동일한 this 를 가리킨다. (마치 렉시컬 스코프를 통해 자식 스코프에서 부모 스코프를 참조하는 것 처럼 보여 헷갈릴 수 있다. 물론 선언된 바로 위 상위 환경을 나타내기 때문에, 스코프와는 차이가 있다. )
const obj = {
name: 'beuccol',
showNameInSec(sec) {
setTimeout(() => {
console.log(this.name);
}, sec);
},
};
obj.showNameInSec(1000); // beuccol
💥 이러한 화살표 함수의 this를 어휘적 this, 렉시컬 this라고 부른다.
참고
[JS] 알쏭달쏭 자바스크립트 this 바인딩
[Javascript] this 의미, 암시적바인딩 vs 명시적바인딩 (call, apply, bind, new, 화살표함수)
[10분 테코톡] 🥦 브콜의 This
[JAVA]this 키워드 개념 및 구현
[Javascript] this
[JavaScript] 4가지의 바인딩과 화살표 함수