자바스크립트 데이터 타입에는 크게 기본형과 참조형이 있다. 기본적으로 기본형은 불변값이고 참조형은 가변값이다.
변수
는 변경 가능한 데이터가 담길 수 있는 공간이고, 식별자
는 그 변수의 이름을 말한다.
변수를 선언하면 컴퓨터는 우선 메모리의 빈 공간에 식별자를 저장하고, 그 공간에 자동으로 undefined를 할당한다. 이후 그 변수에 기본형 데이터를 할당하려고 하면 별도의 공간에 데이터를 저장하고, 그 공간의 주소를 변수의 값 영역에 할당한다.
참조형 데이터를 할당하고자 할 경우 컴퓨터는 참조형 데이터 내부 프로퍼티들을 위한 변수 영역을 별도로 확보해서 확보된 주소를 변수에 연결하고, 다시 앞서 확보한 변수 영역에 각 프로퍼티의 식별자를 저장하고, 각 데이터를 별도의 공간에 저장해서 그 주소를 식별자들과 매칭시킨다.
이처럼 할당 과정에서 기본형과 차이가 생긴 이유는 참조형 데이터가 여러 개의 프로퍼티를 모은 그룹이기 때문이다.
그리고 이 차이로 인해 참조형 데이터를 가변값으로 여겨야만 하는 상황이 발생한다.
참조형 데이터를 가변값으로 여겨야 하는 상황임에도 이를 불변값으로 사용하는 방법은 내부 프로퍼티들을 일일히 복사(깊은 복사), 라이브러리를 사용하는 방법이 있다.
불변 객체는 최근 자바스크립트 진영에서 가장 중요한 개념 중 하나이다.
없음을 나타내는 값은 두 가지가 있는데, undefined는 어떤 변수에 값이 존재하지 않을 경우를 의미하고 null은 사용자가 명시적으로 없음을 표현하기 위해 대입한 값이다.
본래의 의미에 따라 사용자가 없음을 표현하기 위해 명시적으로 undefined를 대입하는 것은 지양해야 한다.
실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
실행 컨텍스트는 전역 공간에서 자동으로 생성되는 전역 컨텍스트와 eval 및 함수 실행에 의한 컨텍스트 등이 있다.
실행 컨텍스트 객체는 활성화 되는 시점에 variableEnvironment, LexicalEnvironment, ThisBinding의 세 가지 정보를 수집한다.
실행 컨텍스트를 생성할 때는 VariableEnvironment와 LexicalEnvironment가 동일한 내용으로 구성되지만 LexicalEnvironment는 함수 실행 도중에 변경되는 사항이 즉시 반영되는 반면 VariableEnvironment는 초기 상태를 유지한다.
VariableEnvironment와 LexicalEnvironment는 매개변수명, 변수의 식별자, 선언한 함수의 함수명 등을 수집하는 environmentRecord와 바로 직전 컨텍스트의 LexicalEnvironment 정보를 참조하는 outerEnvironmentReference로 구성돼 있다.
호이스팅은 코드 해석을 좀 더 수월하게 하기 위해 environmentRecord의 수집 과정을 추상화한 개념으로, 실행 컨텍스트가 관여하는 코드 집단의 최상단으로 이들을 끌어올린다고 해석한다.
변수 선언과 값 할당이 동시에 이뤄진 문장은 선언부만을 호이스팅하고, 할당 과정은 원래 자리에 남아있게 되는데, 여기서 함수 선언문과 함수 표현식의 차이가 발생한다.
스코프는 변수의 유효범위를 말한다. outerEnvironmentReference는 해당 함수가 선언된 위치의 LexicalEnvironment를 참조한다.
코드 상에서 어떤 변수에 접근하려고 하면 현재 컨텍스트의 LexicalEnvironment를 탐색해서 발견되면 그 값을 반환하고, 발견하지 못 할 경우 다시 outerEnvironmentReference에 담긴 LexicalEnvironment를 탐색하는 과정을 거친다.
전역 컨텍스트의 LexicalEnvironment까지 탐색해도 해당 변수를 찾지 못하면 undefined를 반환한다.
전역 컨텍스트의 LexicalEnvironment에 담긴 변수를 전역변수라 하고, 그 밖의 함수에 의해 생성된 실행 컨텍스트의 변수들은 모두 지역변수이다.
안전한 코드 구성을 위해 가급적 전역변수의 사용은 최소화해야 한다.
this에는 실행 컨텍스트를 활성화하는 당시에 지정된 this가 저장된다.
함수를 호출하는 방법에 따라 그 값이 달라지는데, 지정되지 않은 경우에는 전역 객체가 저장된다.
다음 규칙은 명시적 this 바인딩이 없는 한 늘 성립한다.
명시적 this 바인딩
클로저란 어떤 함수에서 선언한 변수를 참조하는 내부함수를 외부로 전달할 경우, 함수의 실행 컨텍스트가 종료된 후에도 해당 변수가 사라지지 않는 현상이다.
내부함수를 외부로 전달하는 방법에는 함수를 return하는 경우뿐 아니라 콜백으로 전달하는 경우에도 포함된다.
클로저는 그 본질이 메모리를 계속 차지하는 개념이므로 더는 사용하지 않게 된 클로저에 대해서는 메모리를 차지하지 않도록 관리해줄 필요가 있다.
어떤 생성자 함수를 new 연산자와 함께 호출하면 Constructor에서 정의된 내용을 바탕으로 새로운 인스턴스가 생성되는데, 이 인스턴스에는 __proto__
라는, Constructor의 prototype 프로퍼티를 참조하는 프로퍼티가 자동으로 부여된다.
__proto__
는 생략 가능한 속성이라서, 인스턴스는 Constructor.prototype의 메서드를 마치 자신의 메서드인 것처럼 호출할 수 있다.
Constructor.prototype에는 constructor라는 프로퍼티가 있는데, 이는 다시 생성자 함수 자신을 가리킨다.
이 프로퍼티는 인스턴스가 자신의 생성자 함수가 무엇인지를 알고자 할 때 필요한 수단이다.
직각삼각형의 대각선 방향, 즉 __proto__
방향을 계속 찾아가면 최종적으로는 Object.prototype에 당도하게 된다.
이런 식으로 __proto__
안에 다시 __proto__
를 찾아가는 과정을 프로토타입 체이닝이라고 하며, 이 프로토타입 체이닝을 통해 각 프로토타입 메서드를 자신의 것처럼 호출할 수 있다.
이때 접근 방식은 자신으로부터 가장 가까운 대상부터 점차 먼 대상으로 나아가며, 원하는 값을 찾으면 검색은 중단된다.
Object.prototype에는 모든 데이터 타입에서 사용할 수 있는 범용적인 메서드만이 존재하며, 객체 전용 메서드는 여는 데이터 타입과 달리 Object 생성자 함수에 스태틱하게 담겨있다.
자바스크립트는 프로토타입 기반 언어라서 클래스 및 상속 개념은 존재하지 않지만 프로토타입 기반으로 클래스와 비슷하게 동작하게끔 하는 다양한 기법들이 도입돼 왔다.
클래스는 어떤 사물의 공통 속성을 모아 정의한 추상적인 개념이고, 인스턴스는 클래스의 속성을 지니는 구체적인 사례이다.
상위 클래스(superclass)의 조건을 충족하면서 더욱 구체적인 조건이 추가된 것을 하위 클래스(subclass)라고 한다.
클래스의 prototype 내부에 정의된 메서드를 프로토타입 메서드라고 하며, 이들은 인스턴스가 마치 자신의 것처럼 호출할 수 있다.
한편 클래스(생성자 함수)에 직접 정의한 메서드를 스태틱 메서드라고 하며, 이들은 인스턴스가 직접 호출할 수 없고 클래스(생성자 함수)에 의해서만 호출할 수 있다.
클래스 상속을 흉내 내기 위한 3가지 방법
이 세 방법 모두 constructor 프로퍼티가 원래의 생성자 함수를 바라보도록 조정해야 한다.