만약 당신이 스크립트 파일을 브라우저에서 실행하면, 브라우저에 내장되어있는 자바스크립트 엔진이(chrome의 경우 v8이라는 자체 자바스크립트 엔진을 사용한다) 스크립트 파일을 분석하고 실행한다. 엔진은 크게 두 부분으로 나눌 수 있는데, 인터프리터와 컴파일러이다. 인터프리터는 말 그대로 스크립트를 해석하여 바로 실행하는 역할을 한다. 인터프리터의 해석-실행 과정이 진행되는 동시에 컴파일러는 자바스크립트 코드를 바이트 코트로 컴파일 하여 당신의 컴퓨터로 보낸다. 따라서 만약 스크립트 파일이 재실행된다면, 인터프리터의 역할이 줄어들고 당신의 운영체제에서 곧바로 빠르게 실행된다. 당신의 자바스크립트 코드가 컴파일 되는 과정에서 브라우저 API의 코드 또한 함께 포함되어 당신의 컴퓨터로 이동하는데, 브라우저 API는 브라우저와 당신의 자바스크립트 코드간의 연결고리라고 생각하면 된다.
힙HEEP 은 장기 메모리로, 메모리를 할당작업이라 생각하면 된다. 프로그램 내에서 다루는 데이터가 저장되는데, 장기메모리리이다.
스택STACK은 단기 메모리로, 프로그램의 흐름을 관리하는데, 주로 현재 실행되고 있는 함수를 관리하는 역할을 한다. 어떤 함수가 어떤 데이터를 반환하는지를 관리한다.
그럼 힙과 스택은 어떤 관계일까? 먼저 힙은 장기메모리답게, 스크립트에서 정의한 함수를 저장한다. 그리고 스택에서, 힙에서 저장한 함수의 실행을 관리하는데, 어떤 함수가 현재 동작하며, 어떤 함수가 언제 종료될 지 등을 관리한다.
function getName(){
return "KIM";
}
function hello(){
console.log(getName());
}
hello();
위 코드를 통해 스택의 작동방식을 이해해보자. 먼저 당신이 스크립트 파일을 실행할 경우, 스크립트 전체는 '익명 함수'라고 불리는 함수의 형태로 스택에서 가장 처음 쌓인다. 스크립트가 실행되는 과정에서 함수 hello의 호출을 만나게 되면, 익명 함수 위에 hello()가 쌓인다. hello는 console.log()를 호출하며, console.log()안에서는 getName() 함수를 호출한다. 따라서 처음 익명함수 하나 뿐이였던 스택은 함수의 호출 순서에 따라 익명함수 위에 hello(), console.log(), getName()이 쌓인다. 그리고 함수의 실행이 종료되면, 함수의 스택에서 빠지게 되고 최종적으로 스크립트까지 종료되면 스택은 비게 된다. 즉, 후입선출 : 나중에 들어온 것이 먼저 나가는 방식이다.
참고로 자바스크립트 엔진은 싱글 스레드로 동작하기 때문에 스택 또한 하나뿐이다.
우리가 사용하는 다양한 자료형을 크게 두 분류로 나눌 수 있다, 원시값을 저장하는 자료형과 참조값을 저장하는 자료형이다
원시값을 저장하는 자료형은 대표적으로 문자열,숫자 등이 있는데, 이들이 저장하는 원시값의 의미는 쉽게 말해서 값 그 자체이다. 다음의 코드를 통해 이해해보자.
let name = "KIM";
let name2 = name;
name = "COOK";
console.log(name2); //"KIM"
name 변수에 "KIM"을 할당하고, name2를 선언해 name의 값을 복사하는 코드이다. 이 때 복사되는 과정은 상식에 맞게 값 그자체를 복사하는 것이다. 즉 name과 name2는 독립적으로 존재하며, 값 그 자체를 새로운 변수(name2)에 할당한 것이기 때문에 name의 값을 바꾼다고 해서 name2에 영향을 줄 수는 없다.
그러나 참조값을 저장하는 객체라면 애기는 달라진다. 참조값을 저장한다는 말은 포인터 - 즉 객체의 값 그 자체가 아닌 객체의 값이 위치한 "주소"를 저장한다는 말이다. 객체는 값 그자체가 아닌 값의 주소를 저장하기 때문에, 위에서 보여준 예시 코드와는 다른 결과를 보여준다.
let player = {name : KIM};
const player2 = name;
player.name = "LEE";
console.log(player2.name) //LEE
player2.name = "MIN";
console.log(player.name) //MIN
player2 = {name : "GOD"}; //런타임 에러
먼저 player2를 player로부터 복사한 것을 확인할 수 있다. 그러나 여기서 복사된 것은 값 그자체가 아니라, 값이 저장되어 있는 주소이기 때문에, player를 통해 값에 변화를 주면, 동일한 값을 공유하는 player2에서 또한 반영이 되는 것이다. 그리고 player2가 const로 선언되었어도, 마지막 줄과 같이 직접적으로 새로운 객체 값을 재할당 하는 것이 아니라 기존 값에 변화를 주는 것이면, 그 값의 주소에는 변화가 없기 때문에 가능한 것이다.
따라서 원시값과 참조값에 대한 이해가 없다면, 자바스크립트 코드 작성시 자신이 의도한 대로 복사가 되지 않을 수 있다.
그렇다면 객체의 주소가 아닌 값을 복사하려면 어떻게 할까?
const player = {name : KIM};
const player2 = { ...player }
...은 자바스크립트의 전개 연산자로 객체의 값을 하나하나 옮기는 행위를 간단하게 ... 으로 표현한 것이다.
따라서 위와 같이 코드를 작성하면 player2에는 player가 저장하고 있는 주소가 저장되는 것이 아니라, player가 저장하고 있는 주소의 실제 값과 동일한 값이 새로 생성되어 그것의 주소가 저장된다.