자바스크립트 엔진

1차 카테고리
JS
2차 카테고리
심화
생성 일시
2025/02/07 16:21
최종 편집 일시
2025/02/11 16:05
발행여부
published

1. 핵심 키워드

엔진 처리는 크게 해석(Context), 실행(Execution)으로 나눌 수 있다.
해석이란 컴파일과 실행할 환경을 설정하는 것이고, 실행이란 해석 단계에서 설정된 환경을 바탕으로 코드를 실행하는 것.
하나의 박스에 발생할 수 있는 것을 모두 집어넣는다면 심플하다. 박스 하나만 가지고 메모리에 올라가면 되기 때문이다. 그러면 이 함수라는 단위를 어떤 묶음으로 가져갈 것인가? → 이것이 실행 컨텍스트(Execution Context)이다.
그런데 왜 묶음으로 가져가야 할까? 식별자 해결(Identifier Resolution)을 위해서다. 함수 이름을 찾는데 같은 실행 컨텍스트면 불러오기 편하다. 만약 필요한 함수가 다른 곳에 위치한다면, 현재 메모리에 실행 컨텍스트가 올라간 상태에서 다른 컨텍스트를 불러와야 한다. → 엔진 부하 증가
이 식별자 해결에서 파생된 단어가 스코프이다. 궁극적으로 무엇을 위해서인가? 식별자 이름들을 어떻게 빨리 찾고 실행할것인가를 위해서이다.
Identifier Resuloution. 식별자 해결을 위해서.
ES3에서는 스코프 체인 개념을 썼고, ES5에서는 렉시컬 환경을 썼다. 엄청나게 큰 차이가 있다. 각각의 환경마다 컨텍스트 개념이 다르다.(묶음이 다르다)

2. Execution Context 형태

실행 컨텍스트 안에 함수 안에서 구할 수 있는 값의 덩어리들을 만든다.
이런 컨텍스트 환경을 정적으로 만든다는 것이다. 환경의 구성요소로는 [[Scope]] 개념과 식별자 해결을 위한 처리가 들어가 있다.
function 키워드를 만나서 show Function 오브젝트를 생성할 때, 이미 스코프가 결정이 된다. → 이것을 Lexical Scope라고 한다. 만약 함수를 호출 했을 때, show 함수에 대한 스코프를 그때 만든다면 동적 스코프가 되는 것이다.
예외로 동적인 스코프를 만드는 것이 있지만, 기본적으로 정적으로 환경을 만든다.
function book() { var cost = 18000; function show() { var title = 'Typescript Book'; }, function getPoint() { return cost; } show(); } book();
JavaScript
복사
show 실행 콘텍스트: { 렉시컬 환경 컴포넌트(Lexical Envioronment Component): { 환경 레코드(ER):{ 선언적 환경 레코드(Declarative Environment Record):{ title: "Typescript Book" }, 오브젝트 환경 레코드(Object Environment Record): {} }, 외부 렉시컬 환경 참조(Outer Lexical Environment Record){ costs: 18000, getPoint: function(){} }, 변수 환경 컴포넌트(Variable Environment Record): {}, this 바인딩 컴포넌트(This Binding Component): { 글로벌 오브젝트(window) } }
JavaScript
복사

2-1. 식별자 해결, 스코프 용도

식별자 해결

사용할 변수/함수를 결정하는 것이다. 신속하고 정확한 검색을 위해 스코프가 필요하다.
스코프에서 이름을 찾기 위해 스코프에 이름을 설정한다.
값은 변경되지만, 이름은 변경되지 않는다.
식별자 해결 대상은 이름
resolution의 사전적 의미: 해결, 결정
결정도 시맨틱적으로 맞음

스코프 용도

식별자 해결을 위한 수단과 방법
스코프가 있어서 식별자 해결이 가능한게 아니라 식별자 해결을 위해서 스코프가 필요한 것.

2-2 ES3에서의 scope chain

실행 컨텍스트와 관련이 있으며 식별자 해결을 위해 사용
식별자를 검색하기 위한 Object 리스트이다.
함수가 호출되면 scope를 생성하고 함수의 변수와 함수를 { name: value } 형태로 생성한다. 그리고 생성한 scope를 scope chain에 바인딩한다.
즉, 스코프 체인으로 식별자를 해결한다. ⇒ 동적처리
ES6부터는 스코프 체인이 사용되지 않고 정적 환경인 Lexical Environment를 생성한다. 따라서 완전한 하나의 컨텍스트를 생성한다. ES6 이후에는 "렉시컬 환경(Lexical Environment)"이 등장하면서 스코프 체인이 내부적으로 사라짐. ES3에서는 실제로 "스코프 체인"이라는 별도 자료구조가 존재했음.
→ 함수가 호출되어 메모리가 올라갈 때, 컨텍스트 하나만 올라가면 된다는 말

2-3. Lexical Environment

개발자 관점에서 최적화된 형태로 코드를 작성해야 하며 이를 위해 엔진 처리를 이해할 필요가 있다.
function 키워드를 만나면 Function 오브젝트를 생성하고, 스코프를 [[Scope]]에 설정한다. 이 시점에서 함수 밖의 스코프가 설정되고 결정된다. 이것이 렉시컬 환경이다.
var point = 123; function book(){ function getPoint(); } book(); // 123
JavaScript
복사
var 키워드 문제
var 키워드 없이 변수를 선언하면 글로벌 오브젝트에 설정된다. (이는 렉시컬 환경 구조와는 맞지 않다.)
ES5에서는 ‘use strict’를 써서 해결한다.
ES6에서는 let, const 변수를 사용한다. 변수 자체에 스코프 제약을 둠.
동적환경
자바스크립트에는 동적환경도 존재한다. 실행 시점에 스코프가 결정이 된다.
with문 - strict 모드에서 에러
eval() 함수 - 보안에 문제 있음
Node.js 코드 형태
서버 프로그램에서는 C10K를 고려해야 한다.
동시 사용자 1만명이 접속하는 서버를 구현해야 한다.
Node.js에서 JS는 비동기 처리를 하는데 ES5의 Context 형태가 효율성이 좋다.

3. Function 오브젝트

생각의 전환 포인트

함수가 호출되면 엔진은 함수의 변수와 함수를 {name: value} 형태로 실행 환경을 설정한 뒤, 함수 코드를 실행한다. ⇒ 이렇게 생각해야 JS의 아키텍처와 흐름을 쉽게 이해할 수 있다.

3-1. function 오브젝트 생성

var book = function(){...};
JavaScript
복사
엔진이 function 키워드를 만나면 빌트인 Function 오브젝트의 prototype에 연결된 메소드로, function 오브젝트를 생성.
생성한 오브젝트를 book 변수에 할당

3-2. function 오브젝트 생성 과정

function foods(){...}
JavaScript
복사
1.
function 키워드를 만나면 {food: {…}} 오브젝트를 생성하고 저장.
food가 function 오브젝트의 이름
오브젝트에는 프로퍼티가 없는 상태다.
선언문도 인스턴스를 생성하는 이유는 함수도 객체이기 때문.
2.
오브젝트에 prototype 오브젝트 첨부한다.
3.
prototype에 __proto__ 오브젝트 첨부
ES5 스펙에 __proto__ ES6에 기술.
현재는 [[Prototype]]
엔진이 사용한다는 의미
foods = { ... prototype: { constructor: foods, [[Prototype]]: {} }, }
JavaScript
복사
4.
빌트인 Object.prototype의 메소드로 Object 인스턴스를 생성하여 prototype.__proto__에 첨부.
5.
foods 오브젝트에 __proto__ 오브젝트를 첨부한다.
foods = { ... prototype: { constructor: foods, [[Prototype]]: Object.prototype }, [[Prototype]]: Function.prototype }
JavaScript
복사

3-3. 함수 실행 환경 인식

함수 실행 환경 인식이 필요한 이유는?
실행될 환경을 알아야 실행할 환경에 맞추어 실행을 할 수 있기 때문
실행 환경 설정 시점
function 키워드를 만나 function 오브젝트를 생성할 때
(호출할 때가 아님 )
설정하는 것
실행 영역(함수가 속한 스코프)
파라미터, 함수 코드 등
function 오브젝트를 생성하고 바로 실행하지 않으므로, 함수가 호출되었을 때 사용할 수 있도록 환경을 저장해야 한다. → 생성한 function 오브젝트에 저장
환경을 function 오브젝트에 내부 프로퍼티 형태로 저장한다.
내부 프로퍼티
엔진이 내부 처리에 사용하는 프로퍼티
[[ Scope ]]

3-4 내부 프로퍼티

생략

3-5. 엔진 해석 방법

스크립팅 언어는 작성된 코드를 위에서부터 한 줄씩 해석하고 실행하지만, 자바스크립트는 중간에 있는 코드가 먼저 해석될 수도 있다.
function book() { var title = 'Javascript'; function getBook() { return title; } var readBook = function () {}; console.log(getBook()); // 'Javascript' } book();
JavaScript
복사
1.
함수 선언문을 순서대로 해석한다.
Function 오브젝트 생성
2.
표현식을 순서대로 해석 후, 변수 초기화
var title = undefined
var readBook = undefined
3.
코드 실행
값의 할당

3-6. 호이스팅, 함수 앞에서 호출

함수 선언문은 초기화 단계에서 function 오브젝트를 생성한다. 따라서 어디에서도 함수를 호출할 수 있다. 즉, 함수 앞에서도 호출이 가능하다.
function hoistingTest() { function test() { return 'test1'; } console.log(test()); // 'test1' var test = function () { return 'test2'; }; console.log(test()); // 'test2' } hoistingTest();
JavaScript
복사
1.
함수 선언문을 먼저 해석한다. { test1: {…} } 형태로 Function 오브젝트 초기화까지 완료되었다.
2.
변수 선언문을 해석하고 초기화를 진행. undefined로 초기화 된다. 하지만 이미 test1에 대한 초기화가 단계에서 이미 값이 존재하기 때문에 초기화하지 않는다. 따라서 Function 오브젝트가 그대로 담겨있다.
3.
test 변수에 함수 Function 오브젝트 인스턴스를 생성하고 할당한다. 이후에는 ‘test2’가 출력이 된다.
function hoistingTest() { function test() { return 'test1'; } console.log(test()); // 'test2' function test() { return 'test2'; } console.log(test()); // 'test2' } hoistingTest();
JavaScript
복사

3-7. 오버로딩

함수 이름이 같더라도 파라미터 수 또는 값 타입이 다르면 각각 존재한다.
하지만 자바스크립트는 오버로딩을 지원하지 않는다. 자바스크립트에서는 {name: value} 형태로 저장하기 때문이다.
function test(one) {} function test(one, two) {} function test(one, three) {} test(one, two)
JavaScript
복사

4. Arguments

파라미터를 { key : value } 형태로 저장. 파라미터 수만큼 0부터 인덱스 부여해서 key로 사용한다.
유사배열(Array-like)
length 프로퍼티가 있다.
모든 인덱스가 0부터 1씩 증가한다.

5. 스코프

5-1. 스코프 목적

스코프(범위를 제한하여. 즉, 범위, 영역)에서 식별자를 해결
식별자 해결
변수 이름, 함수 이름을 찾는 것
이때 스코프를 사용한다.
스코프는 식별자 해결을 위한 것이다.

5-2. 스코프 설정

function 키워드를 만나면 food에 대한 Function 오브젝트를 생성하고 스코프를 설정한다.
생성한 오브젝트 [[Scope]]에 스코프를 설정한다. ES6 이후엔 [[Environment]]로 내부 슬롯이 변경되었다. 스코프 체인을 더이상 사용하지 않고 렉시컬 환경으로 저장한다는 의미.
즉, 이때 스코프가 결정된다.
function food() { var cost = 23000; function get() { console.log(cost); } get(); } food();
Scss
복사

5-3. Global 스코프

글로벌 오브젝트가 글로벌 스코프. 글로벌 오브젝트가 하나 밖에 없음.

오브젝트

개발자 관점으로 오브젝트에 함수와 변수를 작성.

스코프

엔진 관점으로 식별자 해결을 위한 것.
함수에서 보면 최종 스코프 .
book() 함수를 호출하려면 원래는 오브젝트.book형태로 작성해야 하는데 오브젝트를 작성하지 않고 함수만 작성했다.
오브젝트를 작성하지 않으면 글로벌 오브젝트를 오브젝트로 간주하여, 글로벌 오브젝트의 book() 함수를 호출한다.
글로벌 오브젝트는 실체가 없지만 호스트 오브젝트 개념으로 윈도우 오브젝트를 사용한다.
→ 즉, 글로벌 오브젝트라고 쓰면 함수, 변수, 메소드가 동반되고, 글로벌 스코프라고 쓰면 식별자 해결이 동반되는 것이다.

5-4. 스코프 바인딩

바인딩 목적은 스코프를 설정하고 식별자를 해결하는 것이다.

정적 바인딩

초기화 단계에서 바인딩
함수 선언문 이름을 바인딩
표현식 이름을 바인딩

동적 바인딩

실행할 때 바인딩
eval(), with

바인딩 시점의 중요성

바인딩할 때 스코프가 결정이 되기 때문이다.
function 오브젝트 생성 시점에 스코프가 결정된다. 이후로는 스코프가 변경되지 않는다.

6. Execution Context

함수가 실행되는 영역, 묶음. 함수 코드를 실행하고 실행 결과를 저장한다.

6-1. 실행 콘텍스트 실행 단계

여태까지는 초기화, 코드 실행 단계로만 중점적으로 살펴봤었다. 준비 단계를 확인.
1.
준비 단계
2.
초기화 단계
3.
코드 실행 단계

6-2. 실행 컨텍스트 코드 유형

실행 컨텍스트 생성 시점은 실행 가능한 코드를 만났을 때이다. 실행 가능한 코드 유형은 다음과 같다.
1.
함수 코드(렉시컬 환경)
2.
글로벌 코드(글로벌 환경)
3.
eval 코드(동적 환경)

6-3. 실행 컨텍스트 상태 컴포넌트 유형

실행 컨텍스트 상태를 위한 오브젝트로 실행 컨텍스트 안에 생성이 된다.
렉시컬 환경 컴포넌트(Lexical Environment Component)
변수 환경 컴포넌트(Variable Environment Component)
this 바인딩 컴포넌트(This Binding Component)
실행 컨텍스트(EC): { 렉시컬 환경 컴포넌트(LEC):{}, 변수 환경 컴포넌트(VEC): {}, this 바인딩 컴포넌트(TBC):{} } }
Plain Text
복사

6-4. 렉시컬 환경 컴포넌트(Lexical Environment Component)

함수와 변수의 식별자 해결을 위한 환경 설정이다.
함수 초기화 단계에서 해석한 함수와 변수를 { name: value } 형태로 저장한다.
이름으로 함수와 변수를 검색할 수 있게 된다.
함수 밖의 함수와 변수를 참조할 수 있는 환경을 설정한다.

6-4-1. 렉시컬 환경 컴포넌트 생성

function, with, try-catch를 만났을 때, 렉시컬 환경 컴포넌트를 생성한다.

6-4-2. 렉시컬 환경 컴포넌트 구성

환경 레코드(Environment Record)
외부 렉시컬 환경 참조(Outer Lexical Environment Reference)

6-4-3. 렉시컬 환경 컴포넌트 설정

환경 레코드에 함수 안의 함수와 변수를 기록.
외부 렉시컬 환경 참조에 function 오브젝트의 [[Scope]]를 설정한다. (ES6부터는 내부 슬롯 [[Environment]]가 표준)
따라서 함수 안과 밖의 함수, 변수를 하나의 컨텍스트 개념으로 사용할 수 있게 됨. 외부 렉시컬 환경으로 묶음으로 바로 액세스하면 된다. 실행 컨텍스트 밖으로 나갈 이유가 없다.
실행 컨텍스트(EC): { 렉시컬 환경 컴포넌트(LEC):{ 환경 레코드(ER):{ point: 100, ... 등등 }, 외부 렉시컬 환경 참조(OLER):{ ... } } }
Plain Text
복사
외부 렉시컬 환경 참조
스코프와 실행 중인 함수가 Context 형태이므로 스코프의 변수와 함수를 별도의 처리 없이, 즉시 사용할 수 있음.
실행 컨텍스트에서 함수 안과 밖의 함수, 변수를 사용할 수 있으므로 함수와 변수를 찾기 위해 실행 컨텍스트를 벗어나지 않아도 된다.

6-5. 변수 환경 컴포넌트(Variable Environment Component)

실행 컨텍스트 초기화 단계에서 렉시컬 환경 컴포넌트와 같게 설정한다. 코드 실행 이후에는 렉시컬 환경에 등록이 된다. 하지만 초기화가 필요할 때, 초기화 값이 필요하다. 초기값을 복원할 때 사용하기 위한 것.

6-6. 실행 컨텍스트 실행 과정

var cost = 18000; function getCost(paid) { var point = 100; return cost - point; } console.log(getCost(20000));
JavaScript
복사
function 키워드를 보고 Function 오브젝트 생성 후, getCost 초기화 진행. 이때, [[Scope]]에 글로벌 오브젝트 설정. point 초기화 진행.
getCost 함수를 호출하면 엔진은 실행 컨텍스트를 생성하고, 실행 컨텍스트 안으로 이동.

getCost 함수를 호출할 때, 실행 컨텍스트 실행 과정

준비 단계
1.
컴포넌트를 생성하여 실행 컨텍스트에 첨부한다.
렉시컬 환경 컴포넌트, 변수 환경 컴포넌트, this 바인딩 컴포넌트
2.
환경 레코드를 생성하여 렉시컬 환경 컴포넌트에 첨부
함수 안의 함수, 변수를 바인딩한다.
현재까지의 상황. 준비 단계이므로 환경 레코드는 빈 오브젝트인 상황이다.
실행 컨텍스트(EC): { 렉시컬 환경 컴포넌트(LEC):{ 환경 레코드(ER): {}, 외부 렉시컬 환경 참조(OLER): {} } }
Plain Text
복사
3.
외부 렉시컬 환경 참조를 생성하여 렉시컬 환경 컴포넌트에 첨부하고, function 오브젝트의 [[Scope]]를 설정
실행 컨텍스트(EC): { 렉시컬 환경 컴포넌트(LEC):{ 환경 레코드(ER): {}, 외부 렉시컬 환경 참조(OLER): { ... } } }
Plain Text
복사
초기화 단계
4.
호출한 함수의 파라미터 값을 호출된 함수의 파라미터 이름에 매핑. 환경 레코드에 작성한다.
5.
함수 선언문을 Function 오브젝트로 생성. 함수 표현식과 변수에 초기값 설정.
— 여기까지는 외부에 실행 상태를 제공하지 않는다. —
실행단계
6.
함수 안의 코드를 실행한다.
7.
실행 컨텍스트 안에서 관련된 함수와 변수 사용 가능.

6-7. 환경 레코드

환경 레코드를 구분하는 이유는 기록 대상에 따라 다르기 때문이다.
선언적 환경 레코드(Declaractive Environment Record)
function, 변수, catch 문에서 사용.
정적인 것을 기록한다.
오브젝트 환경 레코드(Object Environment Record)
글로벌 함수와 변수, with 문에서 사용.
동적인 것을 기록한다.

글로벌 환경 레코드

글로벌 오브젝트에서 사용한다. 렉시컬 환경 컴포넌트와 형태가 같다.
동적으로 함수와 변수 바인딩 함수에서 var 키워드를 사용하지 않고 변수를 선언하면 글로벌 오브젝트로 설정됨. 그래서 오브젝트 환경 레코드를 사용한다.
외부 렉시컬 환경 참조 값은 null이다.
실행 컨텍스트(EC): { 글로벌 환경(GE): { 환경 레코드(ER): { 오브젝트 환경 레코드: 글로벌 오브젝트 }, 외부 렉시컬 환경 참조(OLER): null } }
Plain Text
복사

6-8. this 바인딩 컴포넌트

this로 함수를 호출한 오브젝트의 프로퍼티에 접근이 주 목적이다.
obj.book()
thisobj를 참조할 수 있도록, this 바인딩 컴포넌트에 obj 참조를 설정한다.
var obj = { point: 100 }; obj.getPoint = function () { return this.point; }; obj.getPoint();
JavaScript
복사
준비단계
1.
obj.getPoint() 함수 호출.
2.
실행 컨텍스트 생성. 렉시컬, 변수 환경 , this 바인딩 컴포넌트 생성.
3.
this 바인딩 컴포넌트에 getPoint()에서 thisobj의 프로퍼티를 사용할 수 있도록 바인딩.
실행 컨텍스트(EC): { 렉시컬 환경 컴포넌트(LEC):{ 환경 레코드(ER): {}, 외부 렉시컬 환경 참조(OLER): {} this 바인딩 컴포넌트: { point: 100, getPoint: function(){} } } }
Plain Text
복사
초기화 단계
4.
파라미터, 함수 선언문, 변수 선언 없음.
실행 단계
5.
return this.point 실행.
6.
this 바인딩 컴포넌트에서 point 검색.
실행 컨텍스트는 어떻게 코드를 짜야 좋은것인가를 고민하기 위한 목적이 있다.

6-9. 콜 스택

생략

6-10. 함수 호출

함수가 호출되면 3개의 파라미터 값을 실행 컨텍스트로 넘겨준다.
1.
함수를 호출한 오브젝트
함수를 호출한 오브젝트를 this 바인딩 컴포넌트에 설정하여 this로 참조. → 함수는 누가 호출했는지 알 수 없기 때문
2.
함수 코드
function 오브젝트의 [[Code]]에 설정되어 있음.
3.
호출한 함수의 파라미터 값
호출된 함수의 Argument 오브젝트에 설정.
function 오브젝트의 [[FormalParameters]]에 작성된 이름을 구하고, param에서 index번째 값을 구해서 매핑 이후 결과를 선언적 환경 레코드에 설정한다.

파라미터 값 할당 기준

선언적 환경 레코드에서 one의 존재를 체크 → 값이 존재한다면 초기화를 진행하지 않는다. 초기화의 특징!! one, two는 초기화를 진행하지 않는다.
var obj = {}; obj.getFood = function (one, two, two) { var one; console.log(one + ', ' + two); // 'pizza, chiken' two = 'burger'; console.log(one + ', ' + two); // 'pizza, burger' }; obj.getFood('pizza', 'burger', 'chiken');
JavaScript
복사

7. function 인스턴스(개어렵..)

7-1. function 오브젝트도 인스턴스다.

앞서 살펴봤지만 빌트인 Number, String 등등 [[PrimitiveValue]] 를 가지는 빌트인 오브젝트들은 값을 반환한다. 하지만 오브젝트 타입은 프리미티브 값이 없기 때문에 인스턴스를 생성한다.
둘 다 성격적으로는 인스턴스지만 Function 오브젝트로 생성한 것과 구분하기 위해서 Function 오브젝트, 인스턴스로 구분한다.
new 연산자로 생성하는 인스턴스는 일반적으로 prototype에 프로퍼티를 작성한다.

7-2. 생성자 함수, 생성자 함수 실행 과정, 인스턴스 생성 과정

생성자 함수 실행 과정

function Book(point) { this.point = point; } Book.prototype.getPoint = function () { return this.point; }; var obj = new Book();
JavaScript
복사
1.
엔진이 new 연산자를 만나면 function 오브젝트 [[Construct]]를 호출하면서 파라미터 값을 넘겨줌.
2.
function 오브젝트를 생성할 때 Book() 함수 전체를 참조하도록 [[Construct]]에 설정.
3.
[[Construct]]에서 인스턴스를 생성하여 반환. new 연산자가 생성하는 것이 아님.
4.
반환된 인스턴스를 new 연산자가 받아 new 연산자를 호출한 곳으로 반환.
생성자 함수

인스턴스 생성 과정

function Book(point) { this.point = point; } Book.prototype.getPoint = function () { return this.point; }; var bookObj = new Book(10);
JavaScript
복사
1.
new Book(10)을 실행하면 Book 오브젝트의 [[Construct]]를 호출하면서 파라미터 값을 넘겨줌.
2.
[[Construct]]빈 Object를 생성하는데, 이것이 인스턴스다.
3.
오브젝트 내부 처리용 프로퍼티를 설정.
4.
오브젝트 [[Class]]에 “Object” 설정. 따라서 생성한 인스턴스 타입은 Object
5.
Book.prototype에 연결된 프로퍼티를 생성한 인스턴스의 [[Prototype]]에 설정. constructor도 설정
위의 [[Construct]]는 내부 오브젝트 프로퍼티, 5번의 constructorBook

7-3. constructor 프로퍼티

생성하는 function 오브젝트를 참조. function 오브젝트를 생성할 때 설정된다.
prototype에 연결된다.
Book function 오브젝트:{ prototype: { constructor: Book } }
JavaScript
복사

constructor 비교

var Book = function () {}; var result = Book === Book.prototype.constructor; console.log('1: ', result); // true var obj = new Book(); console.log('2: ', Book === obj.constructor); // true console.log('3: ', typeof Book); // 'function console.log('4 ', typeof obj); // 'object'
JavaScript
복사
오브젝트 타입이 바뀐다는 것은 오브젝트 성격과 목적이 바뀐 것을 뜻한다?

7-4. prototype상속, prototype 오브젝트 목적, 인스턴스 상속

1.
prototype 확장
Book.prototype.getPoint = function(){}
2.
프로퍼티 공유
var obj = new Book(123); obj.getPoint();
3.
인스턴스 상속
function 인스턴스를 연결하여 상속 Point.prototype = new Book();

7-5. prototype 확장, 프로퍼티 연결, 확장과 인스턴스 형태

function Book(point) { this.point = point; } Book.prototype.getPoint = function () { return this.point; }; var obj = new Book(10); obj.getPoint();
JavaScript
복사
obj: { point: 100, __proto__= { constructor: Book, getPoint: function(){}, __proto__: Object } }
JavaScript
복사
인스턴스를 생성하면 prototype에 연결된 메소드를 인스턴스.메소드이름() 형태로 호출 즉, __proto__를 입력하지 않아도 된다.

7-6. this와 prototype, this로 인스턴스 참조

this로 인스턴스 참조

this가 메소드를 호출한 인스턴스를 참조한다. var obj = new Book(); obj.get() 형태에서 thisobj 참조
인스턴스에서 메소드 호출 방법 prototype에 연결된 프로퍼티가 __proto__에 설정되며 인스턴스 프로퍼티가 된다. 인스턴스 프로퍼티란 this.prototype.setPoint()가 아닌 this.setPoint()로 호출. 인스턴스 안에서는 프로토타입에 연결되어 있는 것들은 모두 다 하나의 프로퍼티 개념이다. 이것이 일반함수와 인스턴스 사용하는 것의 차이
function Book(point) { console.log('1: ', this.point); } Book.prototype.getPoint = function () { this.setPoint(); console.log('2: ', this.point); return this.point; }; Book.prototype.setPoint = function () { this.point = 200; }; var obj = new Book(10); obj.getPoint(); // 1: undefined // 2: 200
JavaScript
복사
1.
생성자 함수에서 this는 생성하는 인스턴스 참조. 생성하는 인스턴스에 point 프로퍼티가 없더라도 에러가 나지 않고 undefined를 반환.
2.
obj.getPoint(); this가 메소드를 호출한 인스턴스 참조 즉, 메소드 앞에 작성한 인스턴스 참조.

그런데 Function 오브젝트로 생성한 것도 인스턴스라 하지 않았나..?

prototype 메소드 직접 호출

function Book(point) { this.point = point; } Book.prototype.getPoint = function () { return this.point; }; var obj = new Book(10); console.log(obj.getPoint()); console.log(Book.prototype.getPoint());
JavaScript
복사
Book.prototypepoint가 없으므로 undefined 반환.
인스턴스와 프로토타입 오브젝트는 인스턴스가 다르다. 저장하는 위치가 다르다.

프로퍼티 공유 시점

사용하는 시점에 prototpye의 프로퍼티 공유
prototype의 프로퍼티로 인스턴스를 생성하지만, 인스턴스의 프로퍼티는 원본 prototype의 프로퍼티 참조. 복사하여 인스턴스에 갖고 있는 개념이 아님.
인스턴스의 메소드를 호출하면 원본 prototype의 메소드를 호출. 따라서 원본 prototype에 메소드를 추가하면 생성된 모든 인스턴스에서 추가한 메소드 사용이 가능. 원본 prototype 메소드를 호출하기 때문이다.
인스턴스를 생성할 때는 없었어도 호출하는 시점에 있다면 호출이 가능하다.

7-7. 인스턴스 프로퍼티

obj 인스턴스 = { point: 100, getPoint: function(){}, __proto__: { getPoint:function(){} } }
JavaScript
복사
prototype에 연결된 프로퍼티도 인스턴스 프로퍼티가 된다. 직접 인스턴스에 연결된 프로퍼티와 차이가 있다.
인스턴스의 프로퍼티를 prototype으로 만든 인스턴스 프로퍼티보다 먼저 사용한다.
인스턴스마다 값을 다르게 가질 수 있다. 인스턴스를 사용하는 중요한 목적

왜 인스턴스를 사용하는가? 왜 클래스를 만드는가?

하나로 묶는 이유는 데이터 중심으로 접근하겠다는 의미이다. 인스턴스마다 데이터를 가져가겠다는 의미. 그러면 설계가 가장 중요하다. 객체지향프로그래밍에 대한 이해가 필요하다. 자바스크립트는 객체지향 언어이다.

8. this

8-1. this 개요, this와 글로벌 오브젝트, this와 window 오브젝트

this 개요

obj.name() 형태로 호출한 함수(메소드)에서 this로 인스턴스(오브젝트)를 참조.
실행 컨텍스트의 this 바인딩 컴포넌트에 바인딩된다.

this와 글로벌 오브젝트

글로벌 오브젝트에서 this는 글로벌 오브젝트를 참조.
console.log(this === window); // true
JavaScript
복사
this로 글로벌 변수 액세스.
this가 글로벌 변수를 참조하기 때문.
var value = 100; console.log(this === window); // true
JavaScript
복사

8-2. this 참조 범위, strict 모드, this 참조 오브젝트

strict 모드에서는 함수 앞에 오브젝트를 작성하지 않으면 this 바인딩 컴포넌트에 undefined 가 설정 되므로 thiswindow를 참조할 수 없음.
var book = { point: 100, member: { point: 200, get: function(){ console.log(this === book.member); // true console.log(this.point); // 200 } } } book.member.get();
JavaScript
복사

8-3. this와 인스턴스

인스턴스 목적?
인스턴스마다 고유 값 유지
인스턴스에서 this의 목적
this로 인스턴스를 참조하여 this.name 형태로 프로퍼티에 접근
__proto__ 프로퍼티 접근
prototype에 연결된 프로퍼티가 인스턴스의 __proto__ 에 첨부되며, this.method() 형태로 __proto__에 첨부된 method() 호출

Call()

function greet(name, age) { console.log(`${this.title} ${name}, 나이: ${age}`); } const person = { title: "Mr." }; greet.call(person, "John", 30);
JavaScript
복사

apply()

greet.apply(person, ["Alice", 25]);
JavaScript
복사

bind()

const boundGreet = greet.bind(person, "Emma"); boundGreet(28);
JavaScript
복사
메서드
언제 사용하면 좋을까?
call()
this를 변경하면서 즉시 실행할 때
apply()
call()과 동일하지만, 인자를 배열로 전달해야 할 때
bind()
this를 고정한 새로운 함수를 만들고, 나중에 실행할 때

9. 프로퍼티 연동 방지, 재귀 함수 형태

var member = { Jan: { item: { title: 'JS', amount: 100, point: [10, 20, 30] } }, Feb: { item: { title: 'TS', amount: 200, point: [40, 50, 60] } }, }; let result = 0; function show(obj) { for (var type in obj) { if (typeof obj[type] === 'object') { if (Array.isArray(obj[type])) { console.log(obj[type]); result += obj[type].reduce((prev, cur) => prev + cur, 0); } else { show(obj[type]); } } else { console.log(type, obj[type]); } } } show(member); console.log(result);
JavaScript
복사

10. 즉시 실행 함수

(function(){ console.log("TEST"); })()
JavaScript
복사
엔진이 함수를 만났을 때 자동으로 함수를 실행
IIFE: Immediately Invoked Function Expression
함수 이름이 없으므로 함수 선언문, 함수 표현식도 아니다. 무명함수, 익명 함수라고도 부른다.
저장할 필요가 없는 1회성 코드이면서 즉시 실행 해야할 경우.

11. 클로저

function 오브젝트를 생성할 때 함수가 속한 스코프를 [[Scope]]에 설정하고, 함수가 호출 되었을 때 [[Scope]]의 프로퍼티를 사용하는 매커니즘.
실행 중인 function 오브젝트에 작성한 변수, 함수를 선언적 환경 레코드에 설정.
[[Scope]]의 변수, 함수를 외부 렉시컬 환경 참조에 바인딩.
변수 이름으로 접근하여 값을 사용하거나 변경할 수 있음.
함수를 호출할 수 있음.
변수가 선언적 환경 레코드에 없으면 외부 렉시컬 환경 참조에서 식별자 해결을 한다. 클로저(Closure)는 함수가 자신이 선언될 때의 렉시컬 환경(Lexical Environment)을 기억하고, 그 환경에 접근할 수 있는 기능. 외부 렉시컬 환경 참조에 함수가 속한 스코프가 설정되기 때문이다.
→ 이게 클로저의 논리이다.
function book() { var point = 100; var getPoint = function (param) { point = point + param; return point; }; return getPoint; } var obj = book(); console.log(obj(200));
JavaScript
복사
1.
book 함수를 호출하면 실행 컨텍스트 생성
2.
3개의 컴포넌트 생성.
3.
function 오브젝트 [[Scope]]를 외부 렉시컬 환경 참조에 바인딩한다. 이때는 글로벌 오브젝트가 바인딩.
실행 컨텍스트: { 렉시컬 환경 컴포넌트 = { 환경 레코드:{ 선언적 환경 레코드:{}, }, 외부 렉시컬 환경 참조:{ [[scope]] } }, 변수 환경 컴포넌트 = {...} this 바인딩 컴포넌트:{} }
JavaScript
복사
4.
var point; var getPoint; 선언적 환경 레코드에 설정.
5.
point = 100; getPoint = function(){…} 코드 실행
6.
function 오브젝트 생성. 스코프를 [[Scope]]에 바인딩한다. book()의 스코프가 [[Scope]]에 바인딩된다.
렉시컬 환경 컴포넌트 = { 환경 레코드:{ 선언적 환경 레코드:{}, }, 외부 렉시컬 환경 참조:{ point: 100 } }
JavaScript
복사
7.
getPoint function 오브젝트 반환 후, obj에 할당.
8.
obj(200); d을 실행하면 getPoint 실행 컨텍스트 생성하고 getPoint[[Scope]]를 외부 렉시컬 환경 참조에 바인딩한다.
9.
함수 안의 코드 실행. point를 선억적 환경 레코드에서 식별자 해결.
10.
point가 없으므로 외부 렉시컬 환경 참조에서 식별자 해결

클로저와 무명함수

원래라면 저장되지 않고 날라가야 하지만, function getPoint(param){}으로 인해 [[Scope]] 에 설정되므로 날라가지 않는다.
var book = (function () { var point = 100; var getPoint = function (param) { point = point + param; return point; }; return getPoint; })(); console.log(book(200));
JavaScript
복사