출처: [10분 테코톡 - 브콜의 This] https://www.youtube.com/watch?v=7RiMu2DQrb4
일반적으로 객체지향 언어에서의 this는
함수가 속해 있는 객체의 자기 자신과 관련이 깊다.
자바스크립트에서는 자기 자신이라는 말이 상당히 모호한데,
그 이유는 자바스크립트의 함수가 '일급 객체'이기 때문이다.
사용 시에는 단독으로 호출되거나, 특정 객체의 '메서드'로,
또는 동일한 메서드를 다른 객체에서 호출하는 것도 가능하다.
이렇게 자바스크립트에서는
함수가 선언된 이후
어떤 환경에서 어떤 객체에 의해 '호출'될지에 따라 this가 달라진다.
예: 브로콜리가 "내 꺼"라고 외치고 아보카도가 "내 꺼"라고 외쳤을 때
'나'가 가리키는 대상은 각각 다르다.
자바스크립트에서 모든 함수는 this를 가지고 있다.
함수가 호출될 때마다 this가 가리키는 객체가 동적으로 결정되는 것을
this가 그 객체에 '바인딩되었다'고 표현한다.
프로그램이 실행되면
this는 기본적으로 4가지 규칙에 의해 바인딩된다.
함수를 단독 실행할 때의 규칙이다.
브라우저 환경과 Node 환경의 두 가지 경우로 나뉜다.
브라우저 환경에서 기본 바인딩 대상은 window 전역 객체이다.
use strict('엄격 모드')를 사용할 경우
전역 객체가 기본 바인딩 대상에서 제외되어서
this가 바인딩될 객체가 존재하지 않음 => undefined로 대체됨
Node.js 의 기본 바인딩 대상은 global 전역 객체이다.
전역 공간에서는 빈 객체(= module.exports)에 바인딩,
함수 내부 코드에서는 global에 바인딩
this를 포함한 함수가 '객체의 메서드'로 호출이 되는 경우이다.
형태: 호출 시 점(.) 바로 앞에 있는 객체에 this가 바인딩됨
this를 '내가 정한 객체'로 고정하여 바인딩하는 것이다.
암시적 바인딩에서는 '한두 다리만 건너도 this가 소실'되지만 명시적 바인딩에서는 이를 해결한다.
종류에는 call, apply, bind 메서드가 있다.
이미 생성된 클래스에 다른 인수를 넣어 수정해서 사용하고 싶다면
새로운 클래스 객체를 또 만드는 게 아니라 이 메서드들을 사용해서 명시적 바인딩을 하면 된다.
context 뒤에 오는 요소들은 각 메서드의 앞에 오는 함수의 인수들
(context는 필수이고 뒤에 오는 인수들은 option)
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
info(v, w) {
console.log(`x: ${this.x}, y: ${this.y}, v: ${v}, w: ${w}`);
}
}
var point = new Point(10, 20);
point.info(1, 2);
var customPoint = { x: 100, y: 200 };
point.info.call(customPoint, 20, 30);
point.info.apply(customPoint, [2, 3]);
call과 apply는 바인딩을 진행하고 자체적으로 실행까지 한다.
함수를 실행할 때
call은 인수를 하나씩 넘기고, apply는 배열로 넘긴다.
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
info(){
console.log(`x: ${this.x}, y: ${this.y}`);
}
}
var point = new Point(10, 20);
point.info();
point.info.bind({ x: 100, y: 200 })(); // 뒤에 소괄호를 붙여 실행!
bind는 바인딩만 진행하고 자체 실행은 하지 않는다.
따라서 반드시 뒤에 소괄호 ( )를 붙여줘야 실행이 된다.
bind의 인자로는 객체{ } 형태로 수정할 인수값들을 넣어준다.
new 연산자로 호출할 때 this가 바인딩되는 규칙이다.
위의 과정에서 this는 1번째 단계인 '새로운 객체 생성' 단계에서 바인딩된다.
아래는 객체 리터럴로 객체를 생성한 경우이다.
위의 func() 함수는 정해진 객체를 반환하는 생성자로서의 역할을 수행하게 된다.
4가지 규칙 중 여러 개가 동시에 적용되는 상황에서 무엇이 더 우선인지는 다음과 같다.
ES6 문법부터 새롭게 추가된 화살표 함수는
기존의 함수와 다른 방식으로 this를 바인딩한다.
위의 예제는 비동기 함수 setTimeout에 화살표 함수를 사용하고 있다.
화살표 함수는 선언될 당시의 '상위 실행 문맥(context)을 기억'한다.
이러한 특징 때문에 화살표 함수를 사용하면
점(.)으로 호출되는 암시적 바인딩에서도 this에 바인딩된 내용을 잃어버리지 않는다.
이러한 this를 '어휘적(렉시컬) this'라고 부른다.
(= 정적으로 바인딩된 this = '선언'될 때 자신이 바인딩되었던 대상을 기억)