본문 바로가기

자바스크립트

자료구조와 자료형

javascript_자료구조와자료형

원시값의 메서드

원시값

  • 원시형의 종류는 문자(string), 숫자(number), bigint, boolean, symbol, null, undefined 총 7가지

객체

  • 프로퍼티에 다양한 종류의 값을 저장할 수 있습니다.
  • {name: 'kim', age:1} 대괄호 {}를 사용해 만들 수 있음. 함수도 객체의 일종.

원시값을 객체처럼 사용하기

  • 문자열이나 숫자와 같은 원시값을 다루어야 하는 작업이 많은데, 메서드를 사용하면 작업을 수월하게 할 수 있을 것 같다는 생각이 들었음.
  • 그런데 원시값은 가능한 한 빠르고 가벼워야 함.

해결책

  1. 원시값은 원시값 그대로 남겨둬 단일 값 형태를 유지합니다.
  2. 문자열, 숫자, 불린, 심볼의 메서드와 프로퍼티에 접근할 수 있도록 언어 차원에서 허용합니다.
  3. 이를 가능하게 하기 위해, 원시값이 메서드나 프로퍼티에 접근하려 하면 추가 기능을 제공해주는 특수한 객체, "원시 래퍼 객체(object wrapper)" 를 만들어줍니다. 이 객체는 곧 삭제됩니다.
let str = "hello";
alert(str.toUpperCase());
  1. 문자열 str은 원시값이므로 원시값의 프로퍼티(toUpperCase)에 접근하는 순간 특별한 객체가 만들어집니다. 이 객체는 문자열의 값을 알고 있고, toUpperCase() 와 같은 유용한 메서드를 가지고 있습니다.
  2. 메서드가 실행되고, 새로운 문자열이 반환됩니다.
  3. 특별한 객체는 파괴되고, 원시값 str만 남습니다.
let str = "hello";
str.test = 5; //(*)
alert(str.test);

// 결과

  1. undefined(비 엄격 모드)
  2. an error(엄격 모드)

// (*) 로 표시한 줄에서 무슨 일이 일어났는지

  1. str의 프로퍼티에 접근하려 하면 "래퍼 객체"가 만들어집니다.
  2. 엄격 모드에선 래퍼 객체를 수정하려 할 때 에러가 발생합니다.
  3. 비 엄격 모드에선 에러가 발생하지 않습니다. 래퍼 객체에 프로퍼티 test가 추가되죠. 그런데 래퍼 객체는 바로 삭제되기 때문에 마지막 줄이 실행될 땐 프로퍼티 test를 찾을 수 없습니다.

숫자형

모던 자바스크립트는 숫자를 나타내는 두 가지 자료형을 지원

  1. 일반적인 숫자는 '배정밀도 부동소수점 숫자(double precision floating point number)' 로 알려진 64비트 형식의 IEEE-754에 저장.
  2. 임의의 길이를 가진 정수는 BigInt 숫자로 표현.

숫자를 입력하는 방법

let billion = 1000000000;
let billion1 = 1e9; // 1000000000
let ms = 0.000001;
let ms1 = 1e-6; // 0.000001

let a = 0xff; // 255 16진수
let b = 0xFF; // 255 16진수
let c = 0b11111111; // 255 2진수
let d = 0o377; // 255 8진수

num.toString(base) : base진법으로 num을 표현한 후, 이를 문자형으로 반환

let num = 255;
alert(num.toString(16)); // ff
alert(num.toString(2)); // 1111111

어림수 구하기

Math.floor : 소수점 첫째자리에서 내림(버림)

Math.ceil : 소수점 첫째자리에서 올림

Math.round : 소수점 첫째 자리에서 반올림

toFixed(n) : n번째 수까지 어림수를 구한 후 이를 문자형으로 반환해주는 메서드

let a = Math.floor(3.111);
let b = Math.ceil(3.111);
let c = Math.round(3.111);
let d = 3.111;


console.log(a); // 3
console.log(b); // 4
console.log(c); // 3
console.log(d.toFixed(1)); // 3.1

부정확한 계산

console.log(0.1 + 0.2); // 0.30000000000000004

숫자는 0과 1로 이루어진 이진수로 변환되어 연속된 메모리 공간에 저장.

10진법을 사용하면 쉽게 표현할 수 있는 0.1, 0.2같은 분수는 이진법으로 표현하면 무한소수가 됨.

isNaN, isFinite

Infinity, -Infinity : 그 어떤 숫자보다 큰 혹은 작은 특수 숫자값

NaN : 에러값을 나타내는 값

isNaN(value) : 인수를 숫자로 변환한 다음 NaN 테스트 함

isFinite(value) : 인수를 숫자로 변환하고 변환한 숫자가 NaN/Infinity/-Infinity가 아닌 일반 숫자인 경우 true 반환.

-> 문자열이 일반 숫자인지 검증하는데 사용.

console.log(isNaN(NaN)); // true
console.log(isNaN("str")); // true

console.log(NaN === NaN); // false, 자기 자신을 포함하여 그 어떤 값과도 같지 않다는 점.

console.log(isFinite("15")); // true
console.log(isFinite("str")); // false, NaN 이기 때문
console.log(isFinite(Infinity)); // false, Infinity 이기 때문
let num = +prompt("숫자를 입력하세요", '');
alert(isFinite(num));

Object.is

Object.is는 === 값을 비교할 때 사용되는 특별한 내장 메서드인데, 아래와 같은 두 가지 케이스에선 신뢰할 수 있는 결과를 보여줌.

Object.is(NaN, NaN); // true
Object.is(0, -0); // false

문자열

작은 따옴표와 큰 따옴표는 기능상 차이가 없습니다.

템플릿 리터럴(template literal)

  • 백틱엔 특별한 기능이 있습니다.
  • 표현식을 ${...}로 감싸고 이를 백틱으로 감싼 문자열 중간에 넣어주면 해당 표현식을 문자열 중간에 쉽게 삽입할 수 있음.

문자열.length : 함수가 아니고, 숫자가 저장되는 프로퍼티, 문자열의 길이 저장.

특정 글자에 접근하기

let str = 'hello';

console.log(str[0]);
console.log(str.charAt(0));

console.log(str[1000]); // undefined
console.log(str.charAt(1000)); // '' (빈 문자열)
let str = "hello";

str.toUpperCase();
str.toLowerCase();
str.indexOf('e');
"hello".includes("lo");
"hello".startsWith("he");
"hello".endsWith("lo");
str.slice(0, 2);

배열

  • Push : item를 배열 끝에 더해줍니다.
  • pop : 배열 끝 요소를 제거하고, 제거한 요소를 반환합니다.
  • Shift : 배열 처음 요소를 제거하고, 제거한 요소를 반환합니다.
  • Unshift : items를 배열 처음에 더해줍니다.

반복문 // 배열에서만 사용가능.

let fruits = ['사과', '오렌지', '자두'];
for(let fruit of fruits) {
    alert(fruit);
}

'length 프로퍼티'

length 프로퍼티는 배열 내 요소의 개수가 아니라 가장 큰 인덱스에 1을 더한 값.

let arr = [];
arr[111] = 22;
alert(arr.length); // 112 

배열과 메서드

splice

arr.splice(index[, deleteCount, elem1, ..., eleN])

slice

arr.slice([start], [end])

concat

arr.concat(arg1, arg2)

forEach

arr.forEach(function(item, index, array) {
  // 요소에 무언가를 할 수 있음.
});

indexOf(item, from) / lastIndexOf(item, from) / includes(item, from)

find

let result = arr.find(function(item, index, array) {
  // true가 반환되면 반복이 멈추고 해당 요소를 반환합니다.
  // 조건에 해당하는 요소가 없으면 undefined를 반환합니다.
});

filter

let results = arr.filter(function(item, index, array) {
  // 조건을 충족하는 요소는 results에 순차적으로 더해집니다.
  // 조건을 충족하는 요소가 하나도 없으면 빈 배열이 반환됩니다.
});

map

let result = arr.map(function(item, index, array) {
  // 요소 대신 새로운 값을 반환합니다.
});

sort(), reverse(), split(), join()

Array.isArray

console.log(typeof {}); // object
console.log(typeof []); // object
console.log(Array.isArray({}); // false
console.log(Array.isArray([]); // true

iterable 객체

반복 가능한 (iterable) 객체는 배열을 일반화한 객체입니다.

for.. of 를 사용할 수 있는 객체를 이터러블이라고 부름.

  • iterable에는 메서드 Symbol.iterator 가 반드시 구현되어 있어야 함.
  • Obj[Symbol.iterator]의 결과는 이터레이터, 이터레이터는 이어지는 반복 과정을 처리.
  • 이터레이터엔 객체{done: Boolean, value:any}을 반환하는 메서드 next()가 반드시 구현되어 있어야 함. 여기서 done: true는 반복이 끝남을 의미. 그렇지 않은 경우엔 value가 다음 값이 된다.

맵과 셋

맵 map

  • new Map() : 맵을 만든다.
  • Map.set(key, value) : key를 이용해 value를 저장.
  • Map.get(key) : key에 해당하는 값을 반환, key가 존재하지 않으면 undefined를 반환.
  • Map.has(key) : key가 존재하면 true, 존재하지 않으면 false를 반환.
  • map.delete(key) : key에 해당하는 값을 삭제.
  • Map.clear() : 맵 안에 모든 요소를 제거.
  • map.size - 요소의 개수를 반환.

맵은 키로 객체를 허용합니다.

맵은 삽입된 순서를 기억합니다.

맵의 요소에 반복 작업하기

  • Map.keys() : 각 요소의 키를 모은 반복 가능한(iterable) 객체 반환.
  • Map.values() : 각 요소의 값을 모든 이터러블 객체를 반환.
  • Map.entries() : 요소의 [키, 값] 한 쌍으로 하는 이터러블 객체를 반환.

Object.entries : 객체를 맵으로 바꾸기

Object.fromEntries : 맵을 객체로 바꾸기

셋 set

중복을 허용하지 않은 값을 모아놓은 특별한 컬렉션, 셋에 키가 없는 값이 저장.

  • new Set(iterable) : 셋을 만듭니다. 이터러블 객체를 전달 받으면 그 안의 값을 본사해 셋에 넣어줌.
  • set.add(value) : 값을 추가하고 셋 자신을 반환.
  • set.delete(value) : 값을 제거.
  • set.has(value) : 셋 내에 값이 존재하면 true, 아니면 false
  • Set.clear() : 셋을 비웁니다.
  • Set.size : 요소의 갯수 반환.

 

위크맵과 위크셋

맵에서 객체를 키로 사용한 경우, 맵이 메모리에 있는 한 객체도 메모리에 남음. 가비지 컬렉터의 대상이 되지 않음.

위크맵(WeakMap) 을 사용하면 키로 쓰인 객체가 가비지 컬렉션의 대상이 됨.

  • 위크맵의 키는 반드시 객체여야 함.
let john = { name: "John" };

let map = new Map();
map.set(john, "...");

let weakMap = new WeakMap();
weakMap.set(john, "...");

john = null; // 참조를 null로 덮어씀

// john을 나타내는 객체는 맵 안에 저장되어있습니다.
// map.keys()를 이용하면 해당 객체를 얻는 것도 가능합니다.
for(let obj of map.keys()){
  alert(JSON.stringify(obj));
}

alert(map.size); // 1

alert(weakMap.size); // undefined join = null이 된 시점에서 메모리 삭제 대상.

위크맵을 사용하면 수동으로 삭제해줄 필요 없음, 키가 되는 객체가 사라지면, 가비지 컬렉터의 대상이 되므로 .

// 📁 visitsCount.js
let visitsCountMap = new WeakMap(); // 위크맵에 사용자의 방문 횟수를 저장함

// 사용자가 방문하면 방문 횟수를 늘려줍니다.
function countUser(user) {
  let count = visitsCountMap.get(user) || 0;
  visitsCountMap.set(user, count + 1);
}

위크셋은 셋과 유사한데, 객체만 저장할 수 있다는 점이 다름.원시값은 저장 불가.


Object.keys, values, entries

 객체
호출 문법map.keys()Obj.keys()가 아닌 Object.keys(obj);
반환 값iterable 객체진짜 배열
let user = {
    name : 'kim',
    age: 1,
};

console.log(Object.keys(user)); // ["name", "age"]
console.log(Object.values(user)); //  ["kim", 1]
console.log(Object.entries(user)); ["name", "kim"],["age", 1]
  1. Object.entries(obj)를 사용해 객체, 키 쌍을 요소로 갖는 배열을 얻음.
  2. 1에서 만든 배열에 map등 배열 전용 메서드 적용
  3. 2에서 반환된 배열에 Object.fromEntries(array)를 적용해 배열을 다시 객체로 되돌림.
let prices = {
    banana: 1,
    apple: 2,
    meat: 4,
};

let doublePrices = Object.fromEntries(
    Object.entries(prices)
    .map(([key, value]) => [key, value * 2])
);

alert(doublePrices);

구조 분해 할당

배열 분해하기

let arr = ["Bora", "Lee"]

let [firstName, surname] = arr;
alert(firstName);
alert(surname);

객체 분해하기

let options = {
    title: "Menu",
    width: 100,
    height: 200
};

let {title, width, height} = options;

console.log(title);
console.log(width);
console.log(height);

Date 객체와 날짜

let now = new Date();

console.log(now.getFullYear()); // 2020
console.log(now.getMonth()); // 7 (0 ~ 11) // 8월 
console.log(now.getDate()); // 23
console.log(now.getDay()); // 0 (0 ~ 6) // 일요일

Date.parse(str)

  • YYYY-MM-DD – 날짜(연-월-일)
  • "T" – 구분 기호로 쓰임
  • HH:mm:ss.sss – 시:분:초.밀리초

JSON과 메서드

  • JSON.stringify – 객체를 JSON으로 바꿔줍니다.
  • JSON.parse – JSON을 객체로 바꿔줍니다.

문자열은 큰따옴표로 감싸야 합니다. JSON에선 작은따옴표나 백틱을 사용할 수 없습니다

객체 프로퍼티 이름은 큰따옴표로 감싸야 합니다

JSON.stringify 호출 시 무시되는 프로퍼티는 아래와 같습니다.

  • 함수 프로퍼티 (메서드)
  • 심볼형 프로퍼티 (키가 심볼인 프로퍼티)
  • 값이 undefined인 프로퍼티

space로 가독성 높이기

JSON.stringify(value, replacer, space)의 세 번째 인수 space는 가독성을 높이기 위해 중간에 삽입해 줄 공백 문자 수를 나타냅니다.

let user = {
  name: "John",
  age: 25,
  roles: {
    isAdmin: false,
    isEditor: true
  }
};

alert(JSON.stringify(user, null, 2)); 
//
/* 공백 문자 두 개를 사용하여 들여쓰기함:
{
  "name": "John",
  "age": 25,
  "roles": {
    "isAdmin": false,
    "isEditor": true
  }
}
*/