안녕하세요?
개인적으로 자바스크립트 공부할 때 보는 유용한 코드 모음집입니다.
** 목 차 **
- 문자열을 배열로 분해
- 문자열의 for...of
- 첫 글자를 대문자로, 나머지를 소문자로 변환
- indexOf를 사용한 문자열의 출현 횟수 카운트
- 문자열을 Unicode 정규화
- NaN의 비교
- Number 타입의 포맷
- min ~ max의 난수
- 배열에서 임의의 요소를 가져오기
- 월의 마지막 날
- 날짜 차이
- 날짜/시간 값을 문자열로 변환
- Date 타입의 포맷
- 레이블 문법의 break
- 배열의 인덱스
- 배열에 여러 요소 추가/교체/삭제
- 배열의 모든 요소 위치 검색
- 중첩 배열 평탄화
- 다차원 배열의 요소 수 얻기
- 배열 내의 요소 이동
- 배열 같은 객체를 배열로 변환
- from 메소드의 구문
- 초기값을 가진 배열 생성
- 배열의 얕은 복사
- 배열을 숫자 순으로 정렬
- 배열을 임의의 규칙으로 재정렬하기
- 배열을 무작위로 재정렬하기
- 배열의 내용을 순차적으로 처리하기
- 배열을 지정된 규칙으로 가공하기
- 임의의 조건식에 따라 배열을 검색하기
- 조건식에 일치하는 요소가 존재하는지를 판정하기
- 배열에서 조건에 맞는 요소만 가져오기
- 배열의 요소를 순서대로 처리해서 하나로 묶기
- reduceRight의 구문
- for문에서의 분해 할당
- Map의 메소드 체인
- Map의 가져오기 키가 존재하지 않는 경우의 기본 값 처리
- Map의 안티 패턴
- Map의 배열화
- Map의 내용을 순서대로 처리
- Object와 Map의 상호 변환
- WeakMap - 약한 참조 키의 Map
- 배열의 중복 삭제
- Set의 메소드 체인
- Object 리터럴을 문자열로 변환
- Object와 JSON 문자열의 상호 변환
- RegExp 객체를 생성
- 문자열이 정규 표현 패턴에 매치했는지 판정
- 정규 표현 패턴에 매치한 문자열을 가져오기
- 정규 표현의 매치 결과를 모아서 가져오기
- 레거시 환경
- 정규 표현으로 문자열을 바꾸기
- 정규 표현으로 문자열을 분할
- 정규 표현 - 후방 참조
- 정규 표현 - 캡처 그룹을 무효화
- 정규 표현 - 앞뒤의 문자열의 유무에 따른 매치 판정
- Unicode 프로퍼티로 특정 문자열을 가져오기
- 유니코드 한글 프로퍼티
문자열을 배열로 분해
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
끝.
'Javascript' 카테고리의 다른 글
Binary Large Object - Blob이란? (1) | 2024.02.27 |
---|---|
미래의 React는 아마도 SvelteJS처럼 컴파일될겁니다 (1) | 2024.02.24 |
프론트엔드 디벨로퍼를 위한 웹 기술의 역사 간단하게 이해하기 (0) | 2024.02.13 |
JavaScript의 Promise, 그것이 알고싶다 (0) | 2024.02.13 |
TypeScript에서 webpack과 Babel의 필요성을 역사적 관점에서 본다 (1) | 2024.02.13 |