[REAL Deep Dive into JS] 32. String

young_pallete·2022년 10월 11일
0

REAL JavaScript Deep Dive

목록 보기
33/46
post-custom-banner

사실... 메서드를 다루는 챕터들이 가장 힘든 것 같아요.
이걸 어디까지 알아야 하고, 설명해야 할지 셈하기 힘들기 때문이에요.
저의 경우는, 대개 제게 필요한 메서드만 골라서 쓰는 편이에요.
따라서 모든 메서드를 다루지 않으니, 양해 바라요!

🚦 본론

생성자 함수, String

표준 빌트인 객체이며, 문자열 리터럴의 래퍼 객체로써 기능합니다.
이때, 래퍼 객체에는 [[StringData]]([[PrimitiveValue]])라는 내부 슬롯이 존재하며, 해당 슬롯에 들어간 값이 문자열 값을 의미합니다.

const a =  new String();
console.log(a)
// length: 0, [[Prototype]]: String, [[PrimitiveValue]]: ""

만약 new를 쓰지 않는다면 어떻게 될까요?
이는 명시적으로 string type으로 치환한 값을 반환해주는 함수로써 기능하게 됩니다.

console.log(String(true)) // 'true'
console.log(String(Infinity)) // 'Infinity'

Iterable Object

String은 정수 타입을 프로퍼티 키로 갖습니다. 이와 mapping된 프로퍼티 값은 해당 인덱스 번째에 위치한 문자입니다.

즉, 문자열은 마치 배열 형식으로 인덱스를 접근할 수 있게 된 객체로 유사 배열 객체의 형태를 띕니다. 또한, iterable한 객체로써 기능하여 spread 연산자 역시 사용할 수 있습니다.

const helloWorldStr = 'Hello, World!';

// 프로퍼티 키를 통해 인덱스에 위치한 문자 값을 가져올 수 있습니다.
// console.log(helloWorldStr[5]) // 출력 값: ,

// 이터러블하므로 spread 연산자를 사용 가능합니다.
// ['H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!']
console.log([...helloWorldStr])

read only Object

String은 래퍼 객체로 기능할 때, 자신을 직접 변경시키는 메서드가 존재하지 않습니다.
언제나 새로운 원시 값을 갖게 되는 문자열 타입의 특성을 보존하기 위함입니다.

이를 프로퍼티 어트리뷰트를 통해 확인이 가능합니다.

// writable하지 않기 때문에, 항상 새로운 문자열을 생성한다는 것을 알 수 있습니다.
const helloWorldStr = 'Hello, World!';
Object.getOwnPropertyDescriptors(helloWorldStr)
/* 
0: {value: 'H', writable: false, enumerable: true, configurable: false}
1: {value: 'e', writable: false, enumerable: true, configurable: false}
2: {value: 'l', writable: false, enumerable: true, configurable: false}
3: {value: 'l', writable: false, enumerable: true, configurable: false}
4: {value: 'o', writable: false, enumerable: true, configurable: false}
5: {value: ',', writable: false, enumerable: true, configurable: false}
6: {value: ' ', writable: false, enumerable: true, configurable: false}
7: {value: 'W', writable: false, enumerable: true, configurable: false}
8: {value: 'o', writable: false, enumerable: true, configurable: false}
9: {value: 'r', writable: false, enumerable: true, configurable: false}
10: {value: 'l', writable: false, enumerable: true, configurable: false}
11: {value: 'd', writable: false, enumerable: true, configurable: false}
12: {value: '!', writable: false, enumerable: true, configurable: false}
length: {value: 13, writable: false, enumerable: false, configurable: false}
[[Prototype]]: Object
*/

즉, 모든 문자열이 변경 불가능한 값을 갖고 있으므로, 인덱스를 통해 값을 수정할 수 없습니다.
String 래퍼 객체로 생성한 값은 오직, helloWorldStrlet으로 바꿔 재할당을 해주는 것만이 가능한 read only 객체의 성질을 띄고 있습니다.

String.prototype.indexOf

인덱스를 찾는 기능을 합니다. 만약 없을 경우는 -1을 반환하므로, 이에 대한 방어코드를 작성할 수도 있겠군요. 😉

const str = ...
if (str.indexOf(...) > -1) {
  ... // 인덱스 검색에 성공할 경우의 로직
} else {
  ... // 인덱스 검색에 실패할 경우의 로직.
}

String.prototype.includes

문자열이 주어진 문자열을 포함하는지에 대한 여부를 검사합니다.

const keyword = '1234'
const value = '12345678'

console.log(value.includes(keyword)) // true;

String.prototype.startsWith

보통 어떤 특정 데이터에 prefix가 있을 때 쓰는 편입니다.

const residentRegistrationNumber = '940915-1234567'
if (residentRegistrationNumber.startsWith('9')) {
    console.log('90년대생')
}

String.prototype.endsWith

그렇다면, 이건 suffix가 있을 때 쓰겠죠? 😉

그런데, 사실... 이 모든 것들이 정규표현식을 알고 있다면 쉽게 알 수 있기는 합니다 😭
다만, 코드의 양이 줄어들기는 하겠군요!

let schoolType;
const school = '삼각산초등학교'
if (school.endsWith('초등학교')) {
    schoolType = '초등학교'
}

String.prototype.subString, String.prototype.slice

둘의 기능은 비슷합니다. [시작 인덱스, 끝 인덱스 + 1]를 넣으면 됩니다.
다만, 차이라면 slice는 좀 더 섬세하게 만들어진 느낌이 듭니다.

예시를 들어볼까요?
slice는 두 인자의 대수비교가 잘못되면 검색 결과를 허용하지 않습니다.

const helloWorldStr = 'Hello, World!';

console.log(helloWorldStr.substring(5,1)) // 5와 1을 치환해준 다음 다시 검색합니다 - ello 
console.log(helloWorldStr.slice(5,1)) // 애초부터 잘못 들어왔다고 판단하여 결과를 반환하지 않습니다 - undefined

하지만, slice의 경우 음수 인덱스 검색에서는 더욱 유연한 형태를 제공합니다.

console.log(helloWorldStr.substring(-5)) // substring은 인수가 0보다 작거나 NaN이면 0으로 취급합니다. - Hello, World!
console.log(helloWorldStr.slice(-5)) // 뒤에서부터 5개를 반환합니다. - orld!

// 음수 검색 시에도 2번째 인자를 전달할 수 있습니다.
// 이때, 2번째 인자는 끝 인덱스 -1를 의미합니다.
console.log(helloWorldStr.slice(-5, -1)) // 뒤에서부터 5번째부터 2번째까지를 반환합니다. - orld

String.prototype.trim

보통 개행 문자를 처리할 때, 앞 뒤로 불필요한 공백이 들어가는 경우가 있습니다.
이럴 때 사용합니다.

const originalString = " 안녕하세요! ".trim()
console.log(originalString) // '안녕하세요!'

String.prototype.repeat

어떤 특정문자를 반복할 때 사용합니다.
보통은 잘 사용하지 않았던 기억이 있네요.
코테 때 가끔 사용합니다... (크흡...)

예전에는 간단하게 프로젝트할 때, 별점 같은 걸 처리할 때 쓰고는 했는데,
별점 같은 것도 보통, 아이콘으로 쓰는 게 더 이쁘니까요.

const tabSpace = ' '.repeat(4);
const codes = ['안녕하세요', '현재 String 공부 중입니다.'];

console.log('const codes = `');
console.log(...codes.map(v => `\n${tabSpace}${v}`));
console.log('`');
/*
 const codes = `
     안녕하세요 
     현재 String 공부 중입니다.
 `

String.prototype.toLowerCase, String.prototype.toUpperCase

이름에서 드러나듯이, 모두 대문자로 변경하거나, 소문자로 변경할 때 사용합니다.
꽤 적지 않게 사용하는 편이니, 알아두시는 게 좋을 거 같아요!

const helloWorldStr = 'Hello, World!';

console.log(helloWorldStr.toUpperCase()) // HELLO, WORLD!
console.log(helloWorldStr.toLowerCase()) // hello, world!

String.prototype.replace

얘는 진~짜 많이 사용합니다. 알아두시는 게 좋아요.
String의 부족한 한계점을 이 친구가 채워줍니다.

문자열은 앞서 말하였듯, 원시값으로 변경이 불가능하며 재할당만 가능합니다.
따라서, 특정 인덱스 문자를 직접 바꿀 수 없는데요.

이때, String.prototype.replace는 패턴이나 문자열에서 일치하는 경우를 특정 문자열로 바꿔줍니다.

// 패턴으로 일치하는 문자열을 변경 가능합니다.
const helloWorldStr = 'Hello, World!';
console.log(helloWorldStr.replace(/world/i, 'Jengyoung')) // Hello, Jengyoung!

// 문자열로도 가능합니다. 다만 문자열은 하나만 바꿔줍니다.
// 만약 여러 개를 바꾸고 싶다면, `String.prototype.replaceAll`을 참고해주세요. (ES2021)
console.log(helloWorldStr.replace('World', 'Jengyoung')) // Hello, Jengyoung!
console.log(helloWorldStr.replaceAll('o', 'O')) // HellO, WOrld!

String.prototype.split

이 친구도 많이 사용합니다.
대개 특정 문자열을 구분자로 하여 배열로 만들 때 많이 사용해요!

const objectKeys = ['name', 'age', 'job']
const applyMessage = '황재영/29/개발자'
console.log(applyMessage.split('/').reduce((acc, cur, idx) => {
	acc[objectKeys[idx]] = cur
    return acc
}, {}))

// {name: '황재영', age: '29', job: '개발자'}

(추가) String.prototype.at()

책에는 안 나와있지만, 필요한 분들이 있으실 것 같아 추가했습니다.
인덱스 검색할 때, 음수인 정수는 검색이 불가능한 것이 기존 자바스크립트 인덱스 검색의 한계였는데요.

ES6/ES7에서는 String.prototype.at이 검토되기 시작하여 사용할 수 있습니다.
마치 파이썬처럼 음수 인덱스 검색이 가능해진 것이죠!

const helloWorldStr = 'Hello, World!';

// charAt은 음수 인덱스 검색이 불가능합니다.
console.log(helloWorldStr.charAt(-1)) // ''

// at 메서드는 음수 인덱스 검색이 가능합니다.
console.log(helloWorldStr.at(-1)) // !

🌈 마치며

후우... 단순한 내용이었는데 꽤나 많네요.
그렇지만 원시 스트링 객체에 대해 좀 더 알게 되기도 했고, 몰랐던 메서드들을 알아가게 되었네요.

생각보다 string 값을 처리할 프로젝트는 굉장히 많습니다. 저 역시도 최근에 몇 개의 유틸 함수와 string 관련한 컴포넌트를 만들어봤는데요!
이쪽을 좀 더 공부하셔서 어-썸한 프로젝트 하나 만드시는 것도 추천드리고 싶네요.

그럼 즐거운 코딩하시길 바라며, 이상! 🌈

profile
People are scared of falling to the bottom but born from there. What they've lost is nth. 😉
post-custom-banner

0개의 댓글