본문으로 바로가기
반응형

 

 

1. 실행문(Statement) 쓰기

 

실행문이란 컴퓨터가 실행할 수 있는 명령을 의미하며, 프로그램이란 특정 결과를 얻기 위한 이러한 실행문의 집합을 의미한다.

 

① 세미콜론

기본적으로 자바스크립트의 뒤에는 세미콜론을 모든 문장에 붙이는 것이 원칙이다.

let x = 10;
console.log(x);

 

다만, 붙이지 않아도 정상 실행되는 이유는 나중에 프로그램 빌드(parcel 사용) 시에 자동으로 붙기도 하고, 엔진에서 알아서 붙여서 해석하기도 하기 때문이다.

안 붙여도 되긴 하는데, 되도록 붙이는 습관을 들이는 것이 나쁘지 않다

 

② 대소문자 구분

자바스크립트는 대소문자를 구분한다.

생성한 변수 혹은 함수의 이름, 예약어(reserved word) 등을 사용 시에는 반드시 대소문자를 정확히 구분 해야 한다.

let test = 10; // 정상
Let test = 20; // 오류

 

③ 식별자(identifier)

식별자는 변수/함수의 이름을 작성 시 사용하는 이름으로, 영문자(대소), 숫자, 언더스코어(_), 달러($)만 사용 가능

자바스크립트는 기본적으로 유니코드(Unicode) 문자셋을 사용한다.

 

④ 식별자 쓰는 방식

자바스크립트는 기본적으로 아래의 2가지 방식의 식별자를 쓴다.

Camel Case방식 : 여러 단어일 때, 첫 단어는 전체 소문자로 하고 다음 단어부터는 첫 문자만 대문자로 쓰는 방식(예: letsLearnJS)
Underscore Case 방식 : 전체 단어를 소문자로 쓰되, 각 단어를 언더스코어(_)로 연결하는 방식(예 : test_case)

개인적으로 Camel Case 방식을 선호한다.

 

⑤ 주석 만들기

// let test = 10; 한줄 주석

/*
let t1 = 20;
let t2 = 30;
여러줄 주석
*/

 

 

2. 데이터 타입

 

사실 자바스크립트는 객체지향 프로그래밍(OOP) 인지, 함수형 언어인지 혼란이 있었다. 둘 다 가능한데, 이 혼란으로 인해 모든 것이 객체인지, 함수도 객체인지 하는 의문들이 있었다.

그러한 의문들을 아래에서 해결해보자.

 

 

데이터 타입 종류

 

데이터 타입은 기본적으로 Literal(Primitive, 원시)타입과 객체(Object) 타입으로 나뉘며, 아래와 같다.

- 원시 타입(Primitive)
   º String
   º Number
   º Boolean
   º Symbol(ES6 에서부터 추가됨)
   º Null
   º Undefined

- 객체 타입(Object, Reference Type)
   º Normal Object
   º 배열
   º 함수
   º 정규 표현식

즉, 자바스크립트에서 원시 타입 데이터를 제외하고는 모두 객체이다!

 

 

원시 타입과 객체 타입의 차이

 

원시(Primitive) 타입은 값으로 저장(Pass by value) 이며, 객체(Object) 타입은 참조로 저장(Pass by reference)이다.
  - Pass by value는 원시 값을 복사해 전달하므로, 전달된 변수가 값이 변경되어도 기존 변수는 값이 불변
  - Pass by reference는 참조 주소를 전달하므로 원본 값에 의한 직접 접근이 가능하여 원본 값이 변경될 수 있다.

즉, 원시(Primitive) 타입은 값(value) 그 자체를 변수에 바로 저장하는데, 객체는 참조(reference)로 저장된다.
따라서 저장 시, 다른 메모리 공간을 사용하게 된다.(메모리 관리 관련 별도 포스팅에서 설명 예정)

 

원시(Primitive) 타입 자료형은 불변이다. 같은 변수에 새 값을 할당할 수는 있지만 이것이 기존 값이 변하는 의미는 아니기 때문이다. 값을 그 자체로 저장하므로, 해당 값을 변경할 수는 없다.

그러나, 객체 타입 자료형은 참조(힙 메모리 주소값 참조)로 저장되기 때문에 값을 변경할 수 있다.

 

데이터를 저장할 수 있는 변수는 var, let, const 예약어로 생성 가능(관련 포스팅은 여기)

 

 

3. 원시(Premitive) 타입

 

① String 타입

- 문자 데이터이며 쌍 혹은 홑 따옴표로 정의할 수 있다.
- 백틱(`)을 사용해 다른 변수 데이터를 넣어 정의하거나 출력도 가능하다.
- 쌍 혹은 홑 따옴표를 안에 쓰고 싶다면 바깥을 다른 것으로 감싸야 한다.

let js = "Javascript" // let은 변수 선언을 위한 예약어
let hi = `Hi! ${js}`  // 백틱 사용하여 변수 데이터 삽입

console.log(`${hi}`); // Hi! Javascript

console.log("'홑'을 안에 쓰고 싶다면 쌍으로 감싸기");
console.log('"쌍"을 안에 쓰고 싶다면 홑으로 감싸기');



// 객체로 만들기
let wrapper = new String("Javascript!");
console.log(obj); // [String: '1']

 

② Number 타입

- 자바스크립트는 정수와 실수 구분 없이, 모든 수를 실수 하나로만 표현(64bit float)
- 기본적인 산술 연산 가능
- e 표기법을 사용 가능

let num = 123;
let num2 = 1.57;

console.log(num, num2); // 123 1.57
console.log(10e6);      // 1,000,000

// 객체로 만들기
let wrapper = new Number(123);
console.log(wrapper) // [Number : 123]


// 산술 연산
console.log(1+2); // 3
console.log(5-8); // -3
console.log(3*4); // 12
console.log(10/2); // 5
console.log(1 % 5); // 1

 

③ Boolean 타입

- true / false를 나타낸다.
- truthy : 자바스크립트에서 true로 취급되는 것 → [true, {}, [], 문자열, 정수, 실수]

- falsy : 자바스크립트에서 false로 취급되는 것 → [false, '', null, undefined, 0, -0, NaN]

let t = true;
let f = false;
console.log(t === f); // false

// 객체로 만들기
let wrapper = new Boolean(true);
console.log(wrapper); // [Boolean: true]


// truthy 테스트 - 아래는 모두 true 판정되어 출력
if(true) console.log("work!");
if({}) console.log("work!");
if('haha') console.log("work!");
if(123) console.log("work!");

// falsy 테스트 - 아래는 모두 false 판정되어 출력 안됨
if(false) console.log("work!");
if(0) console.log("work!");
if(undefined) console.log("work!");
if(null) console.log("work!");

 

④ Symbol

- ES6에서 새로 등장한 데이터 타입으로 다른 값과 중복되지 않는 고유 값
- 충돌 위험 없는 Object의 유일 Property 키를 만들 때 사용 가능

- Symbol() 로 생성 가능 하나 new 연산자는 없고 원시 타입임
- for .. in 문과 같이 열거 시, Symbol 속성은 열거되지 않음

- 숫자 연산 불가, String 연산은 가능

- 사용이유
  → 자바스크립트는 기본적으로 Public이기 때문에 대규모 코드 상으로 다양한 라이브러리를 사용 시에 라이브러리 내 Property를 덮어버릴 수도 있다!
   → 객체를 사용 시에, 필드를 내부에 숨길 때 사용이 가능하다. 외부에서 접근이 기본적으로 불가하여 Private함을 가독성 있게 구현 가능

let symbol = Symbol();  // new 연산자 불가
console.log(symbol); // Symbol()
console.log(typeof symbol); // symbol


// 아래의 예시와 같이 반드시 유니크한 값을 유지할 수 있음
let s1 = Symbol('test');
let s2 = 'test';
let s3 = Symbol('test');
console.log(s1 === s2); // false
console.log(s1 === s3); // false

// for를 통해 생성도 가능한데, 이 경우 전역의 Global Symbol Table 목록 참조
let g1 = Symbol.for('test');  
let g2 = Symbol.for('test');
console.log(g1 === g2); // true

// 객체의 Property로 사용 시, []를 통해 접근 가능
let user = {
  name: 'nice'
}
let id = Symbol('id');
user[id] = '123';
console.log(user[id]);


// for문으로 접근 불가
for (let i in user.keys){
  console.log(i); // 출력 안됨
}


// 라이브러리 Property Key값이 겹치는 경우 예시
const a = Symbol();
class A {
    a = 0;  // 외부에서 사용 시 덮어 쓸 수도 있다!
    [a] = 0; // 이와 같이 사용했다면 걱정 X
    get() { ... };
}

class ExtendA extends A {
    a = function () { ... }; // 충돌 발생!, 기존 값을 덮어써버렸음
}

 

⑤ Undefined

- 미리 선언된 전역 객체의 Property
- 값이 할당되지 않은 상태

- null과 달리 초기화 자체도 정의되지 않은 상태를 의미

let a;
console.log(a);  // undefined
console.log(undefined);  // undefined
console.log(typeof undefined);  // undefined

 

⑥ Null

- 선언, 등록을 하는 예약어
- 의도적으로 비어 있는 값을 명시한 상태(아무것도 참조하고 있지 않은 상태)

- 값이 들어 있지만 의미 없는 Null 값 자체가 명시되어 있는 상태로, undefined와는 다름
- typeof 연산자로 확인하면 타입은 Object 타입임을 주의

let a = null;
console.log(a); // null
console.log(null); // null
console.log(typeof null); // object


// null 와 undefined는 값으로는 falsy라서 동등 연산자로 테스트하면 true가 나온다.
// 일치 연산자로 테스트 하면 false가 나옴
console.log(null == undefined); // true
console.log(null === undefined); // false

 

 

4. 객체(Object) 타입

 

객체 타입은 데이터 / 데이터와 관련한 동작(절차, 방법, 기능)을 모두 포함할 수 있는 개념적 존재이다.

즉, 내부에 이름/데이터를 갖는 프로퍼티(Property)와 동작을 의미하는 메소드(Method)를 포함하여 묶어놓은 독립적 집합체이다.
Object, Array(배열), 함수(function), 정규표현식 이 해당한다.

 

① Normal Object(객체)

- 객체 데이터로 key : value 형태로 중괄호 안에 묶어 데이터를 저장할 수 있다.
- 변수 안의 각 Key를 속성(Property)라고 부르며, Key에 함수를 매핑 시 메소드(Method)라고 부른다. 둘을 합쳐 member라고 부른다.
- 함수를 내부에 사용 시에는 : function 을 생략할 수 있다.

let pet = {
    'type' : 'dog', 
    'name' : 'happy',
    'age' : 3,
    bark(){  // 메소드(함수) 추가, bark: function() { } 와 같이 쓸 수도 있다.
        return "Bow wow!";
    }
};

console.log(pet.type); // dog
console.log(pet.bark()); // Bow wow!

 

② Array(배열)

- 배열 데이터는 순차적으로 데이터를 저장할 수 있는 자료구조로 대괄호를 통해 사용 가능하다.
- 서로 다른 데이터 타입(객체, 함수 포함)을 저장할 수 있으며, 크기는 동적으로 변경이 가능하다.

let arr = [];
arr[0] = 0;
arr[1] = 1;
arr[2] = '2';  // 여러 타입 한 번에 넣을 수 있음

for(let i in arr) console.log(i); // 0 1 2가 순서대로 출력

// 배열 생성자 함수를 통해 생성하는 법
let arr2 = new Array(1, 2, '3');

arr2.forEach((item) => {console.log(item)}); // 1 2 3가 순서대로 출력


// 길이
console.log(arr2.length); // 3


// push로 끝에서 추가
arr2.push(4); // arr2 : [1, 2, '3', 4]
console.log(arr2[3]); // 4


// pop으로 끝에서 제거
arr2.pop(4); // arr2 : [1, 2, '3']
console.log(arr2[3]); // undefined


// shift로 앞에서 제거
arr2.shift(); // arr : [2, '3'];
console.log(arr2[0]); // 2


// unshift로 앞에서 항목 추가
arr2.unshift(-1); // arr : [-1, 2, '3'];
console.log(arr2[0]); // -1


// 대상 element의 첫번째 index 찾기
console.log(arr2.indexOf(2)); // 1

 

③ 함수(Function)

- 함수는 이름, 매개변수, {} 내의 정의 표현으로 구성된다.(여기서는 기본적인 함수에 대해서만 알아본다.)
- 이름이 없는 익명함수로 만들어 데이터로 바로 활용하거나, 객체 내의 메소드로도 표현이 가능하다.
- 더 복잡한 다양한 내용은 함수 관련 별도 포스팅에서 다룰 예정

function myFunction(num){  // 함수 선언
    console.log(num);
}

myFunction(123);  // 함수 호출


// 익명 함수 : 이름 지정하지 않고 만들어 값으로 사용하여 변수에 대입할 수도 있다.
let world = function() {
    return "World~!";
}
console.log(world); // World~!


// 객체의 메소드로 표현
const data = {
    name : 'myData',
    getName : function () {
        return this.name;
    }
}
console.log(data.getName()); // myData

 

④ 정규 표현식(정규식)

- 문자열에 나타나는 특정 조합과 대응시키기 위해 사용되는 패턴
- 동일 정규식 패턴 을 지속 사용 시 원시 타입으로, 패턴 변경 시 생성자(Constructor) 함수를 통해 동적으로 생성
- 더 복잡한 다양한 내용은 함수 관련 별도 포스팅에서 다룰 예정

let re = /ab+c/;  // 원시 타입 정규식
let re2 = new RegExp("ab+c"); // 생성자 함수를 통해 만든 동적 정규식


// 찾고자 하는 패턴을 매칭하여 반환
re.exec("this is abc");

 

 

※ 참고 - Wrapper(래퍼) 객체/오토박싱

 

위에서 설명할 때, Number / Boolean / String은 new 연산자와 함께 객체로 생성할 수 있다고 설명했다.

일단 아래의 코드를 보자. 원시타입 String에 대해서 메소드(함수)를 사용하는 예시이다.

let lang = 'Javascript';
let s = lang.substring(4);
console.log(s); // script


원시타입인데 그 안에 호출할 수 있는 함수가 있다. 이게 어떻게 가능할까?
이는 Number / Boolean / String에 대해서는 그에 대응하는 Wrapper 객체가 제공되기 때문이다.



Wrapper란 무엇일까?
원시 타입에 대응하며 해당 원시 타입을 감싸는 형태로 사용하는 객체를 의미한다.
즉, 원시 타입을 감싸는 객체로 생성되어 해당 객체가 갖는 속성/메소드(함수)를 사용할 수 있게 해준다.


그래서, 실제로 자바스크립트에서는 아래와 같이 동작한다.

① 일치 타입에 대한 Wrapper 객체를 생성한다.(오토박싱)
② 해당 객체의 메소드(함수)를 호출한다.
③ 해당 객체를 즉시 삭제한다.(오토언박싱)


위 동작은 해당 속성/메소드(함수)를 호출한 1개 line에서만 동작한다.(다음 Line에서는 Scope를 벗어난 것!)

따라서, 위 코드는 기능적으로는 아래와 동일하다.
(실제 내부에서 아래와 같이 돌아간다는 것은 아니고 이해를 위한 예시이다.)

let lang = 'Javascript';

// 임시로 Object 생성
let tmp = new String(lang);

// 함수 호출
let s = tmp.substring(4);

// 즉시 삭제(null로 처리시켜 삭제 처럼 표기)
tmp = null;


즉, 우리는 원시 타입으로 데이터를 생성하더라도 내부에서 일시적으로 Wrapper 객체로 변환을 시켜주기 때문에 해당 객체가 갖는 속성/메소드(함수)를 사용할 수 있는 것이다.

이와 같은 변환을 오토박싱(Auto Boxing, Wrapper 객체로 변환), 오토언박싱(Auto UnBoxing, 원시 타입으로 변환)이라 부른다.




그렇다면, 원시 Wrapper 타입이 있는데 new String() 과 같이 생성할 때와의 차이점은 무엇일까?


new 를 통해 객체로 생성하면 Scope에서 벗어나는 순간까지 메모리에 남는다. 즉, 일반 객체와 동일하다.
(메모리 구조에 대한 포스팅은 추후 별도로 수행)


하지만, Wrapper 타입인 경우에는 호출되는 1개 Line에서만 임시로 생성되고 바로 사라진다.
그래서 아래와 같이 원시 타입으로 생성 후, 속성을 임의로 지정한 뒤 출력해보면 undefined로 나온다.

let str = 'test';
str.haha = 50;
console.log(str.haha); // undefined



참고로, 원시 타입 데이터를 new를 통해 명시적으로 객체화 시켜서 만드는 것은 권장되지 않는 방식이다.
(불필요한 추가적 힙 메모리 사용 때문이다.)

 

 

 

읽어 주셔서 감사합니다.

오류가 있으면 정정 댓글 달아주시면 감사 드립니다.

반응형