부트캠프/자바스크립트 완벽 가이드

3장 타입, 값, 변수

하이고니 2022. 12. 27. 00:05

3.1 개요와 정의

타입: 프로그래밍 언어에서 표현하고 조작할 수 있는 값의 종류.

객체: 숫자, 문자열, 불, 심볼, null, undefined 중 어느 것에도 속하지 않는 그 모든 것을 객체라고 하며,

객체 타입의 멤버이며 프로퍼티의 집합을 의미.

배열: 값의 '순서 있는' 집합.

 

객체 지향 프로그래밍: 배열 a의 요소를 정렬할 때 sort(a)가 아니라 a.sort(); 를 사용.

*자바스크립트에서는 객체만이 메서드를 가질 수 있음. 하지만 숫자, 문자열, 불, 심벌도 마치 메서드가 있는 것처럼 동작함.

메소드를 호출할 수 없는 것은 nullundefined 뿐.

 

객체 => 가변 (mutable)

기본 타입 => 불변 (immutable) 

3.2 숫자 

부동 소수점

 

부동소수점 또는 떠돌이 소수점 방식은 

실수를 컴퓨터상에서 근사하여 표현할 때 소수점의 위치를 고정하지 않고 그 위치를 나타내는 수를 따로 적는 것으로,

유효숫자를 나타내는 가수와, 소수점의 위치를 풀이하는 지수로 나누어 표현한다.

 

아래는 예제다.

 

[JavaScript] 부동소수점 사용 방법 예제

[JavaScript] 자바스크립트 강좌 입문 #10 데이터형 - 부동소수점2017/12/02 - [IT/JavaScript・자바스크립트] - [JavaScript] 자바스크립트 강좌 입문 #9 데이터형 - 숫자 리터럴・부동소수점 부동소수점으로 작

ponyozzang.tistory.com

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>소수점</title>
    <link rel="text/javascript" href="script.js">
</head>
<body>
    
    <script type="text/javascript" src="script.js">
    </script>

</body>
</html>
const n1 = 0.148;
const n2 = 2.4e-3;

document.write('<p>' + n1 + '</p>'); 
document.write('<p>' + n2 + '</p>');	// 0.0024

자바스크립트에서 사용하는 실수는 그 갯수가 정해져 있다. 근삿값을 이용하는 수들이 꽤 많다는 것이다. 아래를 보자.

let x = .3 - .2;
let y = .2 - .1;
x === y // => false: 두 값은 같지 않다.
x === .1 //=> false: 0.3 - 0.2 는 0.1 과 같지 않다.
y === .1 //=> true: 0.2 - 0.1 은 0.1 이다.

0.2 - 0.1 은 0.1 이지만 0.3 - 0.2 는 0.1 이 아니다.

 

위와 같은 문제를 사전에 방지하려면 정수값으로 변환해 계산하는 것을 추천한다.

'0.1 달러'가 아닌 '10 센트' 로 생각하기.

 

3.3 텍스트

어포스트로피 안에 어포스트로피 넣기

ex) 'You're right, it can be a quote'

이런 경우에는 역슬래시( \ ) 를 사용하면 된다.

역슬래시는 그 다음 문자와 조합해서 일반적인 방법으로는 문자열에 표시할 수 없는 문자를 표현한다.

 

'You\'re right, it can be a quote'

 

문자열도 비교할 수 있다. 문자열 비교는 16비트 값을 비교하는 방식으로 이루어진다.

 

let s = "Hello, world"; // 이 텍스트를 예제에 사용한다.

// 문자열의 일부를 가져온다.
s.substring(1,4);	     // => "ell": 두 번째, 세 번째, 네 번쨰 문자
s.slice(1,4); 		     // => "ell": 같은 결과
s.slice(-3);		     // => "rld": 마지막 세 문자
s.split(",");		     // => ["Hello", "world"] ',' 구분자를 기준으로 나눈다.

// 문자열 검색
s.indexOf("l")		     // => 2: l이 처음으로 나타나는 위치
s.indexOf("l", 3)	     // => 3: 3번 문자부터 시작해 l이 처음 나타나는 위치
s.indexOf("zz")		     // => -1: s 에는 zz 라는 문자열이 들어있지 않다.
s.lastIndexOf("l")	     // => 10: l이 마지막으로 나타나는 위치

// 불을 반환하는 검색 함수
s.startsWith("Hell")         // => true: s 는 Hell 로 시작한다.
s.endsWith("!")		     // => false: s 는 ! 로 끝나지 않는다.
s.includes("or")  	     // => true: s 에는 or 이 포함되어 있다.

// 문자열을 변경
s.replace("llo", "ya")       // => "Heya, world"
s.toLowerCase()		     // => "hello, world"
s.toUpperCase()		     // => "HELLO, WORLD"
s.normalize()		     // => 유니코드 NFC 정규화는 ES6 에서 추가되었다.
s.normalize("NFD")	     // => NFD 정규화. NFKC, NFKD 도 있다.
 
// 문자열의 각 16비트 문자를 검사한다.
s.charAt(0)			 // => "H": 첫 번째 문자
s.charAt(s.length - 1)   	 // => "d": 마지막 문자
s.charCodeAt(0)			 // => 72: 주어진 위치의 16비트 숫자
s.codePoingAt(0)		 // => 72: 16비트보다 큰 코드 포인트에서 동작하는 ES6 기능

// 패딩 함수는 ES2017에서 추가됐다.
"x".padStart(3)			 // => "  x": 왼쪽에 스페이스를 더해 길이를 3에 맞춘다.
"x".padEnd(3)			 // => "x  ": 오른쪽에 스페이스를 더해 길이를 3에 맞춘다.
"x".padStart(3, "*")	 	 // => "**X": * 를 왼쪽에 붙여 길이를 3에 맞춘다.
"X".padEnd(3, "-")		 // => "x--": - 를 오른쪽에 붙여 길이를 3에 맞춘다. 

// 공백 제거. trim()은 ES5, 나머지는 ES2019 기능이다.
" test ".trim()			 // => "test": 앞뒤 공백 제거
" test ".trimStart()		 // => "test ": 왼쪽 공백 제거. trimLeft 도 있다.
" test ".trimEnd()	 	 // => " test": 오른쪽 공백 제거. trimRight 도 있다.

// 그 외의 문자열 메소드
s.concat("!")			 // => "Hello, world!": + 연산자를 쓰는 게 더 간단하다.
"<>".repeat(5)			 // => "<><><><><>": n 번 복사한다. ES6에서 추가됐다.

*자바스크립트의 문자열은 불변이다.

replace()toUpperCase() 같은 메소드는 기존 문자열을 수정하는 것이 아니라 새 문자열을 반환한다.

 

 

String.raw`Hi\n${1+2}`; // => 'Hi\\n3'
String.raw`Hi\\n${1+2}`; // => 'Hi\\\\n3'
String.raw`Hi\${1+2}`;  // => 'Hi\\${1+2}'

역슬래시가 어떤 원리로 추가되는 건지 잘 모르겠다. 스터디 때 물어보자.

 

3.6 심벌

심벌(Symbol) 은 문자열이 아닌 프로퍼티의 이름이다.

먼저, 자바스크립트의 객체 타입이 프로퍼티의 순서 없는 집합이며 각 프로퍼티에는 이름과 값이 있다는 것을 이해해야 한다.

let strname = "string name";		// 프로퍼티의 이름에 문자열("string name")을 썼다.
let symname = Symbol("propname");	// 프로퍼티의 이름에 심볼을 썼다.
typeof strname 				// => "string": strname 은 문자열이다. 
typeof symname				// => "symbol": symname 은 심볼이다.

let o = {};				// 객체를 하나 생성한다.
o[strname] = 1;				// 문자열 이름으로 프로퍼티를 정의
o[symname] = 2;				// 심볼 이름으로 프로퍼티를 정의
o[strname]				// => 1: 이름이 문자열인 프로퍼티에 접근
o[symname]				// => 2: 이름이 심볼인 프로퍼티에 접근

console.log(o);				// => {string name: 1, Symbol("propname"): 2}

이터러블(iterable): 반복 가능한. for..of 을 사용할 수 있는 객체를 이터러블이라고 부른다.

Symbol.iterator: 객체를 이터러블로 만드는 메서드 이름으로 쓸 수 있는 심벌 값

 

iterable 객체

 

ko.javascript.info

  • 이터러블엔 메서드 Symbol.iterator가 반드시 구현되어 있어야 한다.
    • obj[Symbol.iterator]의 결과는 이터레이터라고 부른다. 이터레이터는 이어지는 반복 과정을 처리한다.
    • 이터레이터엔 객체 {done: Boolean, value: any} 을 반환하는 메서드 next() 가 반드시 구현되어 있어야 한다.
      여기서 done:true 은 반복이 끝났음을 의미하고 그렇지 않은 경우엔 value 가 다음 값이 된다.
  • 메서드 Symbol.iterator  for..of 에 의해 자동으로 호출되는데, 개발자가 명시적으로 호출하는 것도 가능하다.
  • 문자열이나 배열 같은 내장 이터러블에도 Symbol.iterator 가 구현되어 있다.
  • 문자열 이터레이터는 서로게이트 쌍을 지원한다.

3.7 전역 객체

전역 객체의 프로퍼티는 전역으로 정의된 식별자이다. 모든 자바스크립트 프로그램에서 사용할 수 있다.

  • undefined, Infinity, NaN 과 같은 전역 상수
  • isNaN(), parseInt() eval() 같은 전역 함수
  • Date(), RegExp(), String(), Object(), Array() 같은 생성자 함수
  • MathJSON 같은 전역 객체

3.8 불변인 기본 값과 가변인 객체 참조

객체는 값으로 비교하지 않는다. 두 객체의 프로퍼티와 값이 같다고 해서 같은 객체는 아니다.

또한 두 배열에 같은 요소가 같은 순서로 존재한다 해도 둘이 같은 배열은 아니다.

letl o = {x: 1}, p = {x: 1};
o === p		// false
let a = [], b = [];
a === b		// false

두 객체가 같다는 말은 오직 두 값이 같은 객체를 참조할 때만 성립한다.

let a = [];	// 변수 a는 빈 배열을 가리킨다.
let b = a;	// 이제 변수 b도 같은 배열을 가리킨다.
b[0] = 1;	// b가 참조하는 배열을 변경한다.
a[0]	// => 1: 바뀐 부분은 변수 a를 통해서도 보인다.
a === b	// true: a와 b는 같은 객체를 참조하므로 같은 값이다.

객체나 배열을 변수에 할당하는 것은 참조를 할당하는 것이다. 할당한다고 해서 객체의 사본이 새로 생기는 것은 아니다. 객체나 배열의 사본을 만들기 위해서는 반드시 객체 프로퍼티나 배열 요소를 직접 복사해야하 한다.

let a = ["a", "b", "c"];	// 복사할 배열
leb b = [];	// 복사해 넣을 대상
for(let i = 0; i < a.length; i++) {	// a의 각 인덱스에 대해
	b[i] = a[i];	// a의 요소를 b에 복사
}
let c = Array.from(b);	//ES6에서는 Array.from()으로 배열을 복사할 수 있다.

별개의 객체나 배열을 비교할 때는 양쪽의 프로퍼티나 요소를 비교해야 한다.

function equalArrays(a, b) {					
	if (a === b) return true;					// 같은 배열을 참조한다면 일치
	if (a.length !== b.length) return false;	// 크기가 다르면 불일치
    for(let i = 0; i < a.length; i++) {			// 요소를 순회
    	if (a[i] !== b[i]) return false;		// 어느 하나라도 다르면 불일치
    }
    return true;								// 모두 같다면 일치
}

 

3.9 타입 변환

관용구처럼 쓰이는 타입 변환

x + ""		// => string(x)
+x		// => Number(x)
x-0		// => Number(x)
!!x		// => Boolean(x)

ParseInt() 는 두 번째 인자(선택 사항)로 기수를 받는다. 기수는 2 이상 36 이하 범위여야 한다.

parseInt("11", 2)		// => 3: 2진법 '11'을 10진법으로 표현 (1*2 + 1)
parseInt("ff", 16)		// => 255: 16진법 'ff'를 10진법으로 표현 (15*16 + 15)
parseInt("zz", 36)		// => 1295: 32진법 'zz'를 10진법으로 표현 (35*36 + 35)
parseInt("077", 8)		// => 63: 8진법 '77'을 10진법으로 표현 (7*8 + 7)
parseInt("077", 10)		// => 77

*** var 로 선언된 전역 변수와 let 으로 선언된 전역 변수의 중요한 차이

var 로 선언된 전역 변수는 전역 객체의 프로퍼티로 존재. 전역 객체는 globalThis 로 참조 가능.

함수 바깥에서 var x = 2;globalThis.x = 2; 와 같은 의미. 하지만 var 로 선언된 전역 변수는 delete 연산자로 삭제할 수 없다.

letconst 로 선언한 전역 변수와 상수는 전역 객체의 프로퍼티가 아니다.

 

var 은 호이스팅이라는 기능을 갖는다. var 로 변수를 선언하면 이 선언문은 함수의 맨 위로 끌어올려진다.

 

분해 할당

 

분해 할당은 선언과 할당을 합친 일종의 복합 문법이다.

할당 연산자 오른쪽은 배열이나 객체 같은 구조적인 값이며 왼쪽은 하나 이상의 변수 이름이며 배열이나 객체 리터럴 문법을 쓴다.

 

루프에서도 분해 할당을 사용할 수 있다.

다음 코드는 객체의 프로퍼티 전체 '이름 - 값' 쌍을 순회하며 분해 할당을 한다.

분해 할당을 통해, 각 쌍을 배열에서 개별 변수로 변환한다.

 

let o = {x: 1, y: 2};		// 순회할 객체: 요소가 두개 있는 배열
for(const [name, value] of Object.entries(o)) {
	console.log(name, value);	// "x 1", "y 2": 개별 변수로 변환 
}

 

다음 예제는 Math 객체의 함수를 변수에 복사한다.

const {sin, cos, tan} = Math;
// const sin = Math.sin, cos = Math.cos, tan = Math.tan 와 같다.