32. JavaScript 다시보기

JSLEE·2024년 6월 10일

JavaScript 는 인터프리터 언어

컴퓨터는, 사람이 작성한 코드를 곧이 곧대로 알아들을 수 없다.
기계어로 번역을 해서 컴퓨터가 알아들을 수 있도록 변환되는 과정이 필요하다.

이 과정을 기준으로 언어는 대표적으로 2가지로 나뉜다.
1. 컴파일러 언어
2. 인터프리터 언어

먼저, 컴파일러 언어pre-processing이라는 기계어로 변환하는 과정이 따로 존재한다.
사람이 코드를 작성 -> 기계어 변환(Pre-Processing) -> 실행(Processing)

반면에 인터프리터 언어는 pre-processing 과정이 없고, 기계어로 변환과 동시에 실행도 같이 수행된다.
사람이 코드를 작성 -> 변환하며 동시에 실행(processing)
따라서, 미리 기계어로 번역시키는 컴파일러 언어에 비해 인터프리터 언어는 실행단계에서는 수행 속도가 느리다. 하지만, 인터프리터 언어는 코드를 한줄씩 해석해 나가기 때문에, 개발과 테스트의 수행이 빠르다는 장점이 있다.

JavaScript는 인터프리터 언어를 채택하였다.

또한, JavaScript의 다음과 같은 특징이 있다.

  1. 변수의 타입 - 동적 타입 : 변수의 자료형을 따로 지정해주지 않아도, 런타임에서 타입을 자동적으로 추론하여 지정된다.
  2. 함수의 특징 - 일급 객체 : 함수를 객체를 사용할 때와 같이, 동일하게 취급하여 사용할 수 있다.
  3. 상속의 형태 - 프로토타입 기반의 상속 : 다른 언어들이 클래스를 상속받는데 비해, JS에서는 객체를 기반으로 상속 받을 수 있다.
  4. 패러다임 지원 : 함수형 프로그래밍, 명령형 프로그래밍, 객체지향 프로그래밍 등을 지원한다.

ECMAScript Version

JavaScript를 사용할 때, ECMAScript 스펙에 따라 사용할 수 있는 키워드나 새로운 코드 작성 방법이 생기는 경우가 생긴다.
예를 들어, async-await은 ECMAScript 2017(ES8) 이후 생긴 새로운 문법이다.

또한, 브라우저 별로 지원하는 JS 스펙이 다르다.

따라서, 브라우저가 내가 사용하려는 JS 코드들을 문제없이 잘 인식할 수 있을지에 대해 고민을 해야하므로, 개발을 할 때에는 브라우저가 어떤 JS스펙들을 지원하는지 잘 알고있어야 하는데, 이 때 유용한 웹사이트가 caniuse 라는 웹사이트이다.

해당 웹사이트에 접속 후, 내가 사용하려는 JS 문법이 사용 가능한지 검색하여 알 수 있다.

만약, 내가 사용하려는 JS가 해당 브라우저에서 지원을 하지 않는 스펙이라면, 다음과 같은 방법을 사용해야 한다.

  1. Polyfill : 지원하지 않는 브라우저에서 최신 기능을 제공하는 코드. 브라우저가 다른 방식으로 동일한 기능을 구현 가능하게 해준다.
  2. Babel : 호환이 안되는 브라우저에서 ES6 이전 버전으로 JS를 변환해주는 도구로, 문법을 변환해주고 Polyfill 기능이 있다.

JavaScript의 변수에 관하여

컴퓨터는 데이터를 기억하기 위해 메모리(기억장치)를 활용한다.
메모리에는 각각의 위치 주소값이 존재하는데, 자바스크립트는 이 메모리를 직접적으로 제어할 수 없다.
따라서, 식별자가 메모리 주소를 기억하고, 식별자를 통해 해당 메모리 주소를 찾아가서 값을 알아낸 후, 우리가 그 값을 사용하는 것이다.

우리가 변수를 선언할 때 다음의 코드를 이용한다.

let foo = 1 ;

여기서, let키워드 라고 부르고,
foo변수, =할당연산자 , 1 이라는 이름으로 부른다.

변수 생성의 3단계 / 평가와 실행

변수를 선언하게 되면, 변수는 총 3개의 단계를 거쳐 메모리에 저장되게 된다.

  1. 선언 : JS의 Execution Area에 변수 객체 생성
  2. 초기화 : 변수를 메모리에 할당, undefined로 초기화
  3. 할당 : undefined로 초기화된 변수에 실제로 값을 할당

또한, JS는 코드를 실행할 때 평가단계실행단계로 나뉘어진다.
키워드 var의 경우는 다음과 같다.

  1. 코드 평가 : 3단계 중, 선언초기화를 동시에 진행한다.
  2. 코드 실행 : 3단계 중, 값 할당을 진행한다.

다음과 같은 코드를 살펴보자.

console.log(a); // undefined
var a = 10;

우리가 이 코드를 run 시키면, JS는 먼저, 코드 평가를 수행한다.
따라서, a라는 변수의 선언초기화를 진행한다.
즉, a는 undefined의 값을 가진 상태이다. (이렇게 var의 동작이 코드에서 가장 먼저 수행되는 것처럼 보이는 것을 호이스팅이라고 부른다.)

다음으로는 코드를 실행한다.
코드는 순차적으로 작동하여, console.log(a) 가 먼저 수행된다.
따라서, 콘솔에는 undefined가 출력된 후에야 a에 10이라는 값이 할당된다.

키워드 letconst 는 다음과 같다.

  1. 코드 평가 : 3단계 중, 선언 을 수행한다.
  2. 코드 실행 : 3단계 중, 초기화 를 수행하고, 값 할당을 진행한다. (let의 경우, 값 할당을 하지 않아도 오류가 발생하지 않는다.)
console.log(b);
let b = 10 ;

위 코드에서, 코드를 평가할 때 let선언 만 수행했기 때문에 변수 자체에 대해 접근할 수 없으므로 ReferenceError 가 발생하고, 코드가 종료된다.

종합해보면 varconst, let 은 다음과 같다.

var let const
키워드 생략 O 키워드 생략 X 키워드 생략 X
중복 선언 O 중복 선언 X 중복 선언 X
호이스팅
전역 스코프 블록레벨 스코프 블록레벨 스코프

var중복 선언전역스코프 때문에 이름이 중복되는 등의 혼선이 생기고 예기치 않는 값을 얻을 수 있기 때문에, 사용을 지양하는 것이 좋다.

변수 네이밍 컨벤션

변수를 이름지을 때에는 몇가지 규칙이 있다.

camelCase : 일반적인 변수에 사용
snake_case : 일반적인 변수에 사용
PascalCase : 클래스에 사용
CONSTANT_CASE : 변하지 않는 값(상수)에 사용

JS의 데이터 타입(자료형)

자바스크립트의 데이터 타입은 다음과 같다.

  1. 원시 타입 자료형 : String, Number, BigInt, Undefined, Null, Boolean, Symbol
    • Stack Memory를 사용한다.
    • 값을 변경할 수 없다. (immutable) // 값의 재할당은 가능하다. (const 제외)
    • 참조 형태로 전달되지 않고, 값으로 전달 된다.
let str = "hello";
str[0] = "H";
console.log(str) // hello  -> immutable
let a = 1;
let b = a ; // a가 가리키는 주소의 "값"을 복사
a = 2 ; // a에 값을 재할당
console.log(b) // 1 ;
  1. 객체 타입 - 객체, 함수, 날짜, 컬렉션
    • Hip Memory를 사용한다
    • 값을 변경할 수 있다. (mutable)
    • 객체타입은 참조 방식으로 전달한다. (Pass by Reference)
let a = [1,2,3,4] ;
let b = a ; // a가 가리키는 주소의 참조를 복사. 즉 a의 메모리 주소를 가리킴.
a[1] = 0 ; // a의 값을 변경
console.log(b) // [1,0,3,4] ;

따라서, 객체 타입의 경우 원본 데이터에 영향이 가지 않게 값만을 복사하여 사용하고자 할 땐,
얕은 복사(Shallow copy) , 깊은 복사 (deep copy)를 사용해야한다.

연산자의 종류

  • 단항연산자 : 하나의 피연산자만 사용 ( void, typeof, delete )
  • 산술연산자 :
    • 이항 산술 연산자 :
      두개의 숫자값을 피연산자로 받아서 하나의 숫자값 반환(+, -, *, /, %),
    • 단항 산술 연산자 :
      하나의 숫자값을 피연산자로 받아서 연산 ( ++, --, +, - (음수양수반전) )
  • 관계연산자 : 피연산자를 비교하고, Boolean 값을 반환. ( in, instanceof )
  • 비교 연산자 : 피연산자를 비교, Boolean 값 반환 ( == , === , >= 등 )
  • 논리 연산자 : 표현식을 나타낼 때 사용 ( &&, || )
  • 삼항연산자 : condition ? trueValue : falseValue
  • 할당연산자 : =
  • 옵셔널연산자 : 객체 속성 참조시 유효하지 않을경우 에러 대신 undefined 반환
const foo = {};
console.log(foo.age?.apple); // undefined

foo.age = { apple: "red" };

console.log(foo.age?.apple); // red
  • 쉼표연산자 : 왼쪽부터 표현식을 평가하고, 참이라면 가장 마지막 값을 return함
const a = (1,2);  
a ; // 2

함수

JS의 함수는 일급객체의 특징을 가진다. (일급객체란, 다른 객체들에 일반적으로 적용가능한 연산을 모두 지원하는 객체)
따라서, 다음의 특징이 있다.

  • 함수는 함수의 매개변수가 될 수 있음.
  • 함수는 함수의 반환값이 될 수 있음.
  • 할당명령문의 대상이 될 수 있음.
  • 동일 비교의 대상이 될 수 있음.

함수의 매개변수

  • 기본값 매개변수 :
function foo(arg = 1){}
  • 나머지 매개변수 :
function foo(arg , ...rest){}

* parameter(매개변수)와 argument(인자) 차이
매개변수 : 함수를 정의할 때 사용하는 변수. function foo(a,b,c){} ─ a,b,c는 매개변수
인자 : 함수가 호출될 때 넘기는 값. foo(a,b,c) ─ a,b,c는 인자

함수 사용 방법 4가지

  1. 함수 선언문 형식 :
function myFunc() {}
  1. 함수 표현식 :
const foo = function () {}
  1. Function 생성자 함수 :
const bar = new Function(elem , 'return 1')
  1. 화살표 함수 :
(elem)=>{console.log(elem)}

함수 사용 패턴 4가지

  1. 즉시 실행 함수(IIFE) : 최초 1회만 실행, 함수의 재사용 불가
(function() {})()
  1. 재귀함수 : 자기 자신을 호출
function foo(arg) {
  if (arg >= 3) {
    return
  }
  foo(arg + 1);
}
  1. 중첩함수 : 함수 내부에, 또다른 함수 정의
function foo(arg){
  function bar(){
    console.log(arg);
  }
  bar();
}
  1. 콜백함수 : 매개변수로 함수를 입력받아 사용
function foo(arg){
  arg();
}
foo(()=>{console.log(1)});
profile
공부한 내용들을 정리하기 위해 사용하는 블로그입니다.

0개의 댓글