개요

자바스크립트는 1990년대 넷스케이프 회사의 브렌더 아이크가 “모카”라는 이름으로, 나중에는 “라이브스크립트”라는 이름으로 개발하였고 최종적으로 자바스크립트가 되었다고 한다.

이에 자극을 받은 마이크로소프트는 마이크로소프트의 웹 브라우저인 인터넷 익스플로러 전용 스크립트인 JScript를 개발해서 사용했다.

이는 1996.8월에 출시된 인터넷 익스플로러 브라우저 3.0부터 지원되기 시작했다.

이러다보니 동일한 웹 애플리케이션을 개발하더라도 어떤 브라우저에서 구동되느냐에 따라서 서로 다른 스크립트를 작성해야하는 문제가 생긴다.

그래서 이를 해결하고자 일종의 표준을 정하게 되는데…

ECMA 즉, European Computer Manufactures Association이라는 컴퓨터 시스템의 표준화를 위해 세워진 유럽 표준화 기구에 제출하였고 표준에 대한 작업을 ECMA-262란 이름으로 시작하여 1997년 6월 정식으로 채택되었다.

즉 ECMA스크립트는 스크립트 표준 규격이므로 다른 스크립트여도 된다는 의미를 가진다. 그러나 ECMA스크립트를 자바스크립트라고 봐도 무방하다.

최신 프로그래밍 언어들이 제공하는 기능들을 지원하지 못하였기에 이를 해결하고자 2015년부터 매년 새로운 버전이 나오고 있다.

특징

자바스크립트의 자료형은 다른 프로그래밍 언어들과는 조금 다르다.
예를 들어 int와 같은 키워드를 사용하여 변수를 선언하지 않고 선언시엔 타입이 정해져있지 않다가 런타임 시점에 결정된다.

데이터 선언

데이터를 선언하기 위해 사용하는 키워드에는 3가지가 있다.
var, let, const
이중 변화하는 값(변수)을 선언할 때는 varlet을 사용하고 변하지 않는 상수값을 선언할 때는 const를 사용한다.

우선 var부터 보자.

var는 같은 자바스크립트 코드 안에서 동일한 변수명을 사용하는 것이 허용된다.

예를 들면 다음과 같다.

var a = 2;
var a = 3;
var a = 4;

그러나 문제가 좀 있다. 동일한 변수명을 사용해도 오류가 생기지 않고 값이 덮어씌워지기 때문에 코드가 길어질 경우 예상치 못한 동작이 생길 수 있다. 다른 사람이 작업한 자바스크립트 코드를 삽입하여 사용하게 되는 경우가 있다. 이럴 경우 만약 전역변수로 선언되어있는 변수가 중복될 경우 문제가 생기는 것이다.

ES6이전까지는 자바스크립트에서 변수를 선언하는 키워드는 var하나뿐이였다. 그렇기 때문에 오류를 찾아내는 것이 힘들었다.

이를 위해 ES6 부터 letconst가 추가되었다.

let은 동일한 변수명을 변수를 재선언하는 것이 허용되지 않는다.

즉 의도치 않게 변수를 중복 선언하는 경우를 코드를 작성하는 시점에 알 수 있기 때문에 문제를 방지할 수 있다.

이러한 이유로 var보다 let을 사용하는 것이 좋다.

const역시 ES6에서 추가된 선언자로 이름 그대로 변하지 않는 상수이다.

const를 사용할 경우 선언 시점에 값을 할당한 후 같은 변수명으로 재선언할 수 없고 값을 변경하는 것도 불가능하다.

만일 재선언이나 재할당을 하려고 한다면 오류를 발생시킨다.

데이터 타입

자바스크립트에는 6가지 기본 자료형 타입이 있다.

  • String
  • Number
  • Boolean
  • Undefined
  • Null
  • Symbol

String과 Number, Boolean은 우리가 알고있는 의미 그대로고 Undefined는 어떠한 변수에 값이 할당되어있지 않은 상태를 의미한다. Null과의 차이는 암시적이냐 명시적이냐의 차이이다.

예를 들어 setTimeout과 같은 함수를 사용해 1초 뒤에 값이 할당될 수 있는 코드를 작성한다고 해보자. 이럴 때 let a; 와 같이 선언만 해주고 값을 할당하지 않을 때 Null을 사용할 수 있다.

let a = null; (명시적)

Undefined는 암시적이다.

우리가 위의 코드에 null대신 undefined를 선언해주지 않아도 그 값이 존재한다는 의미이다.

주로 Ajax코드를 바닐라하게 짤 때 콜백 함수를 정의하지 않고 Ajax함수에서 값을 받아오려할 때 undefined를 확인할 수 있다.

예를 들어 다음과 같은 객체 데이터가 있다고 해보자.

const data = {
  name = "user",
  age = 26
}

console.log(data.name) // user
console.log(data.age) // 26
console.log(data.email) // undefined
console.log(data.test); // undefined

존재하지 않는 키에 대해 접근하려고 했으니 Java와 같은 언어에서는 오류를 알려줄 것이지만 undefined를 출력한다.

이럴 경우 null을 사용할 수 있다.

키가 존재하는데 값이 존재하지 않는 경우와 존재하지 않는 키에 접근하는 것은 다르기 때문에 명시적으로 null을 사용하는 것이다.

배열

const fruits = new Array('Apple', 'Banana');

new 키워드를 사용해서 호출하는 함수를 생성자 함수라고 한다.

const fruits = ['Apple', 'Banana'];

위와 같이 선언하는 방식을 리터럴방식이라고 한다.

console.log(fruits[1]); // Banana

배열의 길이를 알고싶을 때는 fruits.length와 같이 사용할 수 있다.

배열 안에는 모든 탕칩이 다 들어갈 수 있다. 배열안의 배열, 배열안의 함수, 배열안의 객체도 가능하다.

새로운 값을 추가할 때는 인덱스 번호와 함께 추가하는 것도 가능하고 push메소드를 사용해서 순차적으로 추가할 수 있다.

객체

객체를 생성하는 방법은 위의 배열 선언 방식과 마찬가지로 new Object()와 같이 생성할 수 있다.(생성자 함수 방식)

다음과 같이 생성할 수도 있다.

function User() {
  this.name = "test"
  this.age = 26
}

const user = new User();

그러나 일반적으로 다음과 같이 리터럴 방식을 이용하는 경우가 더 많다. 편하기 때문이다.

const user = {
  name: 'user',
  age: 85
}

값을 조회하는 방법은 점 표기법, 대괄호 표기법이 있다.

user.name // user
user['name'] // user

console.log(user)와 같이 출력을 해보면 내가 선언한 순서와 출력된 순서가 다를 수 있다. 이는 객체안의 키는 고유하기 때문에 순서는 의미가 없기 때문이다.

이제 객체 생성 방법을 알았으니 유저의 정보를 담는 객체를 생성해보자.

const userA = {
  name: 'user1',
  age: 26,
  getUser: function() {
    console.log(this.name, this.age);
  }
}

const userB = {
  name: 'user2',
  age: 22, 
  getUser: function() {
    console.log(this.name, this.age);
  }
}

그러나 문제가 있다. 바로 디테일한 값만 달라지고 틀은 똑같은데 중복되는 코드를 작성해야한다는 것이다. 일반화된 객체 하나만 만들고 이를 재사용할 수 없을까?

가능하다. 객체를 동적으로 생성할 수 있는 방법이 있는데 바로 다음과 같은 방법이다.

function User(name, age) {
  this.name = name
  this.age = age
  this.getUser = function() {
    console.log(this.name, this.age);
  }
}

const user = new User('test', 26);

이런식으로 하나의 틀을 이용해 붕어빵을 찍어내듯이 객체들을 만들어낼 수 있다.

그러나 아직 문제가 완전히 해결되지는 않았는데 바로 getUser메소드가 중복된다는 점이다.

함수 또한 한 번만 정의해서 사용할 순 없을까?

prototype

자바스크립트의 객체들은 prototype이라는 공간을 바라본다. 이 개념을 응용해서 prototype이라는 공간을 통해서 객체들의 공통 속성을 정할 수 있다.

사용법은 이렇다.

User.prototype.getUser = function() {
  console.log(this.name, this.age);
}

User객체들은 prototype이라는 공간을 바라보기 때문에 이렇게 하면 여기서 정의한 메소드를 사용할 수 있다.

마치 상속과 같은 개념이다. 자바스크립트의 class문법도 새로운 개념이 아닌 prototype에 인터페이스를 씌운 형태인 것이다.

익명 함수

const getNumber = function() {
  return 123;
}

위의 코드에선 const 키워드를 통해 데이터 저장공간을 선언하고 함수를 저장했다. 이렇게 저장된 함수는 이름이 없기 때문에 익명 함수라고도 부른다.

console.log를 이용해서 타입을 확인해보면 function이라는 타입이 리턴된다. 즉 이렇게 작성해도 함수를 정의할 수 있다는 것이다.

형변환

다음의 예제는 숫자 타입과 문자 타입을 비교하는 코드이다. 타입이 다른 데이터를 비교했으니 에러가 나거나 같지 않다는 결과가 나올 것으로 기대한다.

const a = 1
const b = '1'

console.log(a == b);

실행해보면 의외로 true가 나온다. 즉 a와 b가 같다는 말인데 어떻게 된 일일까?

자바스크립트의 등호는 세가지 종류가 있다. =는 값을 할당하는 등호이고 ==등호는 동등연산자라고하는 값이 일치하는지를 확인하는 연산자이다. 또 다른 등호가 있는데 바로 ===일치연산자이다.

동등연산자는 타입이 달라도 값이 같으면 일치한다고 판단한다.

1과 ‘1’은 ‘’ 기호의 차이이다. 자바스크립트는 여러 경우의수롤 검토하며 비교해보는 작업을 거친다. 그렇기 때문에 다른 타입이라도 값이 같으면 true를 리턴하는 것이다.

==보다는 === 사용을 권장하는데 그 이유는 예상밖의 결과가 나올 수 있기 때문이다. 예를 들어 const a = 0 const b = false일 경우에 console.log(a == b)true를 출력한다. 의도치않은 결과가 나온 것이다. 이와 같은 잠재적 문제를 예방하기위해 ==보다는 ===를 사용하는 편이 좋다.

엔진

자바스크립트 엔진중 대표적인 것은 바로 구글의 V8엔진이다. 이를 사용해 독립적인 런타임 환경을 구축한 것이 바로 Node.js이다.

자바스크립트 엔진은 두 가지 구성요소로 이루어져있다.

메모리 힙, 콜 스택

메모리 힙 - 객체는 힙에 할당된다. 변수와 객체에 대한 모든 메모리 할당은 여기서 발생한다.

호출스택 = 코드가 실행될 때 호출 스택이 쌓인다.

자바스크립트는 단일 스레드 구조로 이루어져있다. 이는 한 번에 하나의 일만 처리할 수 있다는 뜻이다.

함수를 호출하고 실행하면 해당 함수를 스택의 맨 위에 추가한다. 그리고 함수가 값을 반환하면 스택에 쌓여있던 함수는 제거된다.

스택 오버플로우

스택의 사이즈를 초과했을 때 발생하는 오류이다.

위에서 설명한 대로 함수가 종료조건 없이 계속해서 호출되면 문제가 생긴다.

스택 오버플로우, 말그대로 스택의 공간이 가득차 넘치는 것이다. 주로 재귀함수를 호출할 때 발생한다.