안녕하세요!
정말 오랜만에 작성하는 포스팅인 것 같습니다.
최근 바쁜 일도 생기고 이런저런 개인 사정 때문에 블로그 관리에 소홀했던 것 같습니다..
여름 시즌까지 꽤 바쁠 것 같은데 시간 쪼개서 글 자주 업로드해보도록 노력하겠습니다 :)
이번 포스팅에서는 자바스크립트의 self에 대해 알아보려고 합니다.
this는 많이 접해보셨을텐데 self는 보신 분들도 있을 테고 이번이 처음이신 분들도 있을 수 있습니다.
그렇게 널리 사용되는 친구는 아니기 때문인데요..!
이번 포스팅을 통해 self가 무엇인지 한 번 알아보도록 합시다.
1. self란 무엇인가?
w3c의 문서를 확인해보니, self는 읽기 전용 속성이며 WindowProxy로서의 window 자신을 반환합니다.
라고 합니다.
처음 접한 self인데 설명까지 꽤 난해합니다.
WindowProxy는 또 무엇일까요?
WindowProxy는 브라우징 컨텍스트와 관련되어있는 주제이므로 이후
다른 포스팅에서 자세히 알아보도록 하고 지금은 단순히 전역 객체인 window객체 자체를 의미한다고 보면 되겠습니다.
self는 결국 전역 객체인 window를 반환하는 속성일 뿐입니다.
self가 this와 같은 키워드라고 오해할 수도 있는데 self는 엄연한 속성의 이름일 뿐입니다. (변수명과 같이)
스크린샷으로 캡처한 문서의 이후 내용은 아래와 같습니다.
By using self, you can refer to the global scope in a way that will work not only in a window context (self will resolve to window.self) but also in a worker context (self will then resolve to WorkerGlobalScope.self)
음.. 정말 길고 복잡해 보입니다.
간략히 요약하자면, 윈도우 컨텍스트가 아닌 컨텍스트(웹 워커)에서도 self 를 통해 전역 스코프 객체에 접근할 수 있다고 합니다.
이 내용은 2번째 섹션에서 알아보도록 하고, 먼저 window 컨텍스트에서의 self를 알아봅시다.
아래와 같은 코드를 실행시켜보면 true 값이 로그에 남습니다.
사실 self는 window.self와 동일한 코드입니다.
전역 객체인 window를 생략해도 self가 컨텍스트의 스코프 내에 존재하지 않는다면,
전역 스코프인 window를 탐색하여 접근하기 때문입니다.
(이러한 특성을 스코프 체이닝이라고 합니다)
설명이 다소 어려울 수 있는데 아래의 코드를 보면 이해하기 쉽습니다.
전역에 선언하는 변수는 window.변수이름 = 값; 과 동일합니다.
전역에 선언한 변수 myVariable에 접근하기 위해선 window.myVariable로 접근하는 방법도 있지만,
myVariable 단독으로 표기할 경우에도 스코프(+ 스코프 체이닝)가 있기 때문에 접근할 수 있습니다.
위 코드를 절차적으로 정리해보자면 아래와 같습니다.
- myFunc 함수 호출
- myVariable 변수에 접근 시도
- myFunc 컨텍스트의 스코프에 myVariable이 존재하는지 확인
- 존재하지 않기 때문에 상위 스코프(전역 스코프)에서 myVariable이 존재하는지 확인
- 전역에 존재하기 때문에 전역의 myVariable 값을 참조하여 콘솔에 로그 남기기
만약 전역에도 존재하지 않아 참조가 불가능할 경우 ReferenceError가 발생합니다.
이처럼 self의 경우에도 window.self 대신 self와 같이 단독으로 사용할 수 있습니다.
2. 다른 컨텍스트에서의 self
앞서 살펴본 코드는 모두 전역 객체가 window인 환경에서의 self를 알아보았습니다.
self는 window 컨텍스트가 아닌 웹 워커(Web Worker) 컨텍스트에서 주로 활용합니다.
웹 워커는 전용 워커, 공유 워커, 서비스 워커 등의 다양한 종류가 존재합니다.
웹 워커는 일반적인 스크립트와 같이 동작하지 않고, 브라우저의 별도 스레드에서 작동하는 스크립트입니다.
자바스크립트는 싱글 스레드 기반으로 동작하기 때문에 브라우저의 다른 스레드에서 동작하는 워커를 활용하여
많은 비용이 필요한 작업을 효과적으로 처리하거나 담당하는 특수한 기능을 수행할 수 있습니다.
웹 워커에 대한 자세한 내용은 아래 링크에서 확인하실 수 있습니다.
https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API
Web Workers API
Web Worker는 script 실행을 메인 쓰레드가 아니라 백그라운드 쓰레드에서 실행할 수 있도록 해주는 기술 입니다. 이 기술을 통해 무거운 작업을 분리된 쓰레드에서 처리할 수 있으며, 이를 통해 메인 쓰레드(일반적으로 UI 쓰레드)는 멈춤, 속도저하 없이 동작할 수 있게 됩니다.
developer.mozilla.org
self는 이러한 워커의 컨텍스트에서도 동일하게 사용할 수 있습니다.
임시로 간단한 워커 스크립트를 만들고, 새로운 워커를 생성해보았습니다.
각각의 컨텍스트에서 self를 로그에 기록하도록 코드를 작성했는데 어떤 결과가 나올까요?
self의 값이 다르게 나왔습니다.
self의 값이 하나는 window,
다른 하나는 DedicatedWorkerGlobalScope로 기록된 모습입니다.
문서의 내용대로 window 컨텍스트에서는 window 객체가,
웹 워커의 컨텍스트에서는 DedicatedWorkerGlobalScope를 참조하는 모습을 확인할 수 있었습니다.
문서 상에는 WorkerGlobalScope라고 정의되어있었으나 앞에 Dedicated라는 수식어가 붙은 전역 객체가 콘솔 기록에 남았습니다.
이는 워커 유형에 따라 전역 객체가 조금씩 다르기 때문입니다.
위와 같은 전용 워커는 DedicatedWorkerGlobalScope,
공유 워커는 SharedWorkerGlobalScope,
서비스 워커는 ServiceWorkerGlobalScope
모두 WorkerGlobalScope의 범주에 속합니다.
앞서 self는 워커 컨텍스트에서 자주 사용한다고 언급했었는데요, 아래와 같은 상황을 예로 들어 설명하겠습니다.
웹 워커 내에서 이벤트를 등록할 때 전역 객체의 addEventListener를 사용합니다.
전역 객체에 포함되어있는 메소드이기 때문에 참조를 생략하고 바로 사용해도 동작하며 기능상으로 문제는 없습니다.
다만 그냥 눈으로 봤을 때 어디에, 어떤 대상에 이벤트를 등록하는지 애매해 보이지 않나요?
self 키워드를 사용하여 명시적으로 해당 웹 워커에 이벤트를 등록하겠다는 표현방식으로써 자주 사용합니다.
비록 둘 다 동일한 동작을 수행하고 아무런 문제가 없을지라도 말이죠
3. 다른 상황에서의 self
개발되는 코드를 보면 종종 아래 코드의 1번과 같은 self 가 목격되기도 합니다.
이는 this를 임시로 저장하기 위한 이름이 self인 단순 변수일 뿐 앞서 설명했던 self와는 아무런 관계가 없습니다.
단지 함수의 this 바인딩 문제를 피하기 위한 일종의 트릭 중 하나이기 때문이죠
이벤트 핸들러 함수에서 외부의 this를 참조하고 싶다면 2번 코드와 같이 this를 직접 바인딩하여 사용할 수도 있습니다.
구현하고자 하는 기능이나 방법이 다양하게 존재하니 여기까지 이야기하도록 하겠습니다.
마지막으로 정리하자면 아래와 같이 한 줄로 정리할 수 있을 것 같습니다.
self는 스크립트가 실행되는 컨텍스트의 전역 객체를 반환하는 속성이다.
window에선 window, 워커에선 WorkerGlobalScope,
결국 코드가 실행되는 위치에서의 전역 객체를 참조하기 위한 속성이라고 볼 수 있습니다.
오탈자, 내용 오류 등 발견되는 문제는 댓글로 알려주세요!
부족한 글 읽어주셔서 대단히 감사합니다 :)
참고 자료
https://docs.w3cub.com/dom/window/self/
https://docs.w3cub.com/dom/workerglobalscope/self/
https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Inner_and_outer_windows
https://html.spec.whatwg.org/multipage/window-object.html#windowproxy