FRONT-END/TIL

[TIL] 230126 This, Closure

서근 2023. 1. 26. 16:47
반응형

This

글로벌 컨텍스트의 This에는 두 가지 가 있다.

- 브라우저 에서의 This : Windws

- 노드 환경에서의 This: 모듈

 

노드 This 

노드에서의 This는 모듈을 뜻한다.

 

노드에서 consloe.logthis를 찍으면 빈 객체가 나온다 { } 

console.log(this);

이 모듈을 Exports 해주면 this에는 export 된 { key: value }가 들어간다.

const x = 0;
module.exports = x;

console.log(this)

그리고 globalThis를 호출해서 확인하면, 노드에서 사용 가능한 전역 객체가 나온다,

console.log(globalThis);

전역 객체에 '점'을 붙이면 사용 가능한 전역 객체가 나오는데, 이 globalThis를 생략하고 사용 가능하다.

브라우저 This

브라우저에서의 ThisWindow를 뜻한다.

console.log(this); // window에 들어있는 다양한 window 객체 API
console.log(this.setTimeout); // window 안에 있는 setTimeout
console.log(setTimeout); // window에서 this 생략 가능
console.log(globalThis); // window에서 globalThis는 동일하게 window 객체가 나타남

함수 내부에서의 This

 함수 내부에서 this에 접근하면 globalThis 글로벌 객체에 접근하게 된다.

function someThis() {
    console.log(this);
}
someThis();
var object1 = {
    outer: function () {
        console.log(this); 
        var innerFunc = function () {
            console.log(this);
        };
        innerFunc(); // 2. window를 출력

        var object2 = {
            innerMethod: innerFunc,
        };
        object2.innerMethod(); // 3. object2를 출력
    },
};
object1.outer(); // 1. object1을 출력

만약, 엄격 모드('use strict') 에서는 Undefined가 출력된다. 왜냐하면 함수란 스코프 내부에서 this에 대한 디폴트 바인딩 정보가 없기 때문이다.

메서드 내부에서의 This

this에는 호출한 주체에 대한 정보가 담기는데, 어떤 함수를 메서드로서 호출을 하게 되면 호출의 주체는 함수명(프로퍼티명) 앞의 객체이다.

var object = {
    methodA: function () {
        console.log(this);
    },
    inner: {
        methodB: function () { console.log(this) }
    }
};

object.methodA(); // object 출력
object.inner.methodB(); // object.inner 출력

 콜백 호출 시 그 함수 내부에서의 This

콜백 함수 내에서 this를 호출하게 되면 전역 객체를 가리키게 된다.

setTimeout(function () { // (1)
    console.log(this);
}, 300);

[1, 2, 3, 4, 5].forEach(function (x) { // (2)
    console.log(this, x);
});

document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener('click', function (e) { // (3)
    console.log(this, e); // (4)
});
  1. 전역객체가 출력 ➜
  2. 전역 객체와 배열이 각 5회 출력 ➜
  3. addEventListener는 지정한 HTML 엘리먼트에 'click' 이벤트가 발생할 때마다, 그 이벤트 정보를 콜백 함수의 첫 번째 인자로 삼아 함수를 실행. ➜
  4. 버튼을 클릭 시, 지정한 엘리먼트와 클릭 이벤트에 관한 정보가 담긴 객체가 출력

생성자 내부에서의 This

JS에서는 new명령어와 함께 함수를 호출하게 되면 해당 함수가 생성자로서 동작하게 된다.

 

이때 내부에서의 this는 곧 새로 만들 구체적인 인스턴스 자신이 된다.

var Cat = function (name, age) {
    this.bark = '야옹';
    this.name = name;
    this.age = age;
};

var choco = new Cat('초코', 7);
var nabi = new Cat('나비', 5);
console.log(choco, nabi);

/*
Cat { bark: '야옹', name: '초코', age: 7 } 
Cat { bark: '야옹', name: '나비', age: 5 }
*/

명시적 바인딩

개발자가 명시적으로 this를 바인딩할 수 있는 방법은 callapplybind 메서드 등을 사용해, 명시적으로 this를 지정하면서 함수 또는 메서드를 호출할 수 있다.

화살표 함수

화살표 함수는 특별한 this 바인딩을 가진다. 이 화살표 함수는 function을 줄인 것처럼 보이지만, 여러 부분에서 일반 함수와 다른 점을 가지는데,  this와 관련된 점은 '화살표 함수에는this가 존재하지 않는다.'이다. 실행 콘텍스트 생성 시 this를 바인딩하는 과정이 제외되었다.

 

접근하고자 하면 스코프체인상 가장 가까운 this에 접근하게 된다.

var object = {
    outer: function () {
        console.log(this);
        var innerFunc = () => {
            console.log(this);
        };
        innerFunc(); // { outer: [Function: outer] }
    },
};
object.outer(); // { outer: [Function: outer] }

클로져

클로저는 특정한 두가지의 조합인데, 함수와 그 외부를 둘러싸고있는 렉시컬환경의 조합이다. 즉, 클로저란 내부 함수에서 외부 함수에 있는 상태에 접근할 수 있는 권한을 주고 있는 것이다.

function outer() {
	const x = 0;
		function inner() {
			x
		}
		inner();
}
inner()

여기까지가 일반적인 실행 컨텍스트 스택

outer 함수에서 inner 함수를 선언하고 그 inner함수를 return 했다. 즉, inner라는 함수의 참조 값을 return 하게 되고, 전역 스코프에서 inner라는 변수를 만들어 outer 함수를 할당했다. 이 inner라는 변수는 outer 함수의 블록 스코프인 inner 함수를 가리키는 것

 

순서

앱실행 ➜ 전역 스코프 렉시컬 환경이 들어옴 ➜ outer 선언(렉시컬 환경 만들어짐) ➜ outer 호출 ➜ 실행 컨텍스트에 쌓임 ➜ inner 선언 (렉시컬 환경 만들어짐) ➜ inner을 반환(컨텍스트에 쌓이지 않음) ➜ outer 스코프가 끝나면 실행 컨텍스트 스택에서는 빠지지만, inner 렉시컬 환경에서는 return 되었기 때문에 스코프 체인에는 그대로 남아있다.


참고 문헌 REFERENCE

자바스크립트 - This, 콜백, 클로저, 이현동님

코어자바스크립트, 정재만 지음
자바스크립트-클로저, 이한결님
모던 JavaScript 튜토리얼 - 화살표 함수