코딩스토리

2. JavaScript 데이터 타입과 연산자 본문

Web/JavaScript

2. JavaScript 데이터 타입과 연산자

kimtaehyun98 2021. 1. 6. 18:35

# 이 글은 INSIDE JavaScript(저자 송형주, 고현준) 책 내용을 바탕으로 작성한 글입니다.

 

 

1. 자바스크립트의 데이터 타입과 연산자

자바스크립트 기본 타입

 

특징 : 그 자체가 하나의 값을 나타낸다.

 

자바스크립트는 느슨한 타입 체크 언어이다.

따라서 C언어처럼 int, char 이렇게 미리 정해줄 필요 없이 처음 변수를 선언할 때는 아래와 같이 선언한다.

 

// 숫자 타입
var int_num = 0;
var float_num = 1.1;

// 문자열 타입
var single_quote_string = 'single quote string'; // 작은 따옴표 문자열
var double_quote_string = "double quote string"; // 큰 따옴표 문자열
var singleChar = 'a';

// 불리언 타입
var boolvar = true;

// undefined 타입
var undefy;

// null 타입
var nullval = null;

console.log(typeof int_num, typeof single_quote_string, typeof boolvar, typeof undefy, typeof nullval);

 

이 코드의 실행 결과는 다음과 같다.

number string boolean undefined object

// 여기서 왜 null이 아니라 object가 출력되는지는 이따 다시 설명하자.

 

자바스크립트의 숫자

 

자바스크립트에서는 정수형이 따로 없고 모든 숫자를 실수로 처리한다. 

이는 계산할때 주의해야 한다. ( C++에 익숙한 나 같은 경우!)

 

var num = 5/2;

console.log(num); // 출력값 = 2.5
console.log(Math.floor(num)); // 출력값 = 2

 

자바스크립트의 문자열

 

문자열은 위에서 적은 바와 같이 작은 따옴표(')와 큰 따옴표(")로 생성한다.

한번 정의된 문자열은 변하지 않는다. (자바랑 똑같네요!)

 

// 문자열 생성
var str = "js"
console.log(str[0], str[1]); // 출력값 = js

// 문자열은 바꿀수 없음!
str[0] = 'J';
console.log(str[0], str[1]); // 출력값 = js -> 변하지 않았음!

 

자바스크립트의 불린값

 

이 boolean값은 true와 false 값을 가진다.

알고리즘 문제를 풀어본 사람들은 알겠지만 정말 정말 중요한 데이터 타입이다.

C++과 JAVA 에는 기본적으로 들어가 있다.

 

 

자바스크립트의 null, undefined

 

undefined는 말 그대로 정의되지 않았다는 뜻이고 null 또한 null 값을 의미한다.

 

이때 중요한 점은 아까 typeof() 연산을 수행했을 때 null의 결과값이 null이 아니라 object가 나왔다는 것이다.

(이 책에선 아직까진 왜 그런지 설명해주진 않았다. 나중에 설명해줄 것 같긴 한데.. 아쉽네)

 

// null 타입 변수
var nullvar = null;

console.log(typeof nullvar === null); // 출력값 = false
console.log(nullvar === null);        // 출력값 = true

위 코드에서 처음으로 자바스크립트를 배우는 나로서는 '===' 연산자가 굉장히 낯설었다.

 

책에서는 '위와 같은 예제 때문에 ===을 사용해야 합니다!'라고만 나와있어서 따로 찾아보았다.

 

잠시 적어보면

 

우리가(정확히는 내가) 흔히 사용하는 '==' 연산자가 자바스크립트에서는 Equality 연산자이다.

JavaScript에서는 '==' 연산을 하기 전, 피연산자들을 서로 비교할 수 있는 형태로 형 변환을 시킨다.

 

아래와 같은 예제를 보면 정말 신기하다.

254 == '254'         // return true
undefined == null    // return true
'true' == true       // return false

어.. 이게 무슨 일인가..

 

솔직히 다시 봐도 이해가 잘 안 가는데 더 찾아보니 그냥 '==' 연산 자체가 다른 타입의 피연산자들끼리의 비교를 시도한다고 한다.

 

(사실 내가 지금까지 공부한 언어를 기반으로 이 내용을 봤을 때는 말이 안 되는 내용이다. 하지만 아마도? 정말 아마도 내 추측이지만 위에서 언급했던, 자바스크립트가 느슨한 타입 체크 언어이기 때문에 이게 가능한 게 아닐까 추측해본다.)

 


// '==' 연산자에 대한 설명 - 출처 : developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Equality

 

동등 연산자 (== 와 !=)는 두 피연산자를 비교하기 위해 Abstract Equality Comparison Algorithm를 사용합니다. 다음과 같이 간략히 설명할 수 있습니다:

  • 두 피연산자가 모두 객체일 때, 두 피연산자가 동일한 객체를 참조할 때에만 true를 반환합니다.
  • 하나의 피연산자가 null이고 다른 하나가 undefined일때, true를 반환합니다.
  • 두 피연산자의 타입이 다를 경우, 비교하기 전에 동일한 타입으로 변환하도록 합니다:
    • 숫자와 문자열을 비교할 경우, 문자열을 숫자로 변환하도록 합니다.
    • 하나의 피연산자가 Boolean일 경우, Boolean 피연산자가 true일 경우 1로 변환하고, false일 경우, +0으로 변환합니다.
    • 하나의 피연산자가 객체이고 다른 하나가 숫자나 문자열이면, 객체를  valueOf()나 toString()를 사용해 기본 데이터 타입으로 변환하도록 합니다.
  • 두 개의 피연산자가 동일한 타입일 경우, 다음과 같이 비교됩니다:
    • String: 두 피연산자가 동일한 문자 순서가 동일한 문자열일 경우, true를 반환합니다.
    • Number: 두 피연산자가 동일한 값을 가질 경우, true을 반환합니다. +0 과 -0 은 동일한 값으로 취급합니다. 어느 한쪽이 NaN일 경우, false를 반환합니다.
    • Boolean: 두 피연산자가 모두 true이거나, 모두 false일 경우, true를 반환합니다. 

 

다음으로 '===' 연산자는 Identity 연산자로써 형 변환을 하지 않고 연산한다.

우리가 원래 알고 있던 '=='와 비슷한 개념이 자바스크립트에선 '==='이다.

(밑에 가서 다시 공부할 예정. 찾아보니까 책에 나와있네요^^;)

 

2. 자바스크립트 참조 타입 (객체 타입)

 

자바스크립트에서는 위에 언급한 다섯 종류의 기본 타입 (숫자, 문자열, 불리언, undefined, null)을 제외한 나머지 모든 값은 객체이다.

 

(이 밑에서부터는 자바스크립트를 JS라 할게요 너무 귀찮다)

 

JS에서 객체는 단순히 key값과 value값을 저장하는 컨테이너이다.

 

JS에서 기본 타입은 하나의 값만을 가지지만, 참조 타입인 객체는 여러 개의 property를 포함할 수 있다.

(쉽게 예를 들면 array 같은 경우겠죠?)

 

이러한 property의 성질에 따라 객체의 property를 함수로 포함할 수 있으며 이를 JS에서는 메서드라고 한다.

(지금은 property가 무엇인지 이해가 잘 안 가지만 이따 코드를 통해 이해할 수 있다.)

 

내가 지금까지 배워왔던 객체지향 언어에서는 클래스를 정의하고 이를 기반으로 인스턴스 객체를 만들었다.

 

하지만! 두둥! JS는 클래스란 개념이 없다. 

 

JS에서 객체를 생성하는 방법은 크게 3가지가 있다.

 

  1.  기본으로 제공하는 Object() 함수 사용
  2.  객체 리터럴 사용
  3.  생성자 함수 사용

 

먼저 Object() 생성자 함수를 사용하는 방법이다.

 

// Object()를 이용하여 객체 생성
var foo = new Object();

// foo 객체 property 생성
foo.name = 'foo';
foo.age = 30;
foo.gender = 'male';

자. 이제 property가 뭔지 감이 온다.

 

위의 예제에서는 name, age, gender가 foo객체의 property들임을 알 수 있다.

 

console 창에서 출력해본 결과

실제 console창에 돌려보면 위와 같이 출력함을 알 수 있다.

 

 

다음으로 리터럴 방식을 사용하는 방법을 알아보자.

여기서 리터럴이란, 표기법이다.

즉, 객체 리터럴이란 객체를 생성하는 표기법을 의미한다고 한다.

 

코드는 다음과 같다.

// 객체 리터럴 방식으로 foo 객체 생성
var foo = {
    name : 'foo',
    age : 30,
    gender : 'male'
};

역시 코드로 보는 게 이해가 제일 빠르다.

 

자꾸 C언어랑 비교해서 그렇긴 하지만 구조체 느낌이 물씬 난다.

 

리터럴 방식 출력

당연히 출력값은 똑같다.

여기서 만약 property를 생성할 때 name이나 age처럼 기본 타입이 아닌 함수를 property로 사용할 경우 그러한 property를 메소드라고 부른다.

 

 

생성자 함수로 객체를 생성하는 방법은 다음 chapter에서 공부할 예정이다.

 

 

객체 프로퍼티 읽기/쓰기/갱신

 

객체의 프로퍼티(property)를 생성했다면, 생성된 프로퍼티를 읽어서 출력하거나, 원하는 값으로 갱신할 수 있다.

 

프로퍼티에 접근하는 방법은 다음과 같다.

 

  • 대괄호 표기법 ( [] )
  • 마침표 표기법 ( . )

 

다음 코드를 보면서 이해해보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 객체 리터럴 방식을 이용한 foo 객체 생성
var foo = {
    name : 'foo',
    major : 'computer science'
};
 
// 객체 프로퍼티 읽기
console.log(foo['name']);  // 출력값 : foo
console.log(foo.major);    // 출력값 : computer science
console.log(foo.nickname); // 출력값 : undefined
 
// 객체 프로퍼티 갱신
foo.major = 'software';
console.log(foo.major);    // 출력값 : software
 
// 객체 프로퍼티 동적 생성
foo.age = 24;
console.log(foo.age);      // 출력값 : 24
 
// 대괄호 표기법 사용
foo['ful-name'= 'foo bar';  // full-name이란 property 동적 생성
console.log(foo['ful-name']); // 출력값 : foo bar
console.log(foo.full-name);   // 출력값 : NaN
cs

이번엔 코드를 이쁘게 해 봤다. ㅎ

 

마침표 표기법은 쉽게 이해할 수 있다. 

 

특이한 건 대괄호 표기법이다.

 

대괄호 표기법으로 동적 생성과 출력까지는 문제없다.

이때 문제는 foo.full-name으로 출력하면 출력값이 NaN이 나온다는 것이다.

 

(여기서 NaN이란 Not a Number란 의미로 JS에서 수치 연산을 해서 정상적인 값을 얻지 못할 때 출력되는 값이다.

예를 들어 100 - 'soft'  => NaN)

 

왜 NaN이 나올까?

 

생각해보면 간단하다.

 

full-name 에서 '-' 연산을 진행해버렸기 때문이다 ^^

 

 

그럼 객체의 모든 property를 출력하고 싶을 땐 어떻게 해야 할까?

아까 잠깐 보았듯이 그냥 객체 자체를 출력해도 좋지만, 각각의 property에 접근해서 출력하는 방법이 있다.

 

바로 for in 문을 사용하는 방법이다.

 

// 객체 리터럴을 통한 foo 객체 생성
var foo = {
    name : 'foo',
    major : 'computer science'
    age : '24'
};

// for in 문을 이용한 객체 property 출력
var prop;
for (prop in foo){
    console.log(prop, foo[prop]);
}

 

굳이 따로 설명할 필요가 없을 정도로 이해하기 쉽다.

var prop; 을 통해 데이터 형식을 지정해주지 않았기 때문에 foo안에 있는 property가 무엇이든 가능하다.

 

예를 들면 for( auto i : arr) 이 정도?

 

추가적으로 프로퍼티의 삭제는 delete 연산자를 통해 가능하다.

이때 객체 자체를 삭제하는 것이 아니라, property만 삭제한다.

 

// 객체 리터럴을 통한 foo 객체 생성
var foo = {
    name : 'foo',
    major : 'computer science',
    age : '24'
};

// delete 를 사용해 property 제거
delete foo.name;
console.log(foo.name);    // 출력값 : undefined

 

 

3. 참조 타입의 특성

 

참조 타입이란 객체를 참조한다는 뜻이다.

JS 에는 5가지의 기본 타입을 제외한 나머지 모든 값은 객체다.

그만큼 객체를 참조하는 타입이 중요하다.

 

C언어의 포인터 개념을 생각하면 쉽다.

 

역시 코드를 보면서 이해해 보자.

// 객체 생성

var Obj_A = { value : 100 };
var Obj_B = { value : 100 };
var Obj_C = Obj_B;

// 객체 비교

console.log( Obj_A == Obj_B );
console.log( Obj_B == Obj_ㅊ ); 

 

Obj_A와 Obj_B는 각기 다른 객체를 참조하고 있다.

그렇기 때문에 Obj_A == Obj_B는 False란 값을 반환할 것이다.

반면 Obj_C는 Obj_B와 같은 객체를 가리키고 있다.

그렇기 때문에 Obj_B == Obj_C 는 True를 반환할 것이다.

 

 

 

함수 호출 방식 

 

기본 타입과 참조 타입은 함수 호출 방식이 다르다.

 

기본 타입은 값에 의한 호출 (Call by Value)이고, 참조 타입은 참조에 의한 호출(Call by Reference)이다.

(이거 언젠지 모르겠지만 수업시간에 배웠었는데 그땐 되게 어려운 개념인 줄 알았는데..)

 

많이 봤기 때문에 간단히 짚고 넘어가면

Call by Value : 인자로 값을 복사해서 넘김 -> 함수에서 어떤 짓을 해도 원래 값은 변하지 않음

Call by Reference : 인자로 객체의 참조값을 넘기기 때문에 함수에서 실제 객체 값을 변경 가능

 

위 사진을 잘 보면 Obj란 객체를 인자로 넘겼기 때문에 Call by Reference, 즉 val값이 100에서 200으로 바뀜을 알 수 있다.

 

특이한 점은 함수를 만들 때 인자들의 타입을 명시하지 않는다는 것이다.

 

 

4. 프로토타입

 

나에게 있어서 객체 지향 언어 하면 가장 생각나는 것은 상속이다.

상속(Inheritence)는 1년 동안 나를 많이 괴롭혔다.

 

JS에는 상속과 같은 프로토타입이 존재한다.

프로토타입이란 모든 객체는 부모 객체를 가지고 있는데, 이러한 부모 객체를 프로토타입이라고 한다.

 

만약 Java를 배우기 전에 JS를 배웠다면 이 부분이 많이 황당했을 것 같지만 다행히 미리 뚜드려 맞고 왔기 때문에ㅎ..

 

지금은 어떻게 부모 객체를 갖고 부모 객체는 무슨 타입인지가 중요하지는 않다.

단 부모 객체, 즉 프로토타입을 갖는다는 것이 중요하다.

 

상속을 공부하면서 배웠겠지만 부모 객체로부터 상속을 받는다면 내가 정의하지 않은 메소드도 쓸 수 있게 된다.

위의 코드를 보자.

내가 foo 란 객체를 생성했고, 프로퍼티 두 개를 만들었다.

하지만 toString()이란 함수는 만들지 않았는데, 밑에서 호출했다.

 

원래라면 에러가 나야 하지만 정상적으로 [object Object] 라고 출력된다.

 

이유는 foo 객체의 프로토타입에 toString이 정의되어 있기 때문이다.

 

이후로 foo 객체에 대해 출력해보면

내가 정의한 name과 age란 프로퍼티 말고도 __proto__ 라는 프로퍼티가 하나 더 있음을 알 수 있다.

 

이것이 자신의 부모 객체를 가리키고 있는 내부 프로퍼티이다.

 

 

5. 배열

 

JS에서 배열은 특이하다.

우리가 흔히 알고 있는 배열과 같은 기능을 하지만, 크기를 지정하지 않아도 되며, 어떤 위치(index)에 어느 타입의 데이터를 저장하더라도 에러가 발생하지 않는다.

 

배열 생성은 다음과 같다.

 

// 배열 리터럴을 통한 생성

var five = [ 'one', 'two', 'three', 'four', 'five' ];

// 배열 동적 생성

var arr = [];

arr[0] = 'TaeHyun';
arr[3] = 100;
arr[5] = true;

배열을 생성하는 방법이 매우 자유롭다.

배열의 크기와 index땜에 많은 좌절을 겪었던 나에게 굉장히 신선한 문법이었다.

 

arr 출력 결과

위 사진을 보고 알 수 있듯이 legth는 최대 길이, 즉 배열의 가장 큰 index를 기준으로 출력되는 것을 알 수 있다.

 

더 신기한 것은 이 배열의 legth를 내가 바꿀 수 있다는 것이다.

 

legth를 강제로 2로 줄였기 때문에 3은 삭제된다.

 

배열에 원소를 추가하는 방법도 매우 매우 간단하다.

 

Push( 추가할 원소 )

 

 

배열의 프로퍼티 생성

 

진짜 신기한 것은 이 부분이다.

 

JS에서 배열 역시 객체라고 하였다.

그렇기 때문에 배열에게 프로퍼티를 생성할 수 있다고 한다!

 

엄청 엄청 신기하다! (나만 그래요?)

 

배열의 길이는 프로퍼티를 추가한다고 변하지 않는다.

매우 유용하게 쓰일 것 같다!

 

그럼 모든 프로퍼티를 출력해보자.

 

흔히 사용했던 for문으로 출력을 하면 배열의 원소값들이, for in문을 사용하면 모든 프로퍼티들이 출력된다.

 

 

배열의 요소를 삭제하는 것 역시 프로퍼티를 삭제하는 방법과 같다.

 

배열에서 원소 삭제

요소를 삭제한다고 해서 배열의 길이가 줄어들지는 않는다. 

 

하지만 우리는 이렇게 비어있는 공간은 상당히 불편하다. 

배열을 순회하는 작업을 할 때 이런 비어있는 공간이 있다면 따로 예외처리해줘야 하고 등등.. 

 

이럴 때는 splice() 메서드를 사용하면 된다.

 

splice 메소드

arr.splice( 삭제를 시작할 index, 몇 개의 요소를 삭제할 것인지)

 

즉 arr.splice(1,1) 은 arr의 1번 index에서부터 1개의 원소를 삭제하겠다는 의미이다.

 

 

유사 배열 객체

 

처음 보는 개념이 나왔다.

 

책에서는 length 프로퍼티를 가진 객체를 유사 배열 객체라고 말해주고 있다.

 

나중에 arguments 객체나 JQuery 객체를 공부할 때 필요하다고 하는데 지금은

"배열의 프로퍼티로 length를 가지고 있는 객체" 정도로만 이해하고 넘어가자.

 

 

 

6. 기본 타입과 표준 메서드

 

아래의 코드를 보자.

var num = 0.5;
console.log(num.toExponential(1)); // 출력값 : '5.0e-1'

 

C나 자바를 공부해온 나에게는 말도 안되는 코드이다.

 

num은 int형, 즉 JS에서는 숫자형 기본 자료형이다.

 

근데 메소드를 호출하고 있다.

 

잉? 기본형은 객체가 아닌데 어떻게 메소드를 호출하지? 라는 의문점이 들것이다.

 

JS에서는 숫자, 문자열, 불 값에 대해 각 타입별로 호출 가능한 표준 메소드를 정희하고 있다.

 

이게 무슨 이야긴가 하면

 

기본 타입들도 메소드를 호출할 수 있다는 이야기이다. (단 이미 정의된 표준 메소드만)

 

기본 타입이 메소들를 호출하면, 기본 타입이 잠시 객체로 변환된 다음, 해당 표준 메소드를 호출 한 뒤 다시 기본값으로 돌아간다.

 

 

7. 연산자

 

간단하게만 적어보면

 

'+' 연산자는 덧셈과 문자열 연결 연산을

var str = "Kim " + " Tae Hyun" // 출력값 : Kim Tae Hyun

 

typeof 연산자는 타입을 알려준다.

var num = 0.5;
typeof(num); // 출력값 : "number"

 

'==' 와 '===' 의 차이

 

'==' 은 비교하려는 피연산자의 타입이 다를 경우 타입 변환 후 비교

'==='은 타입을 변경하지 않고 비교 

 

지금은 이 두 차이를 확실히 이해하지 못하지만 책에서는 가급적 '===' 연산자로 코딩하기를 권장한다고 한다.

 

'!!' 연산자 : 피연산자를 불린 값으로 변환

console.log(!!0);           // 출력값 : false
console.log(!!1);           // 출력값 : true
console.log(!!'string');    // 출력값 : true
console.log(!!'');          // 출력값 : false
console.log(!!{});          // 출력값 : true

 

이렇게 해서 JS의 기본 문법을 알아보았다.

 

언어를 공부할 때마다 느끼는 거지만 문법을 보면 너무 어렵고 답답한데

실제로 많이 써보는 게 가장 좋은 것 같다.

'Web > JavaScript' 카테고리의 다른 글

ES6 공부  (0) 2021.02.25
5. 실행 컨텍스트와 클로저  (0) 2021.01.19
4. 함수와 프로토타입 체이닝 - 2  (0) 2021.01.15
4. 함수와 프로토타입 체이닝 - 1  (3) 2021.01.11
1. JavaScript와 HTML  (0) 2021.01.04
Comments