Hoisting
변수들이 선언되기 전에 접근 또는 사용이 가능한 경우가 있다. 이것은 hoisting이 일어났기 때문이다.
변수들이 코드의 최상단으로 끌어올려졌다고도 표현한다.
발생하는 이유?
코드 실행 전(creation phase)에 먼저 코드를 스캔한다. 이 때 변수 선언이 있다면 execution context의 variable environment의 object의 프로퍼티로 저장되기 때문에 접근, 사용이 가능해지는 것이다.
변수/함수 종류 별 hoisting 여부 정리
function declaration
호이스팅 발생
선언 전에 접근+사용 가능
strict mode에서는 block scope, 아니라면 function scope
var variables
호이스팅 발생
선언 전에 접근 가능, 값은 undefined
function scope
let, const variables
호이스팅 발생은 하지만 기능적으론 발생하지 않은 것과 같음
선언 전에 접근 불가능, 하지만 접근하려 하면 referrence error가 나오지 않고 cannot access before initialization 에러가 발생 (TDZ)
block scope
function expressions and arrows
함수가 담길 변수를 var, let, const 중 어떤 것으로 선언했는지에 따라 hoisting 여부가 결정됨
선언 전 접근 여부도 위와 같이 결정됨, var이라면 접근가능, 하지만 함수 사용은 불가능 (undefined) / let, const라면 접근 불가능 (uninitialized)
scope 또한 var, let, const에 따라 다름
TDZ
Temporal Dead Zone
변수 선언 전에 해당 변수에 접근하는 것
not defined가 아닌 cannot access before initialization 에러 발생
(creation phase때 코드를 스캔하여 선언될 변수들을 미리 variable environment에 넣었기 떄문에 언젠간 선언될 것이라는 것을 알고있다.)
사진의 붉은 부분이 TDZ이다. 쉽게 말하면 변수가 선언되기 전 까지의 코드 부분이 TDZ라고 할 수 있다. 이 TDZ에서는 변수에 접근할 수 없다.
This
모든 execution context (즉, 함수) 마다 생성되는 특별한 변수
static하지 않은 변수이다. this는 함수가 어떻게 호출되었는지에 따라 어떤 값을 가질 지 결정된다. 그리고 그 값은 함수가 실제로 호출되었을 때에만 값이 지정된다.
this 값이 지정되는 경우 (함수 호출 방식)
method 호출
자기 자신을 호출한 object가 this값이 된다.
만약 method가 arrow function 이라면 자신을 호출한 object가 아닌 lexical this
method 내부의 this가 자신의 바깥의 object를 가리키는 이유는 그 object의 내부에서 작성되었기 때문이 아니라 그 object가 호출(사용)했기 때문이다. 예를 들어 a,b object가 있고 a의 내부에 calcAge라는 method가 있고 이 method에서 this를 사용했을 때, b.calcAge = a.calcAge로 b에서 a의 method를 borrow했을 때 b.calcAge를 실행시켜보면 this는 b object를 가리키고 있는 것을 확인할 수 있다.
method가 작성된 곳은 a object이지만 b에서 빌려서 호출하니 this는 b를 가리키고 있는 것이다. 따라서 method의 this는 자신을 호출한 obejct를 가리킨다.
만약 위와같은 방법으로 일반적인 변수에 method를 copy시켜서 함수를 호출시켰다면? => 일반적인 함수호출이기 때문에 2번과 같은 결과가 나온다.
일반적인 호출 (simple function call)
this = undefined (only in strict mode)
this = window obejct (global object) (not in strict mode)
method의 내부함수에서의 this는 2번과 같다. method의 경우에는 1번과 같이 동작하지만 method의 내부함수는 다르다.
내부함수의 this문제 해결법에는 두가지가 있다.
첫 번째로 method 내부에 const self = this로 변수를 만들어 주고 이를 내부함수에서 사용한다. (es6 이전의 방식)
두 번째는 내부함수를 arrow function으로 선언하는 것이다. arrow function은 this값이 자신의 상위 스코프를 가리키게 되기 때문에 method와 같은 this를 갖게되고 이는 object의 scope와 같다. (es6+ 방식)
arrow function 호출
this = 자신의 바로 위의 scope (surrounding function, lexical this)
method로는 arrow function을 사용하지 않는 것이 좋다. this 키워드가 자신의 바로 위의 scope로 지정되기 때문에 혼동(원하지 않는 결과)이 오기 쉽다. object literal의 코드블럭은 자신만의 scope를 갖게되는 것이 아니므로 object의 method로 arrow function을 사용했다면 this로 object의 내부의 값을 참조할 수 없다.
event listener로 호출
this = DOM element that the handler function attatched to
new 연산자로 호출
later section
Arguments
파라미터로 넘긴 값들이 들어있는 객체
함수 선언식, 함수 표현식에만 사용 가능하며 arrow function에서는 사용할 수 없다.
Primitives vs Objects
위의 코드에서 oldAge의 값은 age의 값이 변경되기 전의 값이 그대로 유지되었지만 me object의 age는 friend.age에 의해 함께 변한 것을 볼 수 있다. 그 이유는 무엇일까
Primitive와 Object의 차이
primitive는 call stack에 저장되며 object는 memory heap에 저장된다. (이 둘은 JS Engine에 있음)
primitive가 call stack에 저장된다는 말은 자신들이 선언된 execution context의 내부에 저장된다는 뜻이다. (call stack에 들어가는 execution context의 내부에 들어간다는 뜻)
(위의 사진 설명)
primitive value는 call stack의 자신이 선언된 execution context에 저장되며 address와 value를 갖는다.
먼저 age라는 변수를 선언하고 값을 30으로 넣어주었기 때문에 0001주소에 30이라는 값이 들어가게 되고 age 변수는 이를 가리키게 된다.
그리고 oldAge는 oldAge = age에 의해 age가 가리키고 있는 0001주소를 가리키게 되고 그렇기 때문에 똑같이 30이라는 값을 갖게된다.
하지만 age = 31에 의하여 age변수의 값은 31로 변경되었다.
여기서 주목할 점은, call stack의 memory adress의 값은 immutable(불변)하다는 것이다.
그렇기 때문에 0001 주소의 value가 31로 변경되는 것이 아니라 0002라는 새로운 주소에 31이라는 값을 넣고 age변수가 이를 가리키게 되는 것이다.
이런 과정으로 age는 31, oldAge는 30의 값을 갖게되는 것이다.
하지만 reference value (object)는 사진과 같이 값을 저장한다.
call stack의 메모리 공간의 0003이라는 주소에 D30F라는 value를 갖게된다.
object의 value는 call stack에 담기기에 너무 클 수 있기 때문에 비교적 unlimited한 heap에 저장하는 것이다.
여기서 주목할 점은 value가 일반적인 value가 아닌 memory heap의 주소라는 것이다. 이 value는 memory heap의 주소를 갖고 있기 때문에 이를 이용해서 heap에 저장되어있는 value를 갖고온다.
me와 friend는 동일한 주소를 가지고 있고, 그렇기 때문에 friend.age를 변경하였을 때 heap의 value가 변경되어 me.age까지 27로 변경된 것이다.
여기서 friend object는 const로 선언되었음에도 불구하고 변경이 가능했는데 그 이유는 call stack의 value는 변경되지 않았기 때문이다.
me와 friend object는 서로다른 identifier가 완전히 동일한 reference를 가리키고 있는 것이다.
따라서 object를 copy하는 것은 진짜 copy하는 것이 아닌 그저 같은 reference를 가리키고 있는 또 다른 변수를 생성한 것 뿐이다.
0개의 댓글