자바스크립트 개념을 핵심적인 부분만 짚어서 친철하게 설명해주는 책입니다.
두꺼운 <모던 자바스크립트 Deep Dive>에 막막함을 느낀다면, <코어 자바스크립트>로 가볍게 입문하기 좋다고 생각됩니다.
자바스크립트 데이터 타입에는 크게 기본형과 참조형이 있다.
기본형은 값이 담긴 주솟값을 바로 복제한다.
참조형은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제한다.
모든 데이터는 바이트 단위의 식별자, 더 정확하게는 메모리 주솟값을 통해 서로 구분하고 연결할 수 있다.
변수는 변경 가능한 데이터가 담길 수 있는 공간이고, 식별자는 그 변수의 이름을 말한다.
숫자의 경우 정수형인지 부동소수형인지를 구분하지 않고 64비트, 즉 8바이트의 메모리 공간을 확보한다.
변수에 기본형 데이터를 할당하려고 하면 별도의 공간에 데이터를 저장하고, 그 공간의 주소를 변수의 값 영역에 할당한다.
참조형 데이터를 할당하고자 할 경우 컴퓨터는 참조형 데이터 내부 프로퍼티들을 위한 변수 영역을 별도로 확보해서 확보된 주소를 변수에 연결하고, 다시 앞서 확보한 변수 영역에 각 프로퍼티의 식별자를 저장하고, 각 데이터를 별도의 공간에 저장해서 그 주소를 식별자들과 매칭시킨다.
변수와 데이터를 별도의 공간에 나누어 저장하는 이유는 효율적으로 데이터의 변환을 처리하기 위해서다.
이미 만들어놓은 값이 있으면 그 주소를 재활용한다. 아니면 새로운 메모리 공간을 할당해서 값을 저장하고 그 주소를 갖다 쓴다.
어떤 데이터 타입이든 변수에 할당하기 위해서는 주솟값을 복사해야 하기 때문에, 엄밀히 따지면 자바스크립트의 모든 데이터 타입은 참조형 데이터일 수밖에 없다.
다만 기본형은 주솟값을 복사하는 과정이 한 번만 이뤄지고, 참조형은 한 단계를 더 거치게 된다는 차이가 있다.
값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우가 종종 발생한다. 이때 불변 객체가 필요하다.
얕은 복사는 바로 아래 단계의 값만 복사하는 방법이고, 깊은 복사는 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법이다.
어떤 객체를 복사할 때 객체 내부의 모든 값을 복사해서 완전히 새로운 데이터를 만들고자 할 때, 객체의 프로퍼티 중에서 그 값이 기본형 데이터일 경우에는 그대로 복사하면 되지만 참조형 데이터는 다시 그 내부의 프로퍼티들을 복사해야 한다.
자바스크립트에는 '없음'을 나타내는 값이 두 가지가 있다. 바로 undefined와 null이다.
값으로써 어딘가에 할당된 undefined는 실존하는 데이터인 반면, 자바스크립트 엔진이 반환해주는 undefined는 문자 그대로 값이 없음을 나타내는 것이다.
'비어있음'을 명시적으로 나타내고 싶을 때는 undefined가 아닌 null을 쓰면 된다.
실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체다.
자동으로 생성되는 전역공간과 악마로 취급받는 eval을 제외하면 우리가 흔히 실행 컨텍스트를 구성하는 방법은 함수를 실행하는 것뿐이다.
실행 컨텍스트 객체는 활성화되는 시점에 VariableEnvironment, LexicalEnvironment, ThisBinding의 세 가지 정보를 수집한다.
VariableEnvironment에 담기는 내용은 LexicalEnvironment와 같지만 최초 실행 시의 스냅샷을 유지한다는 점이 다르다.
실행 컨텍스트를 생성할 때 VariableEnvironment에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LexicalEnvironment를 만들고, 이후에는 LexicalEnvironment를 주로 활용하게 된다.
VariableEnvironment와 LexicalEnvironment의 내부는 environmentRecord와 outerEnvironmentReference로 구성돼 있다.
LexicalEnvironment는 "현재 컨텍스트의 내부에는 a, b, c와 같은 식별자들이 있고 그 외부 정보는 D를 참조하도록 구성돼 있다."라는, 컨텍스트를 구성하는 환경 정보들을 사전에서 접하는 느낌으로 모아놓은 것이다.
environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.
outerEnvironmentReference는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조한다.
실행 컨텍스트의 thisBinding에는 this로 지정된 객체가 저장된다.
실행 컨텍스트는 활성화 당시에 this가 지정되지 않은 경우 this에는 전역 객체가 저장된다.
그밖에는 함수를 호출하는 방법에 따라 this에 저장되는 대상이 다르다.
this는 함수를 호출할 때 결정된다. 자바스크립트에서 this는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정된다.
this에는 호출한 주체에 대한 정보가 담긴다. 전역 공간에서 this는 전역 객체를 가리킨다.
this 바인딩에 관해서는 함수를 실행하는 당시의 주변 환경(메서드 내부인지, 함수 내부인지 등)은 중요하지 않고, 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지 없는지가 관건인 것이다.
ES6에 새롭게 도입된 화살표 함수는 실행 컨텍스트 생성 시 this를 바인딩하는 과정이 제외됐다.
즉 이 함수 내부에는 this가 아예 없으며, 접근하고자 하면 스코프체인상 가장 가까운 this에 접근하게 된다.
이렇게 하면 별도의 변수로 this를 우회하거나 call/apply/bind를 적용할 필요가 없어 더욱 간결하고 편리하다.
콜백 함수는 제어권과 관련이 깊다.
콜백 함수는 다른 코드(함수 또는 메서드)에게 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수다.
콜백 함수를 위임받은 코드는 자체적인 내부 로직에 의해 이 콜백 함수를 적절한 시점에 실행할 것이다.
콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가진다.
콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수를 호출할 때 인자에 어떤 값들을 어떤 순서로 넘길 것인지에 대한 제어권을 가진다.
콜백 함수도 함수이기 때문에 기본적으로는 this가 전역객체를 참조하지만, 제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조하게 된다.
콜백 함수로 어떤 객체의 메서드를 전달하더라도 그 메서드는 메서드가 아닌 함수로서 호출된다.
메서드를 forEach 함수의 콜백 함수로서 전달했다.
obj를 this로 하는 메서드를 그대로 전달한 것이 아니라, obj.logValues가 가리키는 함수만 전달한 것이다.
별도의 인자로 this를 받는 함수의 경우에는 여기에 원하는 값을 넘겨주면 된다.
전통적으로는 this를 다른 변수에 담아 콜백 함수로 활용할 함수에서는 this 대신 그 변수를 사용하게 하고, 이를 클로저로 만드는 방식이 많이 쓰였다.
전통적인 방식의 아쉬움을 보완하는 방법으로, ES5에서 등장한 bind 메서드를 이용하는 방법이 있다.
콜백 지옥은 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상을 말한다.
비동기적인 코드는 현재 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어간다.
비동기적인 일련의 작업을 동기적으로, 혹은 동기적인 것처럼 보이게끔 처리해주는 장치를 마련하고자 끊임없이 노력해 왔다. ES6에서는 Promise, Generator 등이 도입됐고, ES2017에서는 async/await가 도입됐다.
클로저란 "어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상"이다.
가비지 컬렉터는 어떤 값을 참조하는 변수가 하나라도 있다면 그 값은 수집 대상에 포함시키지 않는다.
"어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상"이란 "외부 함수의 LexicalEnvironment가 가비지 컬렉팅되지 않는 현상"을 말하는 것이다.
클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수를 메모리를 소모하도록 함으로써 발생한다. 그렇다면 그 필요성이 사라진 시점에는 더는 메모리를 소모하지 않게 해주면 된다.
참조 카운트를 0으로 만들면 언젠가 가비지 컬렉터가 수거해갈 것이고, 이때 소모됐던 메모리가 회수된다.
참조 카운트를 0으로 만드는 방법은? 식별자에 참조형이 아닌 기본형 데이터를 할당하면 된다. 보통 null이나 undefined를 할당한다.
클로저를 이용하면 함수 차원에서 public한 값과 private한 값을 구분하는 것이 가능하다.
부분 적용 함수란 n개의 인자를 받는 함수에 미리 m개의 인자만 넘겨 기억시켰다가, 나중에 (n-m)개의 인자를 넘기면 비로소 원래 함수의 실행 결과를 얻을 수 있게끔 하는 함수다.
미리 일부 인자를 넘겨두어 기억하게끔 하고 추후 필요한 시점에 기억했던 인자들까지 함께 실행하게 한다는 개념 자체가 클로저의 정의에 정확히 부합한다.
new 연산자로 Constructor를 호출하면 instance가 만들어지는데, 이 instance의 생략 가능한 프로퍼티인 __proto__는 Constructor의 prototype을 참조한다!
생성자 함수의 프로퍼티인 prototype 객체 내부에는 constructor라는 프로퍼티가 있다. 인스턴스의 __proto__ 객체 내부에도 마찬가지다. 이 프로퍼티는 원래의 생성자 함수(자기 자신)를 참조한다.
__proto__가 생략 가능하기 때문에 인스턴스에서 직접 prototype에 정의된 프로퍼티나 메서드, constructor에 접근할 수 있는 수단이 생겼다.
어떤 데이터의 __proto__ 프로퍼티 내부에 다시 __proto__ 프로퍼티가 연쇄적으로 이어진 것을 프로토타입 체인이라 하고, 이 체인을 따라가며 검색하는 것을 프로토타입 체이닝이라고 한다.
arr 변수는 배열이므로 arr.__proto__는 Array.prototype을 참조하고, Array.prototype은 객체이므로 Array.prototype.__proto는 Object.prototype을 참조할 것이다.
어떤 생성자 함수이든 prototype은 반드시 객체이기 때문에 Object.prototype이 언제나 프로토타입 체인의 최상단에 존재하게 된다.
자바스크립트는 프로토타입 기반 언어라서 '상속' 개념이 존재하지 않는다.
클래스는 하위로 갈수록 상위 클래스의 속성을 상속하면서 더 구체적인 요건이 추가 또는 변경된다.
어떤 클래스의 속성을 지니는 실존하는 개체를 일컬어 인스턴스라고 한다.
엄밀히는 상속이 아닌 프로토타입 체이닝에 의한 참조다.
인스턴스에 상속되는지(인스턴스가 참조하는지) 여부에 따라 스태틱 멤버와 인스턴스 멤버로 나뉜다.
구체적인 인스턴스가 사용할 메서드를 정의한 '틀'의 역할을 담당하는 목적을 가질 때의 클래스는 추상적인 개념이지만, 클래스 자체를 this로 해서 직접 접근해야만 하는 스태틱 메서드를 호출할 때의 클래스는 그 자체가 하나의 개체로서 취급된다.
자바스크립트에서 클래스 상속을 구현했다는 것은 결국 프로토타입 체이닝을 잘 연결한 것으로 이해하면 된다.
ES5까지의 자바스크립트에는 클래스가 없다. ES6에서 클래스가 도입됐지만 역시나 prototype을 기반으로 한 것이다.
하위 클래스로 삼을 생성자 함수의 prototype에 상위 클래스의 인스턴스를 부여하는 것만으로도 기본적인 메서드 상속은 가능하지만, 다양한 문제가 발생할 여지가 있어 구조적으로 안전성이 떨어진다.
static 키워드는 해당 메서드가 static 메서드임을 알리는 내용으로, ES5 체계에서 생성자 함수에 바로 할당하는 메서드와 동일하게 생성자 함수(클래스) 자신만이 호출할 수 있다.
클래스의 prototype 내부에 정의된 메서드를 프로토타입 메서드라고 하며, 이들은 인스턴스가 마치 자신의 것처럼 호출할 수 있다.
Square를 Rectangle 클래스를 상속하는 SubClass로 만들기 위해 class 명령어 뒤에 단순히 'extends Rectangle'이라는 내용을 추가했다. 이것만으로 상속 관계 설정이 끝난다.
전체 요약본 보러가기
원문: https://product.kyobobook.co.kr/detail/S000001766397
남들이 리액트 뭐 할때 코어 자바스크립트의 기초 .. 튼튼함이 중요한거같아요 프론트는.. (제가 프론트는 아니지만 주변 잘하시는분들 보면 다 그렇드라구요)
화이팅입니다!!!