자바스크립트를 공부하면서 class와 프로토타입에 대해 다시 공부하며 여러 생각을 정리를 하는 시간을 가져보았다. 이를 위해 다른 언어에서 쓰는 객체지향을 되짚어보고 Js에서 프로토타입이라는 것을 도입하게 되는 원리에 대해서 공부했습니다.
공부를 하면서 컴퓨터 프로그래밍이 철학과 관련성이 있다는 것도 알게 되었고 this, 호이스팅이라는 개념이 필연적으로 나타나는 것을 알 수 있었다.
“호이스팅은 자바스크립트에서 전체 코드를 훑을 때 선언 부를 상단으로 올린다.”
“this 는 기본적으로 global(브라우저에선 window)을 가리키고 실행 컨텍스트마다 달라질 수 있으니 bind 등을 통해 제어해야 한다.”
라는 암기식이 아닌 '왜'인지 알아보기도 하겠습니다.
먼저 객체 지향이 무엇인지 파악하고 JS언어의 디자인 철학을 알아보겠습니다.
1. 객체지향 프로그래밍
현실 세계에 존재하는 객체(Object)를 소프트웨어 세계에서 표현하기 위해 객체의 핵심적인 부분만을 추상화해 모델링 하려고 하는 프로그래밍 패러다임이라고 정의됩니다. 먼저 가장 일반적인 분류 개념을 보겠습니다.
1) 분류(Classification)
분류란 캐념에서 class라는 키워드가 나왔습니다.
- 개체의 속성이 동일한 경우 개체 그룹이 같은 범주에 속한다. 범주는 정의와 구별의 합이다
이는 전통적인 클래스 기반 객체 지향 프로그래밍의 아이디어-일반화(generalization)로 이해됩니다. 여기서 속성은 클래스의 프로퍼티가 되겠죠. 프로퍼티가 유사한 객체가 있다면 일반화 과정을 통해 클래스로 추상화됩니다.
이러한 방식으로 현실 세계의 많은 것들을 분류가 가능합니다. 그리고 이러한 개념을 실제 프로그래밍에서 적용해나가는 것이 일반적인 OOP라고 말할 수 있겠죠.
2) 프로토타입
이러한 분류 개념을 반박하기 위해 나온 개념이 프로토타입이란 개념입니다.
공유 속성의 관점에서 정의하기 어려운 개념이 있다(사실상 올바른 분류란 없다)라는 반박이 존재한 것입니다.
'예술', '게임' 등 같은 단어는 결코 속성으로 규정할 수 없기에 공통 속성이 없을 수 있습니다.
많은 개발자들이 클래스 설계를 하면서 최적의 설계를 하는 것에는 어려움과 시간이 걸린다는 고충이 있습니다.
표현은 삶의 흐름 속에서만 의미를 갖는다 — 비트겐슈타인
라는 말이 있습니다. 이 말은 '사용'에 의해 의미가 결정 된다는 뜻을 말할 수 있습니다.
같은 단어 이지만 문맥을 통해 단어의 의미가 달라지기 때문입니다.
'단어의 의미 보단 맥락(Context)이 중요하다' 이 맥락이 우리 JS에서 실행 Context를 설명할 수 있을 것 같습니다.
다시 돌아와서, 비트겐슈타인은 인간이 현실에서 실제로 대상을 분류할 때 속성(전통적인 분류에서의 기준)이 아닌 가족 유사성을 통해 분류하게 된다고 얘기합니다.
가족이 있을 때 이 가족이 모두 공유하는 공통 속성은 없습니다. 갈색 머리, 안경, 수염, 큰 코가 가족의 전형적인 특징이라고 하더라도 모든 가족 구성원에게 적용되는 공통된 특성(속성)은 없을 수 있습니다. 그런데도 우리는 전형적인 특징을 통해 ‘가족'으로 분류합니다. 이런 분류 방식을 ‘가족 유사성' 에 의한 분류라고 합니다.
이 이론은 프로토타입 이론의 근거가 됩니다. 이러한 이론에서 중요한 것은
- 현실에 존재하는 것 중 가장 좋은 본보기를 원형(prototype)으로 선택한다.
- 문맥(콘텍스트)에 따라 ‘범주’, 즉 ‘의미’가 달라진다.
예를 들어, 동물을 봤을 때, "고양잇과"라는 범주가 있습니다. 당연히 대표 동물인 고양이는 어린아이도 고양이라고 생각하겠죠. 하지만 표범, 사자 이런 것들도 "고양잇과"이라고 생각하기 힘들 수 있죠. 우리는 물론 학습을 통해 인지할 수 있습니다. 이처럼 누구에 따라, 상황에 따라, 범주는 크게 다르기 때문에 범주는 매번 달라지는 것을 적용한 개념이 프로토타입이라고 말할 수 있겠네요.
프로토타입 기반 OOP
프로토타입 기반 OOP 언어의 특징은 다음과 같습니다.
- 개별 객체(instance) 수준에서 메소드와 변수를 추가
- 객체 생성은 일반적으로 복사를 통해 이루어짐
- 확장(extends)은 클래스가 아니라 위임(delegation)
> 현재 객체가 메시지에 반응하지 못할 때 다른 객체로 메시지를 전달할 수 있게 하여 상속의 본질을 지원 - 개별 객체 수준에서 객체를 수정하고 발전시키는 능력은 선험적 분류의 필요성을 줄이고 반복적인 프로그래밍 및 디자인 스타일을 장려
- 프로토타입 프로그래밍은 일반적으로 분류하지 않고 유사성을 활용하도록 선택
- 결과적으로 설계는 맥락에 의해 평가
이런 것들을 보면 클로져, this, 호이스팅 등이 이러한 프로토타입의 'Context'을 위해 있다는 것으로 느껴집니다.
2. 자바스크립트 lexical scope
변수의 의미는 그 어휘적인(Lexical), 실행 문맥(Execution Context)에서의 의미가 된다
자바스크립트는 이러한 개념이 적용되기 때문에, 동일 범위( 실행 컨텍스트 )의 모든 선언을 참고 (호이스팅)해 의미를 정의합니다.
( 호이스팅 : 실행 컨텍스트 생성 시 렉시컬 스코프 내의 선언이 끌어올려지는 것 )
다시 자세히 정리하면,
자바스크립트 엔진은 코드가 로드될 때 실행 컨텍스트를 생성하고 그 안에 선언된 변수, 함수를 실행 컨텍스트 최상단으로 호이스팅 합니다. 이러한 범위를 렉시컬 스코프라 합니다.
let name = 'lee';
init(); // init 실행문맥 생성. 내부 정의(name, displayName) 호이스팅
function init() {
let name = "kim";
function displayName() {
console.log(name); // 현재 실행문맥 내에 정의된게 없으니 outer 로 chain
//let name = 'park'
}
displayName(); // displayName 실행문맥 생성. 내부 정의 호이스팅.
}
해당 코드는 다음과 같은 순서로 진행됩니다.displayName 실행 문맥에서 name을 찾지만 존재하지 않으므로 outer인 init함수 실행문맥에서 찾습니다. 이러한 개념이 스코프 체인, 프로토타입 체인이라고 말할 수 있습니다.
여기서 '어휘'를 변수라고 했을 때, 변수가 문맥 내에서만 의미를 가지는 개념을 잘 숙지하는 것이 중요하다고 생각합니다.
3. 자바스크립트 - this
this는 초심자가 가장 이해하기 힘든 개념이라고 생각합니다. 저도 이 this가 왜 이런 꼴로 만들어 지는지 이해가 안 되고 잘 사용하기 쉽지 않다고 생각하여 arrow Function을 많이 사용했습니다.
this에 대해서 헷갈리는 것들을 적어 보겠습니다.
- this 는 기본적으로 window 다 ( X )
- 이벤트 리스너에서 등록한 콜백의 this는 내부에서 bind 등을 통해 바뀌기 때문에 무엇인지 알 수 없다. ( X )
어떤 단어를 얘기할때, 그 단어가 어떤 문맥에서 얘기되는 것에 따라 의미가 달라집니다. 이 개념을 가지고 프로토 타입을 보면, 프로토타입에서는 받아들이는 주체와 문맥이 가장 중요합니다. 프로그래밍으로 보면 실행하는 '객체'가 중요합니다.
이것이 바로 프로토타입 기반 언어인 자바스크립트에서 this 가 클래스 기반 언어들과 다르게 동작하는 이유라고 말할 수 있습니다.
const value = 1;
function handle() {
const value = 10;
console.log(this.value); // 첫번째 ?, 두번째 ?
}
const obj = {
value:2,
call : handle
}
obj.call() // 2
handle() // undefined
위처럼 객체를 통해 호출된 함수는 내부 this가 무조건 해당 객체를 가르키게 됩니다. 만약 아무것도 지정이 되어 있지 않으면 글로벌 또는 window를 가르키게 됩니다.
추가) let, const는 글로벌 영역에 존재하지 않는다..
예시 하나 더
function handle() {
console.log(this); // 첫번째 ?, 두번째 ?
}
document
.getElementsById('body')[0]
.addEventListener('click', handle); // 첫번째. 호출되었다고 가정.
handle(); // 두번째. 첫번째 이후에 호출되었다고 가정.
다음 코드에서 id가 body인 엘레먼트를 클리하면, 다음과 같은 과정이 있게 됩니다.
- 브라우저에서 body를 클릭하면 엔진이 반응합니다
- 해당 엘리먼트(div)에 등록된 event listener 들을 실행하라는 메시지를 보냅니다
- div 엘리먼트에서 handle 을 발화합니다.
- 이때 handle의 실행 문맥의 this는 발화한 body를 가리킵니다.
이것으로 정리를 마치겠습니다.
이번 공부를 하면서 자바스크립트 언어 디자인 철학을 접하고 생각해볼 수 있었다.
ES6에 들어오면서 클래스를 사용할 수 있지만 억지로 쓸 필요 없이 JS의 디자인 철학을 존중과 이해를 할 필요가 있다고 고 느꼈습니다. 앞으로 개발을 할 때, 이 점을 염두하고 개발을 해나가며 발전해나갈 수 있도록 해야겠다.
'Front-end > Javascript' 카테고리의 다른 글
Promise(2) - 프라미스 체이닝 (0) | 2023.08.02 |
---|---|
Callback (0) | 2023.08.01 |
이벤트 위임 (0) | 2023.07.28 |
이벤트 버블링과 이벤트 캡처링 (0) | 2023.07.26 |
자바스크립트 엔진 (0) | 2023.07.23 |