📜 서론
먼저 이 글은 개발자 임성묵님의 자바스크립트는 왜 프로토타입을 선택했을까라는 글을 내 나름대로 요약 후 재구성한 글임을 밝힌다.
자바스크립트는 왜 프로토타입을 선택했을까
프로토타입으로 검색하면 으레 나오는 서두처럼 저 또한 자바스크립트를 처음 접했을 때 가장 당황스러웠던 게 프로토타입이었습니다.
medium.com
Javascript는 왜 클래스가 아닌 프로토타입을 사용하는가? 호이스팅 this
와 같이 Javascript 특유의 처리 기법은 왜 존재하는 것인가?
이 글은 이러한 의문에 대해 근본적인 접근을 하는 내용이다.
🏛️ 고전 서양 철학과 클래스 기반 OOP
실제로 구체적으로 존재하는 사물이 있다면 반드시 그것의 본질이 존재한다. — 플라톤
이러한 플라톤의 주장에 따라 서양 철학은 <이데아> - <프랙티스>
의 이분법적 세계관을 갖는다. 이 사고방식을 기반으로 하는 언어가 클래스 기반 객체지향 프로그래밍 언어다. 클래스를 통해 본질을 규정하고, 그걸 인스턴스화 하여 구체적인 형태로 존재하게 만드는 시스템인 것이다.
그의 제자인 아리스토텔레스는 스승의 이데아 이론을 분류(Classification)라는 개념으로 정립을 한다. 이는 클래스 기반 OOP의 일반화(Generalization) 와 대응되는 개념이다.
💡 비트겐슈타인과 프로토타입
공유 속성의 관점에서 정의하기 어려운 개념이 있다(사실상 올바른 분류란 없다) — 비트겐슈타인
19세기의 철학자 비트겐슈타인은 아리스토텔레스의 분류 개념에 대해 위와 같이 반박했다. 실제로도 '게임', '예술'과 같이 공통 속성을 정의할 수 없는 개념들이 존재한다.
그렇다면 분류의 대안에 대한 비트겐슈타인의 대답은 무엇이었을까?
📌 의미사용이론(the use theory of meaning)
표현은 삶의 흐름 속에서만 의미를 갖는다 — 비트겐슈타인
이 이론의 핵심은 맥락(Context) 이다. 즉, 표현, 또는 단어의 본질이란 것은 고정적인 상태로 존재하지 않으며, 상황과 맥락에 의해 결정된다는 것이다.
📌 가족 유사성
의미사용이론과 함께 비트겐슈타인은 가족 유사성이란 이론을 주장한다. 인간은 실제 대상을 분류할 때 공통 속성이 아닌 전형적인 특징을 통해 분류한다는 이론이다.
📌 Rosch 의 프로토타입 이론
위에서 설명한 비트겐슈타인의 이론들은 1970년경 철학자 Eleanor Rosch에 의해 프로토타입 이론으로 정리된다. 객체는 '정의'가 아닌 프로토타입, 즉 원형을 기준으로 비교하여 얼마나 비슷하냐에 따라 범주화를 한다는 이론이다. 또한 그 원형이란, 실존하는 것들 중 가장 좋은 본보기가 선택된다. 또한 맥락(Context)에 따라 범주, 즉 의미가 달라질 수 있다.
🧩 프로토타입 기반 OOP
프로토타입은 자바스크립트에서 상속을 지원하기 위한 방법이다.
위의 프로토타입 이론의 철학은 그대로 프로토타입 기반 객체지향 프로그래밍을 통해 구현되었다. 클래스 기반의 OOP와 구별되는 특징은 다음과 같다.
- 인스턴스 수준에서 메소드와 변수 추가 가능
- 객체 생성은 일반적으로 복사를 통해 이루어짐
- 확장 방식은 위임을 통해 이뤄진다.
- 어휘, 쓰임새는 맥락에 의해 평가된다.
- 실행 컨텍스트, 스코프, 클로져,
this
, 호이스팅 등의 개념들이 이것에 기반한다.
- 실행 컨텍스트, 스코프, 클로져,
📌 어휘적 범위(lexical scope)
앞서 말했듯이 의미사용이론에서 단어의 의미는 맥락에 의해 결정된다. 프로토타입 이론에 기반하여 만들어진 Javascript에서도 마찬가지로 단어, 즉 변수의 쓰임새(의미)는 어휘적인(Lexical) 실행 문맥(Execution Context) 에 의해 결정된다.
Javascript의 호이스팅은 이런 개념이 반영된 결과라고 볼 수 있다. 따라서 "호이스팅이란 무엇인가?"에 대한 가장 적합한 대답은 다음과 같다고 할 수 있다.
실행 컨텍스트 생성 시 렉시컬 스코프 내의 선언이 끌어올려 지는 것
📌 this
Javascript의 골치 아픈 개념들 중 하나인 this
도 의미사용이론을 적용하여 생각한다면 무엇을 가리키는 지 쉽게 파악할 수 있다. 의미사용이론에 의하면 단어의 쓰임새가 곧 의미다. 즉, 단어가 어디서 발화(invoked)되느냐에 따라 의미가 달라진다는 것이다. Javascript에서도 마찬가지로, this
가 정의된 함수가 어떻게 발화되느냐에 따라 this
가 가리키는 값이 달라진다.
이러한 기준에 따라 this
는 다음과 같이 결정된다.
- 전역 실행 컨텍스트에서
this
는 전역 객체를 가리킨다(브라우저에서는window
, Node.js에서는global
). - 함수가 객체의 메소드로 호출될 때, 함수의
this
는 그 메소드를 호출한 객체를 가리킨다. - 단독으로 호출된 함수의 경우, 함수의
this
는 전역 객체를 가리킨다. - 생성자 함수에서
this
는 새로 생성될 인스턴스를 가리킨다. - 화살표 함수의
this
는 함수가 생성될 때의this
값을 가진다.
쉽게 말하자면 해당 함수를 메소드로 사용하는 객체가 곧 호출된 함수의 this
가 가리키는 대상인 것이다.
아래의 예시 코드들을 통해 살펴보자.
예시 1
this-1
this-1. GitHub Gist: instantly share code, notes, and snippets.
gist.github.com
var someValue = "hello";
function outerFunc() {
console.log(this.someValue);
this.innerFunc();
}
const obj = {
someValue: "world",
outerFunc,
innerFunc: function () {
console.log("innerFunc's this : ", this);
},
};
obj.outerFunc(); // 1번 케이스
outerFunc(); // 2번 케이스
1번 케이스
obj
의 메소드 함수인 outerFunc
의 this
는 앞에서 말한 내용에 따르면 outerFunc
를 발화한 obj
를 가리킨다. 마찬가지로 obj
의 메소드 함수인 innerFunc
의 this
또한 obj
를 가리킨다.
2번 케이스
단독으로 실행되는 outerFunc
의 this
는 글로벌 객체를 가리킨다. 하지만 글로벌 객체에는 innerFunc
이 선언되지 않았기 때문에 이 부분에서 에러가 발생하게 된다.
따라서 위 코드의 결과는 다음과 같다.
world
innerFinc's this : <obj에 대한 정보>
hello
<에러 발생>
예시 2
this-timeout
this-timeout. GitHub Gist: instantly share code, notes, and snippets.
gist.github.com
var value = 1;
var obj = {
value: 100,
foo: function () {
setTimeout(function () {
console.log("callback's this: ", this); // 1번 케이스
console.log("callback's this.value: ", this.value); // 2번 케이스
function bar() {
console.log("bar's this: ", this); // 3번 케이스
}
bar();
}, 1000);
},
};
obj.foo();
이 코드의 경우 실행 환경 별로 setTimeout
의 구현 방식 때문에 출력 결과도 달라진다.
1, 2번 케이스
- 브라우저에서 실행할 경우: 어떤 객체의 메소드로 실행하는 것이 아니기 때문에
setTimeout
의 콜백 함수 내의this
는 전역 객체를 가리킨다. - Node.js에서 실행할 경우: Node.js에서는 콜백 함수의
this
를Timeout
라는 Node.js 고유의 객체에 바인딩하여setTimeout
함수를 구현 한다. 따라서 콜백 함수의this
는Timeout
을 가리킨다.Timeout
에는 당연히value
란 값이 없으므로this.value
의 값은undefined
가 될 것이다.
3번 케이스
콜백 함수 내에서 정의되고 실행되는 함수지만, 어떤 객체의 메서드로서가 아닌 단독으로 실행되는 함수이기 때문에 bar
의 this
는 항상 전역 객체를 가리킨다.
따라서 위 코드의 결과는 다음과 같다.
브라우저
callback's this: <window 객체 정보>
callback's this: 1
bar's this: <window 객체 정보>
Node.js
callback's this: <Timeout 객체 정보>
callback's this: undefined
bar's this: <global 객체 정보>
🤔 그래서 자바스크립트는 왜 프로토타입을 선택했나?
사실 이에 대한 질문은 출처의 원 글에서 제대로 설명하지 않았다. 작성자 분께서 워낙 철학적으로 심오한 내용들을 다루면서 글을 쓰시다 보니 까먹으신 모양이다.
어쨌든 세간에 알려진 사실들과 이 글의 내용을 종합해 생각해 본 결과 다음과 같은 결론을 얻었다.
- 웹 환경에서 가장 중요한 것은 유연성과 확장성이다.
- Javascript는 웹에서 실행되는 언어다.
- 즉 Javascript는 태생부터 유연성과 확장성이 동시에 요구되었을 것이다.
따라서 Javascript의 제작자는 본질은 고정된 것이라 주장하는 고전 서양 철학을 따르는 클래스 기반 OOP가 아닌, 본질은 상황과 맥락에 따라 변한다고 주장하는 프로토타입 이론을 기반의 설계를 택했을 거라 생각된다.
이렇게 비트겐슈타인의 철학은 Javascript를 통해 세상에 구현되었고, 그 결과 수많은 사람들이 웹 상에서 자신의 본질을 자유롭게 정할 수 있게 되었다.
'Javascript' 카테고리의 다른 글
Javascript의 인수 개수 제한 (1) | 2025.02.04 |
---|