JavaScript의 자료형과 JavaScript만의 특성
느슨한 타입(loosely typed)의 동적(dynamic) 언어
자바스크립트는 정적 언어와 달리 변수에 타입이 없어 프로그램을 실행하는 도중에 변수에 저장되는 데이터 타입을 동적으로 변경할 수 있다.
이 때문에 자바스크립트가 느슨한 타입의 언어 혹은 동적 언어라 불린다.
타입을 명시하지 않아도 되기 때문에 개발 속도가 향상된다는 장점이 있지만 코드가 길고 복잡해지는 경우 타입 에러를 찾기가 어려워진다는 단점도 있다.
또한 타입 때문에 비교 시에 동등 연산자(==)와 일치 연산자(===)를 구별하여 써야 한다.
동등 연산자는 타입에 상관없이 같으면 true를 리턴하고, 0 과 false를 구분하지 못한다.
true == 1; // true
false == 0; // true
'' == false // true
'1' == 1 // true
반면에 일치 연산자는 타입까지 비교하여 값을 리턴해준다.
true === 1; // false
false === 0; // false
'' === false // false
'1' === 1 // false
이러한 자바스크립트의 타입 문제 때문에 나온 것이 바로 타입스크립트이다.
타입스크립트를 사용함으로써 자바스크립트의 버그 중 15%를 미리 예방할 수 있다는 연구가 있다.
또한, 데이터의 타입을 지정해주면 코드를 좀 더 직관적으로 만들어주며 개발자들은 로직과 같은 큰 구조들에만 집중할 수 있게 된다.
undefined와 null의 차이점
undefined : 변수를 선언하고 값을 할당하지 않은 상태. 자료형이 없는 상태
null : 변수를 선언하고 빈 값을 할당한 상태(빈 객체)이다.
따라서 typeof를 통해 자료형을 확인해보면 null은 object로, undefined는 undefined가 출력되는 것을 확인할 수 있다.
typeof null // 'object'
typeof undefined // 'undefined'
null === undefined // false
null == undefined // true
null === null // true
null == null // true
!null // true
isNaN(1 + null) // false
isNaN(1 + undefined) // true
JavaScript 객체와 불변성
자바스크립트에는 기본적으로 8가지의 자료형이 있다. 하나의 데이터만을 담을 수 있는 원시형 7가지(Number, String, Boolean, Null, Undefined, BicInt, Symbol)와 객체이다.
객체는 참조형 데이터로 원시형과 달리 다양한 데이터를 여러 개 담을 수 있다.
불변 객체 / 얕은 복사와 깊은 복사
불변 객체란 객체 내부의 프로퍼티들을 변경할 수 없도록 되어있는 객체를 뜻한다.
이러한 불변 객체가 필요한 이유는 원하지 않는 객체의 값마저 변경될 수 있기 때문이다.
뿐만 아니라 객체에 변화를 가해도 원본이 그대로 남아있어야 하는 경우에도 불변 객체가 필요하다.
이를 위해서는 깊은 복사가 필요하다.
얕은 복사는 객체의 참조값 즉, 주소값을 복사하고 깊은 복사는 객체의 실제 값을 복사한다.
얕은 복사
const a = {
name: 'ann',
age: 27,
};
const b = a;
b.name = 'sally';
console.log(a); // { name: 'sally', age: 27 } 출력
console.log(b); // { name: 'sally', age: 27 } 출력
위와 같이 주소값을 복사하여 b에 넣어주면 이는 얕은 복사라 할 수 있다.
깊은 복사 - JSON.parse / JSON.stringify
const p1 = {
age: 20,
name: {
first: 'ann',
last: 'kim'
},
sayHello: () => {
console.log('hello world!');
},
};
const p2 = JSON.parse(JSON.stringify(p1));
p1.age = 30;
p1.name.first = 'justin';
console.log(p1); // { age: 30, name: { first: "Justin", last: "Jung" }, sayHello: f };
console.log(p2); // { age: 20, name: { first: "Jessie", last: "Jung" } };
깊은 복사 - 재귀함수
function deepCopy(obj) {
if (obj === null || typeof obj !== "object") {
return obj;
}
let copy = {};
for (let key in obj) {
copy[key] = deepCopy(obj[key]);
}
return copy;
}
const obj = {
a: 1,
b: {
c: 2,
},
func: function () {
return this.a;
},
};
const newObj = deepCopy(obj);
newObj.b.c = 3;
console.log(obj); // { a: 1, b: { c: 2 }, func: [Function: func] }
console.log(obj.b.c === newObj.b.c); // false
깊은 복사된 객체는 객체 안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체이다.
Object.assign() 메소드, spread 연산자 둘 다 완벽하게 Deep copy는 되지 않는다. (1depth 까지만 가능. 즉 다차원 배열에서는 사용이 불가능 하다.)
따라서 확실한 깊은 복사 방법에는 JSON.parse / JSON.stringify, 재귀함수, Lodash 라이브러리 사용 등의 방법이 있다.
호이스팅과 TDZ
스코프와 호이스팅, 그리고 TDZ
스코프
- 스코프란 범위를 의미한다. 자바스크립트에서만 국한된 개념이 아닌 컴퓨터 공학 등 여러 곳에서 범위라는 의미로 쓰인다.
- 스코프는 중괄호(블록) 또는 함수에 의해 나뉜다. 이는 각가 Block Scope와 Function Scope 라 불린다.
- 스코프는 중첩이 가능하며 가장 바깥쪽의 스코프는 전역 스코프, 전역이 아닌 다른 스코프는 모두 지역 스코프이다.
- 안쪽 스코프는 바깥쪽 스코프에 접근할 수 있지만 그 반대는 불가능하다.
호이스팅
- 호이스팅이란 함수 안에 있는 선언들을 모두 끌어올려서 해당 함수 유효 범위의 최상단에 선언하는 것이다.
- 자바스크립트의 함수는 자바스크립트의 파서가 실행되면서 내부적으로 모든 함수들이 호이스팅 된다.
- 자바스크립트의 var 변수 선언 또한 호이스팅이 된다.
console.log(a) // undefined
var a = 'a'
console.log(b) // Error: Uncaught ReferenceError: b is not defined
let b = 'b'
console.log(c) // Error: Uncaught ReferenceError: c is not defined
const c = 'c'
위와 같이 var로 선언하면 undefined 라는 값이 에러 없이 잘 뜨지만 나머지 let과 const는 에러가 뜬다.
이는 var a 가 호이스팅 되면서 변수의 선언이 먼저 일어났기 때문이다.
TDZ
- TDZ는 Temporal Dead Zone의 약어로, 사각지대라는 뜻이다. 즉, 할당하기 전에는 사용할 수 없다.
- 호이스팅 되지 않는 let, const, class에는 접근이 불가능한 TDZ가 존재한다.
- 위 호이스팅 코드 예시에서 let과 const의 콘솔에서 접근하지 못하는 구간(에러가 발생하는 구간)이 존재하는데 이것이 바로 TDZ이다.
실행 컨텍스트와 콜 스택
- 실행 컨텍스트란 자바스크립트 엔진이 코드를 실행하기 위해서 필요한 코드들에 대한 정보로 코드가 실행되는 위치를 설명한다. 더 자세히 말하자면, 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
- 자바스크립트 파일이 열리는 순간 전역 실행 컨텍스트가 생성되고 가장 먼저 콜 스택에 쌓인다.
- 콜 스택은 출입구가 하나뿐인 우물 같은 데이터 구조이다.
- 자바스크립트가 함수 호출을 기록하기 위해 사용하며, 항상 맨 위에 놓인 함수를 우선으로 실행한다.
스코프 체인, 클로저
- 스코프 체인(Scope Chain)이란 일종의 리스트로서 각각의 스코프가 어떻게 연결(chain) 되고 있는지 보여주는 것을 말한다.
- 클로저의 경우 잘못 사용하면 메모리 누수 현상이 일어날 수 있다.
- 그러나 class처럼 사용하고 싶을 때, 정보의 은닉화 등의 장점들이 있기 때문에 메모리 누수 문제만 해결할 수 있다면 클로저를 사용하는 것이 좋다.
실습
let b = 1;
function hi () {
const a = 1;
let b = 100;
b++;
console.log(a,b);
}
console.log(a);
console.log(b); //1
hi(); //함수호출
console.log(b); //1
콘솔에 찍힐 b 값을 예상해보기
- 자바스크립트는 위에서부터 아래로 차례대로 읽는다.
- 따라서 가장 먼저호출되는 console.log 함수가 a의 값을 불러온다.
- console.log(a) 에러 발생 ➡ const로 선언된 a는 hi 함수 안에서 선언되었기 때문에 함수블록 안에서만 사용 가능하다.
- console.log(b) 1 ➡ 처음 let으로 선언된 b는 1이라는 값을 할당받았으므로 콘솔창에 b를 호출하면 1이 나온다.
- h1() ➡ function hi() 함수를 호출한다.
- console.log(a, b) 1, 101 ➡ hi 함수 안에서 선언된 값인 a와 재할당된 b의 값을 출력한다.
- console.log(b) 1 ➡ 어떠한 변화도 없었으므로 위의 콘솔과 마찬가지로 이미 할당된 b의 값인 1이 출력된다.
'개발 > 프론트엔드' 카테고리의 다른 글
[NextJS]App Router - 라우팅(Routing) 종류 (0) | 2024.07.05 |
---|---|
[NextJS] 14버전 앱 라우터에 대해 알아보자 (0) | 2024.06.25 |
[WIL] ReactJS 함수형과 클래스형 (0) | 2022.08.07 |
[WIL] ReactJS 기초 (1) | 2022.07.31 |
[WIL] JavaScript의 ES란? ES5/ES6 문법 차이 (0) | 2022.07.24 |