느슨한 타입(loosely typed)의 동적(dynamic) 언어
변수는 특정 타입으로 할당되지 않는다.
모든 타입의 값으로 재할당이 가능하다.
JavaScript 형변환
자바 스크립트의 형변환은 대부분 적절한 타입으로 자동으로 이루어진다.
let a = 10
alert(a)
위 코드에서 alert는 문자열을 출력해야 하므로 a가 필요에 따라 자동으로 문자열로 바뀐다. 이렇게 자동으로 이루어지는 경우를 암시적 변환
이라고 한다.
작성자가 의도를 가지고 String(a)
와 같이 형을 변환하는 것은 명시적 변환
이라고 한다.
==, ===
JS는 동적 언어라는 특성 때문에, 다른 언어처럼 ==으로 비교하는 경우 의도대로 작동하지 않을 가능성이 있다.
1 == "1"
위의 코드를 입력하면 true를 출력한다.
타입을 변경했을 때, 두 값이 같기 때문이다.
1 === "1"
이와 같은 경우에는 false를 입력한다. 즉, ===을 사용하면 형변환 없이 엄격한 비교가 가능하다.
느슨한 타입(loosely typed)의 동적(dynamic) 언어의 문제점
변수에 예상하지 못한 값이 들어와 오류가 발생할 가능성이 있다.
동적 언어는 런타임 시에 확인해야 하므로, 코드가 길어질수록 오류를 찾기 힘들어진다.
보완 방법
엄격한 문법을 지원하는 TypeScipt
나 Flow
를 사용한다.
undefined와 null
undefined
는 선언했지만 값을 할당하지 않은 변수나, 값이 주어지지 않은 인수에 자동으로 할당된다.
값을 할당하지 않은 변수, 함수가 값을 리턴하지 않는 경우에 undefined가 할당된다.
null
은 의도적으로 값을 비운 상태이다. undefined는 값이 지정되지 않았음을 의미하지만, null은 해당 변수가 가리키고 있는 값이 없음
을 의미한다.
참고 : undefined == null
은 true
가 나온다.
기본형 데이터와 참조형 데이터
기본(원시)형 데이터
는 값을 그대로 할당한다. Number
, String
, Boolean
, null
, undefined
이 기본형 데이터에 속하고, ES6부터는 Symbol
도 기본형 데이터에 추가되었다.
참조형 데이터
는 값이 저장된 주소값을 할당(참조)한다. 배열(Array)
, 함수(Function)
, 정규표현식(RegExp)
등이 참조형 데이터에 속하고, ES6부터는 Map
, Set
, WeakMap
, WeakSet
등도 추가되었다.
불변 객체를 만드는 방법
const
로 선언하는 경우 상수가 되기 때문에 변경하지 못한다고 알려져있다. 값을 변경하는 재할당은 불가능하지만, 객체의 속성은 변경이 가능하다.
const test = {}
test.name = "js"
위 코드를 입력한 후, test를 확인해보면 {name: 'js'}
를 확인할 수 있다.
실제 객체가 변경은 되지만 객체와 변수(test)사이의 바인딩은 변경이 되지 않기 때문에 객체의 속성이 변경 가능
하다.
따라서 const
를 불변 객체
라고 보기는 어렵다.
Object.freeze()
는 객체 속성 변경을 불가하게 만든다.
test = {
name: "js"
}
Object.freeze(test)
test.name = "javascript"
위 코드를 실행한 후 test를 확인해보면 여전히 {name: 'js'}
를 확인할 수 있다.
그러나 Object.freeze()
는 재할당이 가능하다.
불변 객체
는 위 두 가지를 결합하여 사용할 수있다.const test {
name: "js"
}
Object.freeze(test)
위처럼 작업하면 재할당도, 속성 변경도 불가능하다.얕은 복사와 깊은 복사
얕은 복사
는 객체의 참조값(주소 값)
을 복사한다. 주소를 복사하기 때문에, 복사한 변수와 원래 변수가 같은 객체를 가리키고 있다. 따라서 하나를 변경하게 되면 다른 하나도 변경된다.
깊은 복사
는 객체의 실제 값을 복사
한다. 복사한 변수와 원래 변수가 가리키는 객체가 다르기 때문에, 하나를 변경해도 다른 하나에는 영향을 주지 않는다.
스코프, 호이스팅, TDZ
스코프
: 변수, 함수, 클래스가 접근할 수 있는 유효한 범위를 뜻한다.
전역 스코프
라고 하면 어디서든 접근이 가능하고, 지역 스코프
라고 하면 해당 범위 내에서만 접근이 가능하다는 뜻이다.
호이스팅
: 함수의 코드를 실행하기 전에 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 말한다.
호이스팅 때문에, 함수의 선언이 함수의 호출보다 뒤에 있더라도 코드가 실행된다.
TDZ(일시적 사각지대)
pi; // throws `ReferenceError`
const pi = 3.14;
위 코드에서 두 번째 줄 전까지 pi는 TDZ(일시적 사각지대)
에 놓여있다고 한다.
var
, function
, import
의 경우에는 호이스팅이 일어나 호출을 먼저 하고 선언을 해도 문제가 없다. 따라서 위 세 경우는 TDZ의 영향을 받지 않는다
고도 말할 수 있다.
반대로 호이스팅
이 일어나지 않는 let
, const
, class
등은 TDZ의 영향을 받는다
.
함수 선언문과 함수 표현식에서 호이스팅 방식의 차이
함수 선언문
의 경우, function을 사용하므로 함수 전체를 호이스팅한다.
함수 표현식
의 경우, const, let 등을 사용하므로 호이스팅이 일어나지 않는다.
함수 선언문으로 정의한 함수를 사용하는 경우, 변수가 덧씌워지기 쉬운 JS 특성 상 의도하지 않은 결과를 도출할 가능성이 높다.
let, const, var, function
재선언 가능 : var
재선언 불가능 : let, const
재할당 가능 : var, let
재할당 불가능 : const
호이스팅 발생 : var, function
호이스팅 발생 없음 : const, let
실행 컨텍스트와 콜 스택
Execution context(실행 컨텍스트)
: 자바스크립트 코드가 실행되는 환경을 의미한다.
Global Execution context
는 자바스크립트 엔진이 처음 코드를 실행할 때 생성된다. 전역 객체인 Window Object (Node는 Global) 를 생성하고 this가 Window 객체를 가리키도록 한다.
Function Execution context
는 함수가 호출될 때 생성된다. 모든 함수는 호출되는 시점에 자신만의 실행 컨텍스트를 갖게 된다.
실행 컨텍스트가 활성화되는 시점에 아래 세 가지 현상이 발생한다.
호이스팅이 발생한다.
외부 환경 정보를 구성한다.
this 값을 설정한다.
call stack
: 코드가 실행되면서 생성되는 Execution Context를 저장하는 자료구조를 의미한다.
엔진이 처음 스크립트를 실행할 때, Global Execution context
를 call stack
에 push한다. 이후, 함수를 호출할 때마다 그 함수를 위한 Function Execution context
를 call stack
에 push한다. 함수가 종료되면 pop을 하여 call stack
에서 해당 함수의 Function Execution context
를 제거하고, 다음 top에 있는 함수로 이동한다.
스코프 체인과 변수 은닉화
스코프 체인
: 변수를 참조할 때, 변수를 참조하는 코드의 스코프에서 시작하여 상위 스코프로 이동하면서 선언된 변수를 검색한다. 이를 스코프 체인
이라고 한다.
변수 은닉화
: 스코프 체인 때문에, 여러 스코프에 동일한 이름의 변수가 있어도 가장 안쪽에서 검색된 변수만을 참조한다. 이러한 특성을 이용하여, 직접 변경하면 안 되는 변수에 대한 접근을 외부로부터 차단하는 것을 변수 은닉화
라고 한다.
실전문제1
let b = 1;
function hi () {
const a = 1;
let b = 100;
b++;
console.log(a,b);
}
//console.log(a);
console.log(b);
hi();
console.log(b);
위 코드를 실행했을 때, 출력되는 값 예상
1
1 101
1
실제 출력된 값
1
1 101
1
코드 분석
console.log(b); 에서 최상단에 선언한 b 값인 1이 출력된다.
hi(); 에서 함수 hi을 실행한다. a는 상수 1, 변수 b에는 100을 저장하고, b++;을 수행하므로 b가 101로 변경된다. 이후 console.log(a, b)로 '1 101'을 출력한다.
여기에서 선언된 b는 이 함수 스코프 내에서만 존재한다. 함수 실행이 종료되며, 할당된 컨텍스트가 콜스택에서 삭제된다.
console.log(b); 에서 출력하기 위한 b를 검색하면 여전히 최상단에 선언한 b의 값인 1을 출력하게 된다.
console.log(a);의 주석처리를 해제하는 경우
해당 스코프에서 a가 선언되지 않았기 때문에 Reference Error가 발생한다.
이를 해결하기 위해서는 이 문장 전에 a를 선언해야 한다.
실전문제2
1 == "1";
1 === "1";