ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 유용한 자바스크립트 코드 모음집
    Javascript 2024. 2. 17. 19:06

    안녕하세요?

    개인적으로 자바스크립트 공부할 때 보는 유용한 코드 모음집입니다.

    ** 목 차 **

    문자열을 배열로 분해

    const str = 'string'
    console.log([...str])
    console.log(str.split(''))

    실행 결과

    [ 's', 't', 'r', 'i', 'n', 'g' ]
    [ 's', 't', 'r', 'i', 'n', 'g' ]

    문자열의 for...of

    for (str of 'string') {
      console.log(str)
    }

    실행 결과

    s
    t
    r
    i
    n
    g

    첫 글자를 대문자로, 나머지를 소문자로 변환

    str = 'hELLO'
    console.log(str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase())

    실행 결과

    Hello

    indexOf를 사용한 문자열의 출현 횟수 카운트

    const str = 'Hello World'
    let count = 0
    const keyword = 'o'
    let position = str.indexOf(keyword)
    
    while (position !== -1) {
      count++
      position = str.indexOf(keyword, position + keyword.length)
    }
    console.log(`${count}건 히트`)

    실행 결과

    2건 히트

    문자열을 Unicode 정규화

    Unicode 정규화를 통해 전각과 반각을 넘어서 검색이 가능해진다.

    한국어의 경우 전각과 반각을 구분하는 것이 상대적으로 덜 일반적입니다. 대부분의 한국어 텍스트는 반각 문자로 표현되며, 전각 문자를 사용하는 경우는 드물기 때문입니다.

    그래서 일본어의 경우를 예로 들어보겠습니다.

    normalize 메소드의 구문

    /*
      str: 원래의 문자열
      form: 정규화의 형식 (NFD, NFC, NFKD, NFKC 중 하나)
            인수 생략시 NFC가 기본으로 설정됨
    */
    str.normalize(form)
    const list = ['アイウエオ']
    const type = ['NFD', 'NFC', 'NFKD', 'NFKC']
    
    for (let t of type) {
      for (let l of list) {
        console.log(`${t}: ${l} => ${l.normalize(t)}`)
      }
    }

    실행 결과

    ---------- NFD ----------
    アイウエオ => アイウエオ
    abcABC => abcABC
    123 => 123
    ---------- NFC ----------
    アイウエオ => アイウエオ
    abcABC => abcABC
    123 => 123
    ---------- NFKD ----------
    アイウエオ => アイウエオ
    abcABC => abcABC
    123 => 123
    ---------- NFKC ----------
    アイウエオ => アイウエオ
    abcABC => abcABC
    123 => 123

    NaN의 비교

    '=='나 '===' 연산자로 NaN을 비교하면 올바른 결과를 얻을 수 없으므로, Number 객체의 isNaN 메소드를 사용한다.

    // 항상 false가 된다
    console.log(NaN === NaN)
    // NaN을 올바르게 비교하려면 isNaN 메소드를 사용한다
    console.log(Number.isNaN(NaN))

    실행 결과

    false
    true

    Number 타입의 포맷

    const num = 1234.567
    const fmt = new Intl.NumberFormat('ja-JP', {
      style: 'currency',
      currency: 'JPY',
      currencyDisplay: 'symbol'
    })
    console.log(fmt.format(num))

    실행 결과

    ¥1,235

    min ~ max의 난수

    난수 범위가 50 ~ 100

    const min = 50
    const max = 100
    console.log(Math.floor(Math.random() * (max - min + 1)) + min)

    실행 결과

    55

    난수 범위가 0 ~ 100

    console.log(Math.floor(Math.random() * 101))

    실행 결과

    66

    배열에서 임의의 요소를 가져오기

    const list = [1, 2, 3, 4, 5]
    console.log(list[Math.floor(Math.random() * list.length)])

    실행 결과

    5

    월의 마지막 날

    console.log(new Date(2024, 2, 0).toLocaleDateString())

    실행 결과

    2024/2/29

    날짜 차이

    const dt1 = new Date(2000, 0, 1)
    const dt2 = new Date(2000, 0, 15)
    const diff = (dt2.getTime() - dt1.getTime()) / (1000 * 60 * 60 * 24)
    console.log(`${diff}일의 차이가 있다`)

    실행 결과

    14일의 차이가 있다

    날짜/시간 값을 문자열로 변환

    toLocalexxxString() 메소드는 현재 환경의 지역 정보에 따라 최적의 형식으로 날짜/시간을 문자열화한다.

    const dt = new Date(2000, 0, 1, 12, 15, 30, 45)
    
    console.log(dt)
    
    console.log(dt.toLocaleString())
    
    console.log(dt.toLocaleDateString())
    
    console.log(dt.toLocaleTimeString())
    
    console.log(dt.toISOString())
    
    console.log(dt.toDateString())
    
    console.log(dt.toJSON())

    실행 결과

    // 변환 없음
    Sat Jan 01 2000 12:15:30 GMT+0900 (한국 표준시)
    
    // toLocaleString
    2000/1/1 12:15:30
    
    // toLocaleDateString
    2000/1/1
    
    // toLocaleTimeString
    12:15:30
    
    // toISOString
    2000-01-01T03:15:30.045Z
    
    // toDateString
    Sat Jan 01 2000
    
    // toJSON
    2000-01-01T03:15:30.045Z

    Date 타입의 포맷

    const dt = new Date(2000, 0, 1, 12, 15, 30, 45)
    const fmt = new Intl.DateTimeFormat('ko-KR', {
      year: 'numeric',
      month: 'short',
      day: '2-digit',
      weekday: 'long',
      hour12: true,
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit',
      dayPeriod: 'short'
    })
    console.log(fmt.format(dt))

    실행 결과

    2000년 1월 01일 토요일 오후 12:15:30

    레이블 문법의 break

    loop:
    for (let i = 0; i < 10; i++) {
      if (i === 1) {
        break loop
      }
      console.log(i)
    }

    실행 결과

    0
    label2: {
      label1: {
        console.log('label1 block')
        break label2
      }
      console.log('label2 block')
    }

    실행 결과

    label1 block

    배열의 인덱스

    앞에서 본 경우의 인덱스는 0부터 시작: 0, 1, 2, ...
    뒤에서 본 경우의 인덱스는 -1부터 시작: ..., -2, -1

    끝에서 요소를 가져오기

    const list = [0, 1, 2, 3, 4, 5]
    
    for(let i = -1; i > -list.length; i--) {
      console.log(list.at(i))
    } 
    5
    4
    3
    2
    1

    배열의 요소 가져오기

    대괄호 문법과 at 메소드 두 가지 패턴이 있습니다.
    at 메소드는 음의 인덱스를 지원합니다.

    let list = [1, 2, 3, 4, 5]
    // 대괄호 문법
    console.log(list[0])
    // at 메소드
    console.log(list.at(-1))

    실행 결과

    1
    5

    배열에 여러 요소 추가/교체/삭제

    splice 메소드의 구문

    /*
      list: 원래의 배열
      start: 시작 위치
      count: 요소 수
      items: 교체 후의 요소 (가변 길이 인수)
    */ 
    list.splice(start, count, items)

    요소 추가

    const list = [0, 1, 2, 3, 4, 5]
    console.log(list.splice(3, 0, 'x', 'y', 'z'))
    console.log(list)

    실행 결과

    0,1,2,x,y,z,3,4,5

    요소 교체

    const list = [0, 1, 2, 3, 4, 5]
    console.log(list.splice(3, 2, 'x', 'y', 'z'))
    console.log(list)

    실행 결과

    3,4
    0,1,2,x,y,z,5

    요소 삭제

    const list = [0, 1, 2, 3, 4, 5]
    console.log(list.splice(3, 2))
    console.log(list)
    
    const list2 = [0, 1, 2, 3, 4, 5]
    console.log(list2.splice(3))
    console.log(list2)

    실행 결과

    // list
    3,4
    0,1,2,5
    
    // list2
    3,4,5
    0,1,2

    배열의 모든 요소 위치 검색

    const list = [0, 1, 2, 3, 4, 5, 1]
    const keyword = 1
    const result = []
    
    list.forEach((value, index) => {
      if (value === keyword) {
        result.push(index)
      }
    })
    console.log(result)

    실행 결과

    [ 1, 6 ]

    중첩 배열 평탄화

    flat 메소드의 구문

    /*
      list: 원래의 배열
      depth: 평탄화할 계층 (기본값은 1), 불특정 계층의 경우 Infinity를 지정
    */
    list.flat(depth)
    const list = [0, 1, 2, 3, 4, 5, [6, 7, [8, 9]], [10]]
    console.log(list.flat())
    console.log(list.flat(Infinity))

    실행 결과

    [ 0, 1, 2, 3, 4, 5, 6, 7, [ 8, 9 ], 10 ]
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    다차원 배열의 요소 수 얻기

    const list = [
      [1, 2, 3],
      [4, 5, 6],
      [7, 8, 9]
    ]
    console.log(list.flat().length)

    실행 결과

    9

    배열 내의 요소 이동

    배열 내의 지정된 요소를 동일한 배열의 다른 위치로 복사

    copyWithin 메소드의 구문

    /*
      list: 원래의 배열
      target: 이동 대상 위치
      start: 복사 시작 위치
      end: 복사 종료 위치
    */
    copyWithin(target, start, end)
    console.log([0, 1, 2, 3, 4, 5].copyWithin(3, 4, 6))
    console.log([0, 1, 2, 3, 4, 5].copyWithin(1, 2))
    console.log([0, 1, 2, 3, 4, 5].copyWithin(2))
    console.log([0, 1, 2, 3, 4, 5].copyWithin(3, -6, -3))

    실행 결과

    [ 0, 1, 2, 4, 5, 5 ]
    [ 0, 2, 3, 4, 5, 5 ]
    [ 0, 1, 0, 1, 2, 3 ]
    [ 0, 1, 2, 0, 1, 2 ]

    배열 같은 객체를 배열로 변환

    배열 같은 객체란, 배열처럼 보이지만 배열이 아닌 객체를 말합니다.
    Map, Set, HTMLCollection/NoList, arguments, String이 해당됩니다.

    from 메소드의 구문

    /*
      obj: 배열 같은 객체
      mapFn: 값 변환에 사용하는 함수
      thisArg: 인수 mapFn에서 this가 나타내는 값
    */
    Array.from(obj, mapFn, thisArg)

    DOM에서 요소 가져오기

    <form>
      <select id="select-box">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
      </select>
    </form>
    const opts = Array.from(document.querySelector('#select-box').options)
    opts.forEach((opt) => {
      console.log(opt.value)
    })

    실행 결과

    1
    2
    3

    초기값을 가진 배열 생성

    인덱스로 초기화

    const list = Array.from(
      { length: 5 },
      (value, index) => {
        return index
      }
    )
    console.log(list)

    실행 결과

    [ 0, 1, 2, 3, 4 ]

    모든 요소를 고정값으로 초기화

    const data = new Array(5)
    data.fill('-', 0, data.length)
    console.log(data)

    실행 결과

    [ '-', '-', '-', '-', '-' ]

    특정 범위의 연속 번호 생성

    const begin = 10
    const end = 20
    const step = 3
    
    const list = Array.from(
      { length: (end - begin) / step + 1 },
      (value, index) => {
        return begin + (index * step)
      }
    )
    console.log(list)

    실행 결과

    [ 10, 13, 16, 19 ]

    배열의 얕은 복사

    from 메소드

    const list = [0, 1, [0, 1, 2]]
    const copy = Array.from(list)
    copy[2][0] = 100
    console.log(list)

    실행 결과

    [ 0, 1, [ 100, 1, 2 ] ]

    스프레드 구문

    const list = [0, 1, [0, 1, 2]]
    const copy = [...list]
    copy[2][0] = 100
    console.log(list)

    실행 결과

    [ 0, 1, [ 100, 1, 2 ] ]

    slice 메소드

    const list = [0, 1, [0, 1, 2]]
    const copy = list.slice()
    copy[2][0] = 100
    console.log(list)

    실행 결과

    [ 0, 1, [ 100, 1, 2 ] ]

    concat 메소드

    const list = [0, 1, [0, 1, 2]]
    const copy = list.concat()
    copy[2][0] = 100
    console.log(list)

    실행 결과

    [ 0, 1, [ 100, 1, 2 ] ]

    배열을 숫자 순으로 정렬

    sort 메소드의 구문

    /*
      list: 원래의 배열
      m, n: 비교할 요소
      statements: 비교 규칙
    */
    list.sort(function(m, n) {
       ...statements...
    })

    오름차순

    const list = [5, 25, 10].sort((m, n) => {
      return m - n
    })
    console.log(list)

    실행 결과

    [ 5, 10, 25 ]

    내림차순

    const list = [5, 25, 10].sort((m, n) => {
      return n - m
    })
    console.log(list)

    실행 결과

    [ 25, 10, 5 ]

    배열을 임의의 규칙으로 재정렬하기

    const levels = ['one', 'two', 'three']
    const list = [
      { name: 'name0', level: 'three' },
      { name: 'name1', level: 'two' },
      { name: 'name2', level: 'one' },
      { name: 'name3', level: 'one' },
      { name: 'name4', level: 'three' },
      { name: 'name5', level: 'two' },
    ]
    const result = list.sort((m, n) => {
      return levels.indexOf(m.level) - levels.indexOf(n.level)
    })
    console.log(result)
    실행 결과
    [
      { name: 'name2', level: 'one' },
      { name: 'name3', level: 'one' },
      { name: 'name1', level: 'two' },
      { name: 'name5', level: 'two' },
      { name: 'name0', level: 'three' },
      { name: 'name4', level: 'three' }
    ]

    배열을 무작위로 재정렬하기

    const list = [0, 1, 2, 3, 4].sort(() => {
      // -0.5 ~ 0.5 범위의 난수
      return Math.random() - 0.5
    })
    console.log(list)
    실행 결과
    [ 2, 1, 3, 0, 4 ]

    배열의 내용을 순차적으로 처리하기

    forEach 메소드의 구문

    /*
      list: 원본 배열
      value: 인덱스 값
      index: 원본 배열
      array: 원본 배열
      statements: 요소에 대한 처리, function과 화살표 함수에서 thisArg의 처리가 다름
      thisArg: 콜백 함수에서 this가 나타내는 값
    */
    list.forEach(function(value, index, array) {
       ...statements...
    }, thisArg)
    const obj = { x: 0, y: 1, z: 2 }
    const list = ['x', 'y']
    
    list.forEach(function(value) {
      console.log(this[value])
    }, obj)

    실행 결과

    0
    1

    배열을 지정된 규칙으로 가공하기

    map 메소드의 구문

    /*
      list: 원본 배열
      value: 요소 값
      index: 인덱스 값
      array: 원본 배열
      statements: 요소에 대한 처리 (반환 값은 가공 후의 값)
      thisArg: 콜백 함수에서 this가 나타내는 값
    */
    list.map(function(value, index, array) {
       ...statements...
    }, thisArg)
    const list = [1, 2, 3]
    const result = list.map((value) => {
      return value * 10
    })
    console.log(result)

    실행 결과

    [ 10, 20, 30 ]

    flatMap (map + flat) 메소드

    map과 flat 메소드의 조합 처리보다는 약간이나마 효율적이다.

    const list = [1, 2, ,3, null]
    const result = list.flatMap((value) => {
      // 평탄화에 의한 공백 열은 제거되므로 제거하고 싶은 요소는 빈 배열을 반환
      if (value === null) {
        return []
      }
      return [value * 10, value * 100]
    })
    console.log(result)

    실행 결과

    [ 10, 100, 20, 200, 30, 300 ]

    임의의 조건식에 따라 배열을 검색하기

    find 메소드의 구문

    /*
      list: 원본 배열
      value: 요소 값
      index: 인덱스 값
      array: 원본 배열
      statements: 요소 값을 판정하기 위한 처리 (반환 값은 true/false)
      thisArg: 콜백 함수에서 this가 나타내는 값
    */
    list.find(function(value, index, array) {
       ...statements...
    }, thisArg)
    const list = [
      { name: 'name0', level: 0 },
      { name: 'name1', level: 1 },
      { name: 'name2', level: 2 },
    ]
    
    let result = list.find((value) => {
      // 판정 결과가 true가 된 요소를 가져온다
      return value.name.includes('0')
    })
    console.log(result)
    
    // 화살표 함수의 생략 형태
    result = list.find(value => value.name.includes('0'))
    console.log(result)

    실행 결과

    { name: 'name0', level: 0 }
    { name: 'name0', level: 0 }

    findIndex 메소드

    인덱스 값을 가져오고 싶을 때 사용한다.
    구문은 find 메소드와 같다.

    const list = [
      { name: 'name0', level: 0 },
      { name: 'name1', level: 1 },
      { name: 'name2', level: 2 },
    ]
    const index = list.findIndex(value => value.name.includes('0'))
    console.log(index)

    실행 결과

    0

    조건식에 일치하는 요소가 존재하는지를 판정하기

    some 메소드의 구문

    일치하는 요소가 1개라도 존재하는 경우에 사용한다.

    /*
      list: 원본 배열
      value: 요소 값
      index: 인덱스 값
      array: 원본 배열
      statements: 요소 값을 판정하기 위한 처리 (반환 값은 true/false)
      thisArg: 콜백 함수에서 this가 나타내는 값
    */
    list.some(function(value, index, array) {
       ...statements...
    }, thisArg)
    const list = [
      { name: 'name0', level: 0 },
      { name: null, level: null },
      { name: 'name1', level: 1 },
      { name: 'name2', level: 2 },
    ]
    let result = list.some(value => value.level > 1)
    console.log(result)
    
    // 임의의 조건으로 중단
    result = list.some((value) => {
      if (value.name === null) {
        return true
      }
      console.log(value)
    })
    console.log(result)

    실행 결과

    true
    { name: 'name0', level: 0 }
    true

    every 메소드의 구문

    모든 요소가 일치하는 경우에 사용한다.
    구문은 some 메소드와 같다.

    const list = [
      { name: 'name0', level: 0 },
      { name: 'name1', level: 1 },
      { name: 'name2', level: 2 },
    ]
    const result = list.every(value => value.level >= 0)
    console.log(result)

    실행 결과

    true

    배열에서 조건에 맞는 요소만 가져오기

    filter 메소드의 구문

    /*
      list: 원래의 배열
      value: 요소 값
      index: 인덱스 값
      array: 원래의 배열
      statements: 요소 값을 판단하기 위한 처리 (반환 값은 true/false)
      thisArg: 콜백 함수에서 this가 나타내는 값
    */
    list.filter(fuction(value, index, array) {
       ...statements...
    }, thisArg)
    const list = [
      { name: 'name0', level: 0 },
      { name: 'name1', level: 1 },
      { name: 'name2', level: 2 },
    ]
    const result = list.filter(value => value.level > 0)
    console.log(result)

    실행 결과

    [ { name: 'name1', level: 1 }, { name: 'name2', level: 2 } ]

    배열의 요소를 순서대로 처리해서 하나로 묶기

    reduce 메소드의 구문

    /*
      list: 원래의 배열
      result: 이전까지의 결과
      value: 요소 값
      index: 인덱스 값
      array: 원래의 배열
      statements: 요소 값에 대한 처리
      initial: 초기 값, 첫 번째 루프에서 result에 전달하는 값
    */
    list.reduce(fuction(result, value, index, array) {
       ...statements...
    }, initial)
    const list = [0, 1, 2]
    // result 인수에 초기 값을 설정하는 경우: 초기 값으로 100을 설정
    let result = list.reduce((result, value) => {
      return result + value
    }, 100)
    console.log(result)
    
    // result 인수에 초기 값을 설정하지 않는 경우: 초기 값으로 배열의 0번째 값이 설정됨
    result = list.reduce((result, value) => result + value)
    console.log(result)

    실행 결과

    103
    3

    reduceRight의 구문

    reduce 메소드가 왼쪽에서 오른쪽 방향으로 연산하는 반면, reduceRight 메소드는 오른쪽에서 왼쪽으로 연산한다.
    구문은 reduce 메소드와 같다.

    const list = [
      [0, 1],
      [2, 3],
      [4, 5],
    ]
    let result = list.reduce((result, value) => result.concat(value))
    console.log(result)
    
    result = list.reduceRight((result, value) => result.concat(value))
    console.log(result)

    실행 결과

    // reduce
    [ 0, 1, 2, 3, 4, 5 ]
    
    // reduceRight
    [ 4, 5, 2, 3, 0, 1 ]

    for문에서의 분해 할당

    리스트

    const list = [['x', 0], ['y', 1], ['z', 2]]
    
    for (const [value1, value2] of list) {
      console.log(value1, value2)
    }

    실행 결과

    x 0
    y 1
    z 2

    객체

    const map = new Map([
      ['x', 0],
      ['y', 1],
      ['z', 2]
    ])
    
    // for...of의 map은 map.entries()의 생략
    for (const [key, value] of map) {
      console.log(key, value)
    }

    실행 결과

    x 0
    y 1
    z 2

    const obj = { x: 0, y: 1, z: 2 }
    
    for (const [key, value] of Object.entries(obj)) {
      console.log(key, value)
    }

    실행 결과

    x 0
    y 1
    z 2

    Map의 메소드 체인

    const map = new Map()
    console.log(map.set('x', 0).set('y', 1).set('z', 2))

    실행 결과

    Map(3) { 'x' => 0, 'y' => 1, 'z' => 2 }

    Map의 가져오기 키가 존재하지 않는 경우의 기본 값 처리

    const map = new Map([
      ['x', 0],
      ['y', 1],
      ['z', 2],
    ])
    console.log(map.has('xxx') ? map.get('xxx') : 'xxx')

    실행 결과

    xxx

    Map의 안티 패턴

    map에 대해 괄호 구문으로 값을 저장하지 않는다.
    괄호 구문으로 값을 설정한 경우는, get 메소드로 값을 가져올 수 없다.

    const map = new Map()
    map['x'] = 0
    console.log(map['x'])
    // 괄호 구문으로 값을 저장하면 get 메소드를 사용하여 값을 가져올 수 없다
    console.log(map.get('x'))

    실행 결과

    0
    undefined

    Map의 배열화

    const map = new Map([
      ['x', 0],
      ['y', 1],
      ['z', 2],
    ])
    // 키를 배열화
    console.log(Array.from(map.keys()))
    // 값을 배열화
    console.log(Array.from(map.values()))
    // 키/값을 배열화
    console.log(Array.from(map.entries()))

    실행 결과

    [ 'x', 'y', 'z' ]
    [ 0, 1, 2 ]
    [ [ 'x', 0 ], [ 'y', 1 ], [ 'z', 2 ] ]

    Map의 내용을 순서대로 처리

    forEach 메소드의 구문

    /*
      dic: 원래의 맵
      value: 요소 값
      key: 키 값
      map: 원래의 맵
      statements: 요소 값에 대한 처리
      thisArg: 콜백 함수에서 this가 나타내는 값
    */
    dic.forEach(fuction(value, key, map)) {
       ...statements...
    }, thisArag)
    const map = new Map([
      ['x', 0],
      ['y', 1],
      ['z', 2],
    ])
    map.forEach((value, key) => {
      console.log(`${key}: ${value}`)
    })

    실행 결과

    x: 0
    y: 1
    z: 2

    Object와 Map의 상호 변환

    Object를 Map으로 변환

    const map = new Map([
      ['x', 0],
      ['y', 1],
      ['z', 2],
    ])
    const obj = Object.fromEntries(map)
    console.log(obj)

    실행 결과

    { x: 0, y: 1, z: 2 }

    Map을 객체로 변환

    const obj = {
      x: 0,
      y: 1,
      z: 2,
    }
    const map = new Map(Object.entries(obj))
    console.log(map)

    실행 결과

    Map(3) { 'x' => 0, 'y' => 1, 'z' => 2 }

    WeakMap - 약한 참조 키의 Map

    약한 참조란, Map 외에서 키가 참조되지 않게 되면, 가비지 컬렉션의 대상이 된다는 것이다.
    WeakMap에서는 키(객체)가 파괴되면 함께 값도 파괴되므로, 메모리 누수를 해결할 수 있다.

    WeakMap의 제한

    • 키는 참조형이어야 한다
    • get, set, has, delete 메소드만 사용할 수 있다

    사용 용도

    주요 용도로는, 객체의 부수적인 데이터를 관리하기 위해 사용한다. 예를 들어, 객체에 대한 접근 횟수를 감시하는 등.

    Map에서 키(객체)를 삭제

    let obj = {}
    const map = new Map()
    map.set(obj, 'object')
    // 객체를 파괴
    obj = null
    // Map에 저장한 객체는 파괴되지 않고, 살아남는다
    // 사라져야 할 객체가 사라지지 않아서 메모리 누수의 원인이 된다
    console.log(map.size)

    실행 결과

    1

    WeakMap에서 키(객체)를 삭제

    let obj = {}
    const map = new WeakMap()
    map.set(obj, 'object')
    // 객체를 파괴
    obj = null
    // WeakMap에서도 키가 파괴된다
    console.log(map.size)

    실행 결과

    undefined

    WeakMap을 활용한 클래스

    class WeakMapClass {
      // 초기화
      constructor(init) {
        this._weakMap = new WeakMap(init)
      }
       // 존재 체크
      has(key) {
        return this._weakMap.has(key)
      }
      // 저장
      set(key, value) {
        this._weakMap.set(key, value)
        return this
      }
      // 가져오기
      get(key) {
        return this._weakMap.get(k)
      }
      // 삭제
      delete(key) {
        return this._weakMap.delete(key)
      }
      // 전체 삭제
      clear() {
        this._weakMap = new WeakMap()
      }
    }

    배열의 중복 삭제

    indexOf와 filter 메소드

    배열 내에서 가장 처음 일치하는지 여부로 판단하여, 중복을 제거한다.

    const list = [0, 1, 2, 0, 1, 2, 3]
    const result = list.filter((value, index) => {
      return list.indexOf(value) === index
    })
    console.log(result)

    실행 결과

    [ 0, 1, 2, 3 ]

    Set

    const set = new Set([0, 1, 2, 0, 1, 2, 3])
    // from 메소드로 배열화
    console.log(Array.from(set))
    // 스프레드 구문으로 배열화
    console.log([...set])

    실행 결과

    [ 0, 1, 2, 3 ]
    [ 0, 1, 2, 3 ]

    Set의 메소드 체인

    const set = new Set()
    set.add(0).add(1).add(2).add(0)
    console.log(set)

    실행 결과

    Set(3) { 0, 1, 2 }

    Object 리터럴을 문자열로 변환

    console.log(`{"x":0,"y":1,"z":2}`)

    실행 결과

    {"x":0,"y":1,"z":2}

    Object와 JSON 문자열의 상호 변환

    객체를 JSON 문자열로 변환

    const obj = {
      x: 0,
      y: 1,
      z: 2,
    }
    console.log(JSON.stringify(obj))

    실행 결과

    {"x":0,"y":1,"z":2}

    JSON 문자열을 객체로 변환

    const json = `{"x":0,"y":1,"z":2}`
    console.log(JSON.parse(json))

    실행 결과

    { x: 0, y: 1, z: 2 }

    JSON 문자열을 객체로 변환 - Date형 복원

    const obj = {
      str: 'string',
      date: new Date()
    }
    // obj.date는 문자열로 복원된다
    const jsonString = JSON.stringify(obj)
    console.log(jsonString)
    
    // jsonString.date를 Date형으로 변환
    const parsedObj = JSON.parse(jsonString, (key, value) => {
      if (typeof(value) === 'string' &&
          value.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/)){
        return new Date(value)
      }
      return value
    })
    console.log(parsedObj)

    실행 결과

    {"str":"string","date":"2024-02-05T14:41:29.981Z"}
    { str: 'string', date: 2024-02-05T14:41:29.981Z }

    eval 함수

    eval 함수는 주어진 문자열을 코드로 평가하여 실행한다.
    eval 함수를 사용하기보다는 다른 방법을 사용할 수 없는지 검토하는 것이 좋다. 대부분, 대안책은 준비되어 있다.
    다음의 이유로 남용은 피하는 것이 좋다.

    • 사용자의 입력 내용을 eval에 주면, 임의의 스크립트를 자유롭게 실행할 수 있게 되는 가능성이 있다
    • 일반적인 코드보다는 성능이 나쁘고, 처리 속도가 느리다
    • eval('console.log("eval 함수")')
      

    let data = 'data'
    // { var obj = data } 와 같은 의미
    eval(var obj = ${data})
    console.log(obj)

    // 객체의 접근 프로퍼티의 전환
    const obj = { x: 0, y: 1, z: 2 }
    const prop = 'x'
    eval(console.log(obj.${prop}))

    
    실행 결과
    ```javascript
    eval 함수
    data
    0

    RegExp 객체를 생성

    RegExp 객체의 생성은 다음의 2패턴이 있다.

    • RegExp 객체의 생성자를 이용한다
    • 정규 표현 리터럴을 이용한다
    /*
      patter: 정규 표현 패턴
      opts: 동작 옵션
    */
    new RegExp(pattern, opts) // 생성자
    /pattern/opts // 리터럴
    옵션 개요
    d 매치한 위치를 기록한다
    g 글로벌(전체)에 대해 매치한다
    i 대문자/소문자를 구분한다
    m 여러 줄에 대응한다
    s '.'가 개행 문자에 일치하도록 한다
    u Unicode에 대응한다, 서로게이트 페어 대책 등에서 사용한다
    y lastIndex 프로퍼티로 지정한 위치에서 매치한다

    RegExp 생성자로 정규 표현 객체를 받는다

    RegExp의 생성자는 정규 표현 리터럴을 받을 수 있다.

    console.log(new RegExp(/[0-9]/, 'i'))
    /[0-9]/i

    문자열이 정규 표현 패턴에 매치했는지 판정

    const pattern = /^[0-9]{3}-[0-9]{4}$/
    
    const str1 = '012-3456'
    console.log(pattern.test(str1))
    
    const str2 = '0123-4567'
    console.log(pattern.test(str2))

    실행 결과

    true
    false

    정규 표현 패턴에 매치한 문자열을 가져오기

    match 메소드는 글로벌(전체) 매치의 유효/무효에 따라 결과가 달라진다.

    |글로벌(전체) 매치| 결과| 서브매치 문자열|
    |-|-|
    |유효| 매치한 모든 문자열 |포함하지 않는다|
    |무효| 매치한 첫 번째 문자열 |포함한다|

    const str = `zip code1: 012-3456
                 zip code2: 123-4567`
    const pattern1 = /[0-9]{3}-[0-9]{4}/g
    for (const result of str.match(pattern1)) {
      console.log(result)
    }
    
    const pattern2 = /([0-9]{3})-([0-9]{4})/
    console.log(str.match(pattern2))

    실행 결과

    // 글로벌(전체) 매치 유효, 서브매치 없음
    012-3456
    123-4567
    
    // 글로벌(전체) 매치 무효, 서브매치 있음
    [
      '012-3456',
      '012',
      '3456',
      index: 11,
      input: 'zip code1: 012-3456\n             zip code2: 123-4567',
      groups: undefined
    ]

    서브매치의 그룹에 이름을 붙이기

    const str = `zip code1: 012-3456
                 zip code2: 123-4567`
    const pattern = /(?<city>[0-9]{3})-(?<local>[0-9]{4})/
    const result = str.match(pattern)
    console.log(result.groups.city, result.groups.local)

    실행 결과

    012 3456

    정규 표현의 매치 결과를 모아서 가져오기

    matchAll 메소드는 글로벌(전체) 매치하는 모든 문자열과 서브매치 문자열 등을 결과로 반환한다.

    const str = `zip code1: 012-3456
                 zip code2: 123-4567`
    const pattern = /([a-z].*)([0-9]{3}-[0-9]{4})/g
    
    for (const result of str.matchAll(pattern)) {
      console.log(result)
    }

    실행 결과

    [
      'zip code1: 012-3456',
      'zip code1: ',
      '012-3456',
      index: 0,
      input: 'zip code1: 012-3456\n             zip code2: 123-4567',
      groups: undefined
    ]
    [
      'zip code2: 123-4567',
      'zip code2: ',
      '123-4567',
      index: 33,
      input: 'zip code1: 012-3456\n             zip code2: 123-4567',
      groups: undefined
    ]

    레거시 환경

    matchAll 메소드는 ES2020에서 추가된 메소드이므로, IE 같은 레거시 브라우저에서는 사용할 수 없다.
    레거시 환경에서 matchAll 메소드와 같은 동작을 구현한 경우는 exec 메소드를 사용한다.
    exec 메소드는, 다음의 특징을 가진다.

    • 글로벌(전체) 검색과 관계없이, 실행 결과는 항상 1개
    • 서브매치 문자열, 확장 프로퍼티 등의 정보를 포함한다
    • 마지막에 매치한 문자열의 위치를 기록한다 (다음에는 이전의 문자열 위치에서 검색을 재개한다)
    • 매칭 문자열이 존재하지 않는 경우는 반환 값을 null
    const str = `zip code1: 012-3456
                 zip code2: 123-4567`
    const pattern = /[0-9]{3}-[0-9]{4}/g
    
    while ((result = pattern.exec(str)) !== null) {
      console.log(result)
    }

    실행 결과

    [
      '012-3456',
      index: 11,
      input: 'zip code1: 012-3456\n             zip code2: 123-4567',
      groups: undefined
    ]
    [
      '123-4567',
      index: 44,
      input: 'zip code1: 012-3456\n             zip code2: 123-4567',
      groups: undefined
    ]

    정규 표현으로 문자열을 바꾸기

    replace 메소드의 구문

    /*
      str: 바꿀 대상 문자열
      pattern: 정규 표현
      rep: 바꾼 후의 문자열
      match: 매치한 문자열
      p1, p2, p3...: 서브매치 문자열 (그룹의 수에 따라 변동)
      offset: 매치한 문자열의 위치
      string: 검색 대상의 문자열
    */
    str.replace(pattern, rep, fuction(match, p1, p2, p3..., offset, string)) {
       ...statements...
    }
    변수 개요
    $& 매치한 부분 문자열
    $` 매치한 부분 문자열의 바로 앞의 문자열
    $' 매치한 부분 문자열의 바로 뒤의 문자열
    $1 〜 100 서브매치 문자열
    $$ 달러 기호
    const str = 'tel number: 00-111-2222'
    const pattern = /(\d{1,2})-(\d{2,4})-(\d{3,4})/g
    console.log(str.replace(pattern, '$1($2)$3'))
    // 정규 표현에서 g 옵션을 지정하지 않은 경우는 매치한 첫 번째 문자열만을 바꾼다
    console.log(str.replace(/\d/, 'x'))

    실행 결과

    tel number: 00(111)2222
    tel number: x0-111-2222

    replcaeAll 메소드

    모든 매칭 문자열을 바꾸려면, replcae 메소드 대신에 replaceAll 메소드를 사용할 수도 있다.
    replaceAll 메소드를 사용하면 다음의 장점이 있다.

    • 모든 매칭 문자열을 바꾸는 것이 의도가 명확해진다
    • 글로벌(전체) 매치하기 위한 g 옵션이 지정되어 있지 않은 경우는, 에러가 된다
    const str = 'tel number: 00-111-2222'
    const pattern = /(\d{1,2})-(\d{2,4})-(\d{3,4})/g
    console.log(str.replaceAll(pattern, '$1($2)$3'))
    console.log(str.replaceAll(/\d/g, 'x'))

    실행 결과

    tel number: 00(111)2222
    tel number: xx-xxx-xxxx

    콜백 함수를 이용한 바꾸기

    const str = 'tel number: 00-111-2222'
    const pattern = /\D/g
    const result = str.replace(pattern, (match) => {
      return match.toUpperCase()
    })
    console.log(result)

    실행 결과

    TEL NUMBER: 00-111-2222

    정규 표현으로 문자열을 분할

    const pattern = /[\/\./-]/g
    console.log('1970-1-1'.split(pattern))
    console.log('1970/1/1'.split(pattern))
    console.log('1970.1.1'.split(pattern))

    실행 결과

    [ '1970', '1', '1' ]
    [ '1970', '1', '1' ]
    [ '1970', '1', '1' ]

    정규 표현 - 후방 참조

    후방 참조는, 같은 정규 표현에서 이전에 캡처한 것을 참조하는 것이다.

    const str = 'tel number: 00-00-00'
    // \1: 캡처 그룹의 번호
    const pattern1 = /(\d{1,2})-\1-\1/g
    // 이름 붙은 캡처 그룹
    const pattern2 = /(?<num>\d{1,2})-\k<num>-\k<num>/g
    console.log(str.match(pattern1))
    console.log(str.match(pattern2))

    실행 결과

    [ '00-00-00' ]
    [ '00-00-00' ]

    정규 표현 - 캡처 그룹을 무효화

    (?:...) 로 매치 패턴을 둘러싸는 것으로, 그룹을 서브매치의 대상에서 제외할 수 있다.

    const str = 'xyz0123'
    // 캡처 있는 그룹핑
    const pattern1 = /^([a-z0-9]{3})(\d{1,4})$/
    // 캡처 없는 그룹핑
    const pattern2 = /^(?:[a-z0-9]{3})(\d{1,4})$/
    console.log(str.match(pattern1))
    console.log(str.match(pattern2))

    실행 결과

    // 캡처 있는 그룹핑
    [
      'xyz0123',
      'xyz',
      '0123',
      index: 0,
      input: 'xyz0123',
      groups: undefined
    ]
    
    // 캡처 없는 그룹핑
    [ 
      'xyz0123',
      '0123',
      index: 0,
      input: 'xyz0123',
      groups: undefined
    ]

    정규 표현 - 앞뒤의 문자열의 유무에 따른 매치 판정

    정규 표현의 선행, 후행은 앞뒤의 문자열이 존재하는지 여부로 문자열을 매치할지 판정하기 위한 표현이다.
    선행, 후행은 다음의 4종류가 있다.

    정규 표현 개요
    X(?=Y) 긍정 선행: X의 바로 뒤에 Y가 이어지는 경우는 X에 매치한다
    X(?!Y) 부정 선행: X의 바로 뒤에 Y가 이어지지 않는 경우는 X에 매치한다
    (?<=Y)X 긍정 후행: X의 바로 앞에 Y가 있는 경우는 X에 매치한다
    (?<!Y)X 부정 후행: X의 바로 앞에 Y가 없는 경우는 X에 매치한다
    // 긍정 선행
    console.log(str.match(/xyz(?=0)/g))
    // 부정 선행
    console.log(str.match(/xyz(?!3)/g))
    // 부정 선행
    console.log(str.match(/(?<=z)0123/g))
    // 부정 후행
    console.log(str.match(/(?<!x)0123/g))

    실행 결과

    // 긍정 선행
    [ 'xyz' ]
    
    // 부정 선행
    [ 'xyz' ]
    
    // 부정 선행
    [ '0123' ]
    
    // 부정 후행
    [ '0123' ]

    Unicode 프로퍼티로 특정 문자열을 가져오기

    Unicode 프로퍼티란 Unicode에서 정의한 문자에 대해 할당된 속성(프로퍼티)의 것이다.
    정규 표현 패턴의 안에서 Unicode 프로퍼티를 사용할 수 있도록 한 것이 Unicode 프로퍼티 이스케이프라는 구문이다.
    정규 표현 패턴에서 다음과 같이 사용한다.

    // 'sc='는 프로퍼티의 분류를 의미한다
    // 'gc='를 생략할 수 있는데 생략한 경우는 다음과 같다 'gc=Letter' -> 'Letter'
    /[\p{sc=Han}]+/gu

    다음과 같은 프로퍼티가 있다.

    프로퍼티 개요
    \p{Letter}, \p{L} 문자
    \p{Punctuation}, \p{P} 구두점
    \p{Uppercase_Letter}, \p{Lu} 영대문자 (전각, 반각)
    \p{Lowercase_Letter}, \p{Ll} 영소문자 (전각, 반각)
    \p{Number}, \p{N} 반각 / 전각 숫자 (로마 숫자도 포함)
    \p{Nd} 반각 / 전각 숫자 (10진수)
    \p{Space_Separator}, \p{Zs} 공백
    \p{sc=Hangul}+/gu 한글
    \p{sc=Hiragana}, \p{sc=Hira} 히라가나
    \p{sc=Katakana}, \p{sc=Kana} 카타카나
    \p{sc=Han} 한자
    const str = 'STRINGもじれつ文字列한글'
    // 영대문자
    console.log(str.match(/[\p{Lu}]+/gu))
    // 한글
    console.log(str.match(/[\p{sc=Hangul}]+/gu))
    // 히라가나
    console.log(str.match(/[\p{sc=Hira}]+/gu))
    // 한자
    console.log(str.match(/[\p{sc=Han}]+/gu))

    실행 결과

    [ 'STRING' ]
    [ '한글' ]
    [ 'もじれつ' ]
    [ '文字列' ]

    유니코드 한글 프로퍼티

    초성은 Hangul_Syllable_Type=Leading_Jamo 또는 HST=L
    중성은 Hangul_Syllable_Type=Vowel_Jamo 또는 HST=V
    종성은 Hangul_Syllable_Type=Trailing_Jamo 또는 HST=T
    그리고 완성된 한글 음절은 Hangul_Syllable_Type=LV_Syllable 또는 HST=LV로 표현할 수 있습니다.

    // 초성만 있는 문자열 /[\p{HST=L}]+/gu
    
    // 중성만 있는 문자열 /[\p{HST=V}]+/gu
    
    // 종성만 있는 문자열 /[\p{HST=T}]+/gu
    
    // 완성된 한글 음절만 있는 문자열 /[\p{HST=LV}]+/gu
    
    // 한글 자모나 음절을 포함하는 문자열 /[\p{Script=Hangul}]+/gu

    끝.

Designed by Tistory.