누구나 알고있지만 한번 더 정리해보는 Javascript 문자열

Tei·2021년 5월 26일
1
post-thumbnail

String Vs 프리미티브 방식

  • 큰 차이는 없습니다. 하지만 String 방식으로 선언하면 새로운 요소를 할당할 수 있습니다.
  • 하지만 이것이 유용한 경우는 별로 없습니다(...)
{
  let stringWithString = new String("string1");  
  stringWithString.newValue = "123" // 요게 가능
  return stringWithString.newValue
}

{
  let strigWithTemplete = "string2"
  strigWithTemplete.newValue = "456" // 에러! 
  return strigWithTemplete.newValue
}

태그드 템플릿

  • 템플릿 리터럴에서 구문 분석을 위한 함수를 만들 수 있습니다.
  • 가령 사용자가 입력한 비밀번호를 * 처리한다고 생각해봅시다.
// censor 
const censor = (str) => "*".repeat(str.length)

// 이렇게 모든 변수마다 censor 를 불러줘야 함.
let pwInput = `this is my pw ${censor(pw)} and secret ket is ${censor(sk)}`;
  • 만약 비밀로 해야하는 값이 1000개가 있다면, 모두 censor 로 감싸주는것은 비효율적일 것입니다.
  • 이를 자동화 하는 태그드 템플릿 함수를 만들어보겠습니다.
const pw = "12345"
const sk="abc987"
let t1 = censorTemplete`this is my pw ${pw} and secret ket is ${sk}`

const censorTemplete = (str,...arg) => {
  
  // str = ["this is my pw ", " and secret ket is ", ""]
  // arg = [12345,abc987]
  
  return str.reduce(
    (acc,e,i) => `${acc}${e}${censor(arg[i]||'')}`
  ,'')
}
  • str 은 문자열이 ${} 을 기준으로 잘려진 배열 형태로 전달됩니다.
  • 나머지 인자를 rest 파라매터로 받으면, 항상 str 배열의 크기는 arg 배열보다 하나 큰 것이 보장됩니다.
  • 태그드 템플릿 함수의 리턴값은, 새로운 문자열입니다.

RAW 문자열

  • String.raw 는 기본적으로 제공되는 태그드 함수의 일종입니다
  • 이스케이프 문자를 해석하지 않습니다.
'hello\nWorld' 
String.raw`Hello\nWorld` // Hello\nWorld
  • 이는 이스케이프로 쓰이지 않은 \ 을 쉽게 처리하게 해줍니다
String.raw`D:\Tei's Folder\tei_file.txt`
  • 하지만 마지막을 이스케이프 문자로 끝낼수는 없습니다. 이 경우 마지막 ` 이 이스케이프되어 문자열이 제대로 닫기지 않는 문제가 발생합니다.
String.raw`D:\Tei's Folder\tei_file.txt\`
  • 참고로 템플릿 리터럴에서 줄바꿈 전에 이스케이프 문자를 쓰면, 줄바꿈을 이스케이프합니다.
let result = `line1 \
    line2`

// "line1 line2"

문자열의 합성

  • +연산자나 += 연산자 사용가능
let str1 = "123"
let str2 = str1 + "456"
str1 += "456"
  • str.concat 은 성능상의 이유로 사용하지 않는것을 권장합니다.

유니코드

  • 자바스크립트에서 유니코드는 여러 문제를 발생시킬 수 있습니다...
  • 유니코드란 코드포인트 라고 하는 숫자랑 문자를 연결해주는 것입니다.
  • 코드포인트는 최소 네자리를 가지는 16진수로 표현하고 앞에 U+를 더하는 형식이다.
  • 코드포인트 값은 U+0000 ~ U+10FFFF입니다.
  • https://apps.timwhitlock.info/emoji/tables/unicode#block-6a-additional-emoticons
  • 위 링크에서 확인할 수 있는데, 😈 의 유니코드 값은 U+1F608 입니다.

그래서 무슨 문제가?

console.log("😈".length) // 2
console.log("😈".split("")) // ["�", "�"]
console.log("😈".charAt(0))// � 
console.log("😈"[0]) // � 
  • 자바스크립트는 문자열을 다룰때 이스케이스 시퀀스 방식을 사용합니다(UTF-16 코드 단위).

  • 이스케이프 시퀀스의 예시는 \uFF21 같이 이스케이프문자 + 유니코드입니다.

  • 하지만 문제는 이렇게 다룰 수 있는 범위는 \u0000~\u00FF 라는것입니다...

  • U+1F608 인 악마는 이 범위를 초과해버리는 것이죠...

  • 이렇게 표현할 수 없는 유니코드는 두개로 쪼개서 대리 쌍 (surrogate pairs)이라는 표로 구하게 됩니다.

  • 이 대리쌍 때문에 정말정말정말정말 많은 문제가 발생합니다...(length, 뒤집기, 문자열 찾기,정규식)

[..."😈"].length // 1
  • 그나마 es6 문법에서는 많은것이 개선되긴 했습니다.
for (let elem of '😈') {
	console.log(elem)
}
// for of 는 대리쌍이 아니라 온전한 코드포인트로 순회해줌.
  • 하지만 유니코드(특히 빈번하게 만날 수 있는게 이모지....) 가 포함된 문자열을 처리할 때 문제를 해결하기 쉽지 않으니 유니코드를 처리해야 한다면 유의...
  • 유니코드 입력을 가공해야 한다면 먼저 기획자를 잘 설득해보시고 설득이 통하지 않는다면 이슈가 발생하지 않길 기도합시다.

같은 Base 문자열 비교

let resume = 'Résumé'
console.log(a.toUpperCase()) // RÉSUMÉ 인데 원래는 RESUME 으로 씀.
  • 이때 'RÉSUMÉ'RESUME 을 equal 비교시 false 가 나오게 됩니다.

  • 이럴떄 localeCompare 를 사용할 수 있습니다.

  • localeCompare 는 문자열의 대소를 반환하는 함수 (어떤 문자열이 더 앞인지)

'a'.localeCompare('c'); // a 가 c 보다 이전이기때문에 음수.

'a'.localeCompare('a'); // 같으면 0
  • localCompare 함수는 민감도 옵션을 줄 수 있는데, 'base" 옵션을 줄 경우, 같은 베이스 문자를 가지고 있는 문자를 동등하게 판단합니다.
let resume = 'Résumé'
let resume2 = 'RESUME'
console.log(resume.localeCompare(resume2, 'en', { sensitivity: 'base' })) // 0, 같음
  • 이를 통해 Base 문자가 같은 문자를 같게 판단할 수 있음. 이는 정렬에서도 활용 가능한데 다음 챕터에서 알아보도록 하겠습니다.

정렬

  • 문자열의 정렬은 Array 의 "sort" 메소드로 보통 하게 됩니다.
  • 앞서 보았듯, 자바스크립트는 UTF-16 코드 단위로 문자열을 다루고, 문자열의 비교도 이 값을 통해 하게됩니다.
  • 대문자의 UTF-16 코드 값은 항상 소문자보다 작습니다.
  • 즉 대문자가 섞인 문자열을 소팅하면 항상 [대문자로시작, 소문자] 가 되게 됩니다.
console.log(["Tei", "yaguun", "no"].sort());
//["Tei", "no", "yaguun"]
  • localeCompare 를 통해 제대로 정렬할 수 있습니다.
console.log(["Tei", "yaguun", "no"].sort((a, b) => a.localeCompare(b)))
// ["no", "Tei", "yaguun"]

패딩

  • pad 계열 함수를 통해 문자열을 패딩할 수 있습니다
  • 디자이너가 아... 이거 왜 안맞아요? 맞춰주세요 할 때 유용합니다.
console.log("short".padStart(12))      // "       short"
console.log("longlonglong".padEnd(12)) // "longlonglong"
profile
Being a service developer

0개의 댓글