[Day 10] VanillaJS 사전 문제

thru·2023년 6월 14일
1

FEDC-TIL

목록 보기
9/21

What is "this" ?

오늘 공부한 내용 ☁️

VanillaJS 강의를 본격적으로 들어가기 전 사전 문제를 푸는 시간을 가졌다.
사전 문제들은 자바스크립트를 사용하며 함정에 쉽게 빠질 수 있는 케이스들을 모았다고 한다.


새로 알게된 내용 🌱

this

다른 언어에서는 별 생각없이 쓰던 것이다보니 자바스크립트에서도 조금 특이하다라는 소문만 듣고 별로 알아보지 않았다. 이번 기회에 알아보니 참 골때린다.

기본 결정 규칙

this는 실행 컨텍스트가 생성될 때 다른 environment들과 함께 결정된다.
실행 컨텍스트는 함수가 호출될 때 생성되므로 사실상 함수가 실행되는 시점에 결정된다고도 말할 수 있다.

함수의 호출은 두 가지 경우가 존재한다.
함수로서의 호출과 메서드로서의 호출인데 함수와 메서드의 구분은 독립성의 유무로 한다.
객체로부터 .이나 [ ]로 호출된 함수는 메서드로 호출된 것이고 이 때의 this는 이 메서드를 호출한 주체인 객체의 정보를 담는다.

함수로서의 호출의 경우 함수를 호출하는 주체가 없으므로 기본값인 전역 객체가 들어간다.
이는 자바스크립트 설계 상의 오류라고 한다.

전역 객체란 전역 컨텍스트를 생성하는 주체로 실행 환경에 따라 다르다.
브라우저에서는 window이고 Node.js환경에서는 global이다.

다른 언어의 this처럼 주변 환경에 영향을 받는 것이 아닌 호출에 의해서만 결정된다는 것이 자바스크립트 this의 특징이다.

심지어 메서드 안에 있더라도 내부에서의 함수 호출이면 전역 객체를 참조해버린다.

함수 호출을 new와 함께하면 함수가 생성자로 동작한다.
이 때는 만들어지는 객체를 참조하도록 구현되어있다.

콜백 함수의 경우 기본은 함수 호출이기에 전역 객체가 기본값이지만 보통 제어권을 받은 함수가 this가 될 대상을 지정해줄 수 있다.

명시적 바인딩

프로그래머가 this의 대상을 지정해줄 수도 있다. 지정하는 과정을 바인딩이라고 한다.

먼저 callapply 함수의 첫 번째 인자를 통해 바인딩해줄 수 있다.
이 함수들은 this를 첫번째 인자로 변경한 뒤 다른 인자들을 전달해서 함수를 실행한다.

비슷하게 bind도 있는데 얘는 실행은 하지 않고 새로운 함수를 반환한다.

바인딩 외의 용도도 있지만 call과 apply는 ES6 이후로도 계속 쓰는 지는 의문이고
bind는 유용해보이지만 따로 포스팅하는 게 깔끔할 것 같다.

콜백 함수 중에도 this를 내부적으로 지정하지 않고 인자로 받아와서 설정할 수 있는 경우가 있다.

조금 허무한 방법으로는 상위 스코프에서 따로 변수를 만들어 this를 저장하고 아래로 전달해주는 방법이 있다.
ES5에서는 종종 썼다고 한다.

자바스크립트는 원래 그렇게 쓰는 겁니다.아닙니다

화살표 함수

ES6 에서 도입된 화살표 함수는 실행 컨텍스트를 생성하는 과정에서 this의 바인딩이 생략되었다.
따라서 화살표 함수의 내부에서 this를 참조하면 스코프체인에 따라 상위의 this를 접근하게 된다.

그냥 이뻐서 넣은 줄 알았다.


리마인드된 내용 🔨

Hoisting

그냥 var를 위쪽으로 끌어와서 선언한다 정도로만 알고 넘어갔었는데 내부 과정은 약간 다르다.
이는 결과적으론 동일하다보니 사람이 이해하기 쉽게 표현한 것이라고 한다.

실행 컨텍스트가 형성될 때 컨텍스트 내부를 쭉 훑으며 변수의 식별자들을 수집한다.
이는 내부 코드의 실행 이전에 일어나기 때문에 자바스크립트 엔진은 컨텍스트 내부의 변수명을 미리 알 수 있다.
즉, var 선언을 최상단에 끌어온 코드와 Lexical environment 관점에서는 똑같다.

식별자는 함수 또한 가지므로 함수의 선언도 미리 수집한다.
function으로 시작하는 일반적인 함수 선언문은 전체를 끌어올린 것과 동일한 동작을 한다.
그래서 선언 코드 전에는 undefined 값을 갖는 변수와 달리 선언 전에도 함수는 온전히 사용할 수 있다.

이와 달리 변수에 함수를 할당하는 함수 표현문은 변수의 hoisting과 동일하게 변수의 선언문만 끌어올리는 효과가 나타나므로 선언 전에 함수를 사용할 수 없다.

var를 안쓰면 되는 거 아닌가?

놀랍게도 let과 const, class 도 hoisting이 일어난다.
다만 선언 전에 사용하려고 하면 오류를 내보낸다.
이 오류가 나타나는 구간을 Temporal Dead Zone이라고 하는데 변수의 lifecycle과 연관이 있다.

변수의 lifecycle은 선언, 초기화, 할당의 세 단계로 나뉜다.
var의 경우 선언과 초기화가 처음에 같이 이루어져 할당 이전에도 undefined으로 초기화된 값에 접근할 수 있다.
let, const 등의 경우 선언은 마찬가지로 처음에 이루어지지만 초기화가 변수 선언문 시점에 이루어져 이전에 접근하면 Reference Error가 발생한다.
선언과 초기화 단계 사이 구간을 TDZ라고 부르는 것이다.

참고로 함수 선언문은 세 단계가 모두 처음에 이루어진다.


클로저

Day 2 TIL에도 클로저 관련 정리를 했었는데 그 때 사실 Lexical environment에 더 주목을 해서 그런지 이번에 문제 풀 때 클로저와 Lexical을 혼동했다.

그저 빡대가리..

클로저는 자바스크립트의 강력한 특성이기도 하지만 여러 함수형 언어에서 보편적으로 등장한다.
ECMAScript에서도 딱 정해둔 것은 아니다보니 문헌에 따라 다르게 설명하지만 참조한 책에서 정리하기로는 어떤 함수 A가 선언한 변수 a를 참조하는 내부함수 B가 외부로 전달될 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상으로 정의하였다.
다른 문헌에서는 이러한 현상이 일어나는 함수 A 자체를 클로저라고 하는 경우가 있다.
책에 따르면 함수는 그저 클로저라는 현상이 나타나기 위한 조건이라고 한다.

작가의 주관 한 스푼

이는 따로 구현해서 만들어진 기능이 아니라 외부로 전달되면서 참조 카운트가 0이 되지 않아 가비지 컬렉션되지 않았기 때문에 일어난 기능으로 볼 수 있다.
즉, 메모리와 관련된 기능이므로 제거하고 싶다면 참조를 명시적으로 지워주면 된다.

내부함수를 외부로 전달하는 방법은 일반적으로는 return을 통하는 것인데 콜백 함수나 EventListener 로도 가능하다.


느낀점 🎬

문제 풀면서 공부한 기억은 나는데 내용이 기억이 안나서 많이 반성했다. 그래도 참조한 책이 관련 내용들을 다 짚어줘서 참고글을 찾는 노력을 줄일 수 있었다. 더 깊은 활용 방법들도 소개되어 있었는데 책을 다 읽고 글을 쓸 때 넣는 게 나을 것 같아서 뺐다.

그런데 배우면 배울 수록 통통배 위의 항공모함이 생각나는 건 왜일까..


참조


profile
프론트 공부 중

0개의 댓글