JavaScript 심화

현성·2023년 12월 8일
0

심볼(Symbol)

  • 변경이 불가한 데이터로, 유일한 식별자를 만들어 데이터를 보호하는 용도로 사용할 수 있습니다.
  • Symbol('설명')
  • '설명'은 단순 디버깅을 위한 용도일 뿐, 심볼 값과는 관계가 없습니다.
const sKey = Symbol("Hello!");
const user = {
  key: "일반 정보!",
  [sKey]: "민감한 정보!",
};

console.log(user.key); // 일반 정보!
console.log(user["key"]); // 일반 정보!
console.log(user[sKey]); // 민감한 정보!
console.log(user[Symbol("Hello!")]); // undefined
console.log(sKey); // Symbol(Hello!)
console.log(typeof sKey); // symbol
  • 예제

keys.js

export const birthKey = Symbol("Date of birth!");
export const emailsKey = Symbol("Emails!");

hs.js

import { birthKey, emailsKey } from "./keys";

export default {
  firstName: "HyunSung",
  lastName: "Lim",
  age: 20,
  [birthKey]: new Date(2004, 9, 10, 17, 30),
  [emailsKey]: ["asd@naver.com", "qwe@gmail.com"],
};

main.js

import hs from "./hs";
import { birthKey, emailsKey } from "./keys";

// 일반적으로 Symbol로 만든 key는 콘솔로 조회할 수 없습니다.
console.log(hs);
console.log(Object.keys(hs));
for (const key in hs) {
  console.log(hs[key]);
}

// 하지만 Symbol데이터를 할당하는 코드가 있는 파일을 직접 가져오면 조회가 가능합니다.
console.log(hs[birthKey]);
console.log(hs[emailsKey]);

BigInt

  • BigInt는 길이 제한이 없는 정수(integer)입니다.
  • 숫자(number) 데이터가 안정적으로 표시할 수 있는, 최대치(2^53 -1)보다 큰 정수를 표현할 수 있습니다.
  • 정수 뒤에 n을 붙이거나 BigInt()를 호출해 생성합니다.
console.log(1234567890123456789012345678901234567890);

console.log(1234567890123456789012345678901234567890n);
console.log(BigInt(1234567890123456789012345678901234567890));

const a = 11n;
const b = 22;

// 숫자 => BigInt
console.log(a + BigInt(b)); // 33n
console.log(typeof (a + BigInt(b))); // bigint

// BigInt => 숫자
console.log(Number(a) + b); // 33
console.log(typeof (Number(a) + b)); // number

불변성 & 가변성

  • 불변성(Immutability)은 생성된 데이터가 메모리에서 변경되지 않습니다.
  • 가변성(Mutability)은 생성된 데이터가 메모리에서 변경될 수 있음을 의미합니다.
  • 자바스크립트 원시형은 불변성을, 참조형은 가변성을 가지고 있습니다.
let a = 1;
let b = a;

b = 2;

console.log(b); // 2
console.log(a); // 1

let a = { x: 1 };
let b = a;

b.x = 2;

console.log(b); // {x: 2}
console.log(a); // {x: 2}

a.x = 7;

console.log(b); // {x: 7}
console.log(a); // {x: 7}

b.x = 1;

console.log(b); // {x: 1}
console.log(a); // {x: 1}

// 일치 연산자는 엄밀히 말해 메모리 주소가 같은 지를 비교합니다.
console.log(a === b); // true

let a = { x: 1 };
let b = { x: 1 };

// 참조형 데이터는 같은 모양이더라도 다른 메모리 주소를 할당받습니다.
console.log(a === b); // false

b.x = 2;

console.log(b); // {x: 2}
console.log(a); // {x: 1}

let a = [1, 2, 3];
let b = a;

b[0] = 4;

console.log(b); // [4, 2, 3]
console.log(a); // [4, 2, 3]

얕은 복사 & 깊은 복사

  • 참조형은 가변성으로 인해, 데이터를 복사할 때 주의가 필요합니다.
  • 얕은 복사(Shallow Copy) : 참조형의 1차원 데이터만 복사
  • 깊은 복사(Deep Copy) : 참조형의 모든 차원 데이터를 복사

객체 얕은 복사

const a = { x: 1 };
// const b = Object.assign({}, a);
const b = { ...a };

b.x = 2;

console.log(b); // {x: 2}
console.log(a); // {x: 1}

객체 깊은 복사 - lodash 설치

  • 실패
const a = { x: { y: 1 } };
const b = { ...a }; // 얕은 복사!

b.x.y = 2;

console.log(b); // {x: {y: 2}}
console.log(a); // {x: {y: 2}}
  • 성공
import cloneDeep from "lodash/cloneDeep";

const a = { x: { y: 1 } };
const b = cloneDeep(a); // 깊은 복사!

b.x.y = 2;

console.log(b); // {x: {y: 2}}
console.log(a); // {x: {y: 1}}

배열 얕은 복사

const a = [1, 2, 3];
// const b = a.concat([]);
const b = [...a];

b[0] = 4;

console.log(b); // [4, 2, 3]
console.log(a); // [1, 2, 3]

배열 깊은 복사

  • 실패
const a = [[1, 2], [3]];
const b = [...a];

b[0][0] = 4;

console.log(b); // [[4, 2], 3]
console.log(a); // [[4, 2], 3]
  • 성공
import cloneDeep from "lodash/cloneDeep";

const a = [[1, 2], [3]];
const b = cloneDeep(a);

b[0][0] = 4;

console.log(b); // [[4, 2], 3]
console.log(a); // [[1, 2], 3]

가비지 컬렉션(GC, Garbage Collection, 쓰레기 수집)

  • 자바스크립트의 메모리 관리 방법으로 자바스크립트 엔진이 자동으로 데이터가 할당된 메모리에서 더 이상 사용되지 않는 데이터를 해제하는 것을 말합니다.

  • 가비지 컬렉션은 개발자가 직접 강제 실행하거나 관리할 수 없습니다.


클로저(Closure)

  • 함수가 선언될 때의 유효범위(렉시컬 범위)를 기억하고 있다가 함수가 외부에서 호출될 때 그 유효범위의 특정 변수를 참조할 수 있는 개념을 말합니다.
function createCount() {
  let a = 0;
  return function () {
    return (a += 1);
  };
}

const count = createCount();

console.log(count()); // 1
console.log(count()); // 2
console.log(count()); // 3

const count2 = createCount();

console.log(count2()); // 1
console.log(count2()); // 2

실제 사용시 유용한 예제

  • 클로저 사용 전
const h1El = document.querySelector("h1");
const h2El = document.querySelector("h2");

// 별도의 상태 관리가 필요!
let h1IsRed = false;
let h2IsRed = false;

h1El.addEventListener("click", () => {
  h1IsRed = !h1IsRed;
  h1El.style.color = h1IsRed ? "red" : "black";
});

h2El.addEventListener("click", () => {
  h2IsRed = !h2IsRed;
  h2El.style.color = h2IsRed ? "red" : "black";
});
  • 클로저 사용 후
const h1El = document.querySelector("h1");
const h2El = document.querySelector("h2");

const createToggleHandler = () => {
  let isRed = false;
  return (event) => {
    isRed = !isRed;
    event.target.style.color = isRed ? "red" : "black";
  };
};

// h1, h2뿐만 아니라 요소를 점점 늘려갈 때도 재사용가능한 코드로 작성해줄 수 있습니다.
h1El.addEventListener("click", createToggleHandler());
h2El.addEventListener("click", createToggleHandler());

메모리 누수(Memory Leak)

  • 더 이상 필요하지 않은 데이터가 해제되지 못하고 메모리를 계속 차지되는 현상입니다.
  • 불필요한 전역 변수 사용
window.hello = "Hello world!";
window.hs = { name: "hs", age: 20 };

  • 분리된 노드 참조
<body>
  <button>Remove!</button>

  <div class="parent">
    <div class="child">1</div>
    <div class="child">2</div>
  </div>
</body>
const btn = document.querySelector("button");
const parent = document.querySelector(".parent");

// 버튼을 계속 눌러봐도 요소는 삭제되었지만, 콘솔을 확인해보면 데이터는 남아있습니다.
btn.addEventListener("click", () => {
  console.log(parent);
  parent.remove();
});

해결 방법

const btn = document.querySelector("button");

btn.addEventListener("click", () => {
  const parent = document.querySelector(".parent");
  console.log(parent); // 두 번째 클릭부터 null
  parent && parent.remove();
});

  • 해제하지 않은 타이머
let a = 0;
setInterval(() => {
  a += 1;
}, 100);

setTimeout(() => {
  console.log(a);
}, 1000);

해결 방법

let a = 0;
const intervalId = setInterval(() => {
  a += 1;
}, 100);

setTimeout(() => {
  console.log(a);
  clearInterval(intervalId);
}, 1000);

  • 잘못된 클로저 사용
const getFn = () => {
  let a = 0;
  return (name) => {
    a += 1; // 메모리 누수 발생
    console.log(a);
    return `Hello ${name}~`;
  };
};

const fn = getFn();
console.log(fn("HS")); // 1 HS
console.log(fn("Neo")); // 2 Neo
console.log(fn("Lewis")); // 3 Lewis

해결 방법

const getFn = () => {
  let a = 0;
  return (name) => {
    return `Hello ${name}~`;
  };
};

const fn = getFn();
console.log(fn("HS")); // HS
console.log(fn("Neo")); // Neo
console.log(fn("Lewis")); // Lewis

콜스택, 테스크 큐, 이벤트 루프

콜스택, 테스크 큐, 이벤트 루프

profile
👈🏻 매일 꾸준히 성장하는 개발자 !

0개의 댓글