앞서 포스팅한 1, 2장의 내용은 그간 대충만 알던걸 꼬집어서 명확하게 알아가는 느낌이었다면,
this 에 관한 이번 챕터는 내게는 와닿지는 않는다. 우선 내용을 정리해보면...
다양한 상황에서 this 예측해보기
[규칙] - 명시적 this 바인딩이 없는 한 늘 성립한다.
- 전역 공간에서의 this 는 전역객체를 참조한다.
- 어떤 함수를 메서드로서 호출한 경우 : this 는 메서드 호출 주체를 참조한다.
- 어떤 함수를 함수로서 호출한 경우 : 전역 객체를 참조한다. (메서드의 내부 함수에서도 동일)
- 콜백 함수 내부에서의 this 는 해당 콜백 함수의 제어권을 넘겨받은 함수가 정의한 바에 따르며, 정의하지 않은 경우에는 전역 객체를 참조한다.
- 생성자 함수에서의 this 는 생성될 인스턴스를 참조한다.
[명시적 this 바인딩]
- call, apply 메서드는 this 를 명시적으로 지정하면서 함수 또는 메서드를 호출한다.
- bind 메서드는 this 및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만든다. (반환한다)
- 요소를 순회하면서 콜백함수를 반복 호출하는 내용의 일부 메서드(forEach 등)는 별도의 인자로 this 를 받기도 한다.
--- 제일 이해가 안됐음;
각각에 대해 설명하는 내용 또한 정리해본다.
1. 전역 공간에서의 this 는 전역객체를 참조한다.
1) 전역 공간에서 this = 전역 객체이다.
전역 객체는 전역 컨텍스트를 생성하는 주체이기 때문
--- 어떤 인과관계가 있다는거지? 그냥 그렇게 설계 된거라고 이해하면되나
=> 전역 컨텍스트가 생성될때 this 를 설정해야 하는데, 전역 코드는 특정 객체에 속하지 않기 때문에 유일하게 참조할 수 있는 객체는 전역 객체이다.
▶️자바스크립트 런타임 환경에 따른 전역 객체
(1) 브라우저 -> window
(2) Node.js -> global
* console.log(this) 로 출력해서 확인해볼 수 있다.
this 와 큰 관련은 없지만 ~~ 전역 공간 관련 내용
▶️전역 변수는 전역 객체의 프로퍼티로 할당이 된다. (자바스크립트 엔진에 의해!)
어떤 실행 컨텍스트 내에서는, 각 LexicalEnvironment (이하 L.E) 객체가 식별자 정보를 수집하고 프로퍼티로 저장한다.
전역 컨텍스트의 L.E가 참조하는 대상이 전역 객체 이므로 전역 객체의 프로퍼티에 저장이 된다.
즉 자바스크립트의 모든 변수는 특정 객체의 프로퍼티로서 동작한다. --- 넘 신기 🥸
var a = 1; console.log(a); // 1 --- (A) console.log(window.a); // 1 console.log(this.a); // 1
(A) 출력 결과가 1인 이유 : 변수 a에 접근하려고 할때 스코프 체인에서 a를 검색하다가 가장 마지막에 도달하는 전역 스코프의 LE = 전역 객체에서 해당 프로퍼티 a를 발견해서 이 값을 반환한 것임.
그렇다면 전역공간에서 var 변수 선언하는거랑, window 프로퍼티에 직접 할당하는거랑 똑같겠네?var a = 1; window.b = 2; delete window.a //false (삭제되지 않음) delete a //false delete window.b //true (삭제됨) delete b // true
차이점
1) 삭제 명령은 다르게 동작한다.
변수 선언 -> 프로퍼티 할당하면서 configurable 속성(변경 및 삭제 가능성)을 false로 하여 의도치 않게 삭제되는것을 방지하는 장치 (일 것)
2) 호이스팅 여부에서 차이
프로퍼티로 할당 시 호이스팅 발생하지 않는다. 따라서 할당 전에 접근하면 ReferenceError 발생함.
= 단순히 전역 객체의 속성을 추가하는 것이므로 LE가 관리하지 않는다.
2. & 3. 함수 호출 방식에 따라 this가 달라진다 : 메서드 or 함수
어떤 함수를 메서드로서 호출한 경우 : this 는 메서드 호출 주체를 참조한다.
어떤 함수를 함수로서 호출한 경우 : 전역 객체를 참조한다. (메서드의 내부 함수에서도 동일)
함수 vs. 메서드 => 차이는 독립성!
- 함수 : 그 자체로 독립적인 기능 수행
- 메서드 : 자신을 호출한 대상 객체에 관한 동작을 수행
=> 각 상황마다 this 에 다른 값을 부여함
(객체의 프로퍼티에 할당된 함수는 메서드? .... 호출까지 되어야 메서드로서 동작한다. 그렇지않으면 함수로서 동작함)
함수가 호출되고 실행 컨텍스트가 생성될때 this 가 결정된다.
(실행 컨텍스트 생성 시 ① 식별자 정보 수집(호이스팅) ② 스코프 체인 정보 수집 ③ this 바인딩 )
-> 전역 컨텍스트에서 실행되면 this 는 전역 객체
여기서 내가 헷갈린건~~
그렇다면 어떤 함수 A 컨텍스트에서 함수B가 선언되고 실행되면 함수 B의 this 는 함수 A인가?
=> 아니다. 함수는 호출된 방식에 따라 this 가 결정된다. => B의 this 는 window
즉 전역 컨텍스트를 제외하고는, 오로지 호출된 방식(함수냐 메서드냐)에 따라 this 가 결정된다는 것이다.
메서드
var func = function (x) {
console.log(this, x);
}
func(1) // window, 1 --- 함수로서 호출
var obj = {
method: func
}
obj.method(2); // {method: f}, 2 --- 메서드로서 호출 -> 호출한 객체를 this 로 참조
점 표기법이든, 대괄호 표기법이든 어떤 함수를 호출할때 그 함수 이름(프로퍼티 이름) 앞에 객체가 명시 되어 있다면 메서드로서 호출한 것이다. => 함수의 호출 주체인 객체가 this 가 된다.
마지막 점 (또는 대괄호) 앞의 객체 = this
var obj = {
method: function () {console.log(this)},
inner : {
methodB : function (){console.log(this)}
}
obj.methodA(); // {method: f, inner : {...}} === obj
obj['methodB'](); // {method: f, inner : {...}} === obj
obj.inner.methodB(); // {methodB : f} === obj.inner
함수
함수로서 호출할때는 호출 주체가 명시되지 않음
=> 실행컨텍스트를 활성화할때 this가 지정되지 않으면 this 는 전역 객체를 바라본다.
var obj1 = {
outer : function (){
console.log(this); // -----(1)
var innerFunc = function (){
console.log(this); // --------(2)(3)
}
innerFunc();
var obj2 = {
innerMethod : innerFunc
};
obj2.innerMethod();
}
}
obj1.outer();
▼ 예제 코드 따라가면서 순서대로 적어보기
전역 컨텍스트 생성
obj1 객체 선언 및 초기화
obj1.outer() 실행
outer 컨텍스트 생성? ㅇ -> 호이스팅, 스코프 체인 정보 수집, this 바인딩
(1) this 출력 : 메서드로서 실행되었으니 {outer: f } === obj1
outer 프로퍼티에 익명함수 선언
익명함수 내부에서 innerFunc 선언 (호이스팅)
innerFunc 호출 (실행)
innerFunc 컨텍스트 생성 -> 호이스팅, 스코프 체인 정보 수집, this 바인딩
(2) this 출력 : 함수로서 호출됐으므로 window
innerFunc 컨텍스트 제거
obj2.innerMethod 호출 = innerFunc
obj2.innerMethod 컨텍스트 생성
(3) this 출력 메서드로서 호출되었으니 {innerMethod} === obj2
innerFunc 컨텍스트 제거
this 바인딩에 관해서는, 함수를 실행하는 당시의 주변 환경 (메서드 내부인지, 함수 내부인지) 등은 중요하지 않고
오직 함수를 호출하는 주체에 의해 결정된다.
즉.. 내부 함수가 상위 스코프의 this를 자동으로 상속하지 않음.
----> 개발자가 this 가 바인딩 되어있을거라고 기대하는 바와 다르다..
=> ES5 기준, 내부 함수에 this 를 상속할 자체적인 방법은 없지만 우회하는 방법이 있다.
상위 스코프의 this 를 저장해서 메서드 내부 함수에서 활용
self 등의 변수에 this 를 저장
var obj1 = {
outer : function (){
console.log(this); // -----(1) {outer: f} === obj1
var innerFunc1 = function (){
console.log(this); // --------(2) Window
}
innerFunc1();
var self = this;
var innerFunc2 = function (){
console.log(self); // --------(3) {outer: f} === obj1
}
innerFunc2();
}
}
obj1.outer();
ES6에서 화살표 함수 등장 : 함수 내부에서 this 가 전역 객체를 바라보는 문제를 보완하기 위해 등장
화살표 함수는 실행 컨텍스트를 생성할때 this 를 바인딩하지 않는다!
=> 상위 스코프의 this 를 그대로 활용
4. 콜백 함수 호출 시, 그 함수 내부에서의 this : 상황에 따라 다르다?
콜백함수란: 함수 A의 제어권을 다른 함수 or 메서드 B 에게 넘겨주는 경우
콜백함수의 this 는? 기본적으로는 함수니까 전역 객체를 참조
그러나 제어권을 갖는 함수 B에서 별도의 this 를 지정하면 그 대상을 참조
setTimeout(function (){console.log(this); }, 300); --- (1)
[1,2,3].forEach(function (x) {
console.log(this, x)}); --- (2)
document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a')
.addEventListner('click', function (e){
console.log(this, e); --- (3)
})
(1) 전역객체
(2) 전역객체
(3) 버튼 엘리먼트
.addEventListner는 콜백함수를 호출할때 자신의 this 를 상속하도록 정의함
=> 점 (.) 앞부분이 this가 됨
5. 생성자 함수 내부에서의 this
생성자 함수 : class
공통된 성질의 객체를 생성 : instance
자바스크립트에서 함수는 생성자 역할도 함께 한다.
new 함수 호출 = 어떤 함수가 생성자로서 동작한다 -> 인스턴스 생성
이 함수 내부의 this 는 곧 만들어지는 인스턴스 자신이다.
생성된 인스턴스의 __proto__ 프로퍼티 : 생성자의 prototype 프로퍼티를 참조
해당 객체 this에 공통 속성을 부여
var Cat = function (name, age){
this.bark = "야옹", // 여기서 this 는 생성될 인스턴스를 가리키며, bark, name, age 속성을 부여
this.name = name,
this.age = age
}
잠만 너무 길다 이거...
명시적 this 바인딩은 다음 포스팅으로!
'개발 공부 일지 > 코어 자바스크립트 (정재남)' 카테고리의 다른 글
[코어 자바스크립트] 클로저 개념 (0) | 2025.03.04 |
---|---|
[코어 자바스크립트] 비동기 제어로 콜백 지옥 탈출하기 (1) | 2025.02.25 |
[코어 자바스크립트] 콜백 함수 (0) | 2025.02.25 |
[코어 자바스크립트] this - 명시적 바인딩 (0) | 2025.02.21 |
[코어 자바스크립트] 데이터 타입 (기본형, 참조형) (0) | 2025.02.18 |