-
유용한 자바스크립트 코드 모음집Javascript 2024. 2. 17. 19:06
안녕하세요?
개인적으로 자바스크립트 공부할 때 보는 유용한 코드 모음집입니다.
** 목 차 **
- 문자열을 배열로 분해
- 문자열의 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