퍼펙트 자바스트립트

Posted by yunki kim on August 9, 2021

  평소 프로그래밍 공부를 하면서 어떠한 기술을 활용하고자 할때 그 기술에 대해 이론 지식을 모르고 사용하면 너무 찜찜하고 스트래스를 받아서 차라리 안쓰거나 이해가 될때 까지 찾아보곤 한다. 지금까지 js를 공부하고 활용하면서 이런 성격(?) 때문에 지속적으로 js에 대해 찾아보고 공부 했지만 요즘 들어 이론을 좀 제대로 집고 넘어가야 될거 같다는 생각이 든다. 

  js에 대해 그냥 인터넷을 찾자니 글 마다 깊이다 다 다르고, 내가 어느 정도 알고 있다고 생각되는 부분에서 새로운 지식을 배울 수 도 있다는 생각이 들어서 인터넷을 찾아 보는 대신 퍼펙트 자바스크립트 라는 책을 사서 공부하기로 했다.

  이 글은 내가 퍼펙트 자바스크립트 라는 책을 읽으면서 새로 배운, 또는 한 번은 집고 넘어 가야 될거 같은 것들에 대해 정리한 글이다. 따라서 js의 특정 주제를 다룬다기 보다는 그냥 중구난방으로 js라는 큰 카테고리의 일부 이론을 정리한 글이다.

 

js의 특징

  1. 인터프리터 언어

  2. 동적 타입 언어

  3. 프로토타입 기반 객체 지향 프로그래밍

  4. 리터럴 표기식

  5. 함수형 프로그래밍

 

js type

  ECMAScript 명세에는 내장 타입(built-in type)을 5개로 나눈다. 명세에는 primitive type이라는 용어는 없으며 대신 primitive value가 사용된다.

 js는 다섯가지 기본 타입이 있다: 문자열, 숫자(64비트 부동소수점), 불린, null, undefined.

 js는 동적 타입 언어 이기 때문에 정적 타입 언어와는 다르게 타입을 가진 대상이 값과 객체이다. 동적 타입 언어는 변수에 타입이 존재한다.

class VS prototype

  자바의 경우 promitive type을 제외하면 사용자 정의 타입이다. 그리고 이런 사용자 정의 타입은 인스턴스로서 존재 하고 이런 프로그래밍 스타일을 클래스 기반이라 한다. 반변 prototype의 경우, 즉 js는 언어 사양상 타입을 정의 하는 구문이 존재 하지 않는다. js의 프로토타입은 프로퍼티 또는 메서드 등으로 타입을 정의 한다. 타입은 특징의 공통성이다. 그러므로 js는 각 객체에 특징을 갖게 하기 위해 프로토타입을 사용하고 이런 스타일을 프로토타입 기반이라 한다.

 

  내장 타입 중 string, number, boolean은 대응되는 객체가 존재 한다(String, Number, Boolean) 이 객체들은 내장 타입에서 프로퍼티를 사용하게 되면(ex. ('a').length) 암묵적 형변환이 되면서 대응되는 객체로 형변환 된 후에 프로퍼티를 호출하는 용도로 사용된다. 물론 new연산을 통해 직접 사용할 수 는 있지만 암묵적 형변환이 되기 때문에 굳이 그럴 필요는 없다.

 

  부동 소수점

    다른 언어에서도 마찬가지 이지만 js의 부동 소수점은 소수점 이하의 값을 정확히 표현할 수 없다. 만약 실수를 정확히 다루어야 한다면 자바의 BigDecimal에 해당하는 실수 라이브러리를 사용해야 한다. js에 표준으로 사용되는 실수 라이브러리는 없다.

부동소수점은 근사값

  NaN

    인자를 숫자로 형변환 할때 해당 인자가 숫자가 아니면 NaN이 된다. 또 한 어떠한 연산을 할때 연산 중 결과가 하나라도 NaN인게 있다면 해당 연산의 최종 결과는 NaN이다.

 

  null

    특이하게 null값의 typeof는 object가 나온다. 이것이 js사양의 버그라 주장하는 사람도 존재한다

  

  undefined

    undefined는 null처럼 리터럴 값이 아닌 정의된 전역 변수이다. 따라서 ES5이전에는 undefined에 값을 대입하는 것이 가능했지만 ES5부터는 read only로 바뀌었다.

    undefined는 다음과 같은 경우에 나온다:

      1. 초기화 되지 않은 변수의 값

      2. 존재하지 않는 프로퍼티

      3. 실질 인자를 부여하지 않고 함수를 호출했을 때 함수 안에서 대응하는 인자의 값

      4. return문 자체가 없는 또는 return문 식이 없는 함수의 반환값

      5. void 연산자의 평가 결과(void 0으로 undefined를 얻는 것을 관용구이다)

 

  함수 타입

    js에서 함수는 객체의 일종이다. 하지만 typeof연산을 하면 object가 아닌 function이라는 문자열이 나온다.

 

형변환

  문자열에서 숫자로 형변환을 할때 문자열에 수가 아닌 다른 문자가 있다고 해보자. 이때 Number()를 사용하면 NaN이 parseInt()를 사용하면 수가 아닌 다른 문자가 나오기 직전까지만 숫자로 형변환 된다.

  숫자0, 숫자 NaN, null, undefined, 빈 문자열을 제외하면 js에서 모든 값이 true이다. 이때문에 아주 잼이 있는 현상이 생긴다. 불린값 false를 Boolean객체로 선언하면 true가 된다(new Boolean(false) -> true로 판정된다)

 

문장, 식, 연산자

  js의 소스코드는 문장의 집합이다. 문장은 문장과 식으로 구성되고 식은 식과 연산자로 구성된다. 이렇듯 js는 자기 자신을 정의할때 재귀적 정의를 사용한다. 하지만 이러한 재귀적 정의는 무한 순환하지 않는다. 문장도 식도 결국에는 자기 자신을 사용하지 않고 정의된다. 문장은 최종적으로 예약어와 식과 기호(괄호, 콜론 등)로 분해할 수 있다. 

  다음은 ES5에서 정의한 예약어이다.

명칭 설명
키워드 break, do, instanceof, typeof, case, else, new, var, catch, finally, return, void, continue. for, switch, shile, default, fi, throw, delete, in, try
미래의 예약어 class, enum, extends, super, const, export, import, implemets, let, private, public, yield, interface, package, protected, static
null 리터럴 값
true 리터럴 값
false 리터럴 값

 

식별자

  식별자는 변수명, 함수명 등 개발자가 프로그램 안에서 정의한 단어이다. 식별자는 다음과 같은 규칙을 따른다

    1. 예약어 이외의 단어

    2. true, false, null 이외의 단어

    3. Unicode의 비공백 문자로 시작하고, 그 다음에 Unicode문자 또는 숫자가 이어지는 단어

    4. 단어 길이에 대한 제약은 없음

 

문장

  프로그래밍 언어에서 문장(statement)은 해당 언어의 문법에 의해 명확히 정의된 구문(syntax)규칙으로 정해진다. 문장은 프로그램을 실행할 때 실행(execute)된다. 따라서 절차지향 프로그래밍을 기준으로 문장을 차례대로 실행하는 것이 프로그램이 실행되는 과정이다.

  소스코드에서 문장과 실행 시 실행 단계의 단위는 1대1대응이 아닐 수 있지만 프로그램의 실행은 문장을 하나씩 실행해 가는 과정이다. js에서 문장의 끝은 세미콜론이다. 하지만 js에서 세미콜론을 생략하면 세미콜론을 자동으로 보완하는 사양이 있기 때문에 생략을 해도 어느 정도 제대로된 동작이 이루어진다. 이런 프로세스를 ASI(Automatic Semicolon Insertion)이라 한다.

  ASI는 다음과 같은 규칙을 통해 세미콜론을 삽입한다.

    1. line terminator('\n'등)가 있을때

    2. '}'가 있을때

    3. 파일의 끝에 도달했을때

    4. return, break, throw, continue가 있을때

  따라서 세미콜론을 생략하면 다음과 같은 에러가 발생한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//1번에 의해
const hey = 'hey'
const you = 'hey'
const heyYou = hey + ' ' + you
 
['h''e''y'].forEach((letter) => console.log(letter))
 
//다음과 같이 해석
//Uncaught TypeError: Cannot read property 'forEach' of undefined
const hey = 'hey';
const you = 'hey';
const heyYou = hey + ' ' + you['h''e''y'].forEach((letter) => console.log(letter))
 
//4번에 의해
//undefined 반환
(() => {
  return
  {
    color: 'white'
  }
})()
cs

  식문장

    식문장은 식 자체가 문장으로 되는것이다. 일부 식만을 식문장으로 쓸 수 있는 자바와는 다르게 js에서는 모든 식이 식문장이다. 따라서 이때문에 버그가 생길 수 있다. 다음과 같은 코드가 있다고 해보자

1
2
const a;
== 0;
cs

2번째 줄은 아무런 의미가 없는 코드이지만 js에서는 오류가 발생하지 않는다. 만약 =를 ==로 잘못 쓴거여도 문법 오류가 발생하지 않는다.

 

for in 문의 주의 사항

  1. 프로퍼티를 나열하는 순서가 정해져 있지 않다.

    객체 리터럴 식에 쓴 순서대로 동작이 되기도 하지만 원래 프로퍼티 간의 순서는 존재하지 않는다. 배열의 경우 순서를 의식하는 데이터이다. 하지만 이 역시 for in 문은 순서를 보장하지 않는다.

  2. 열거할 수 없는 프로퍼티

    for in 문에서 나열할 수 없는 프로퍼티가 존재 한다. 배열 객체의 경우 length가 나열되지 않는다.

  3. 프로토타입 상속한 프로퍼티

    for in 문은 프로토타입 상속한 프로퍼티도 나열한다.

 

식의 평가

  전통적으로 문작을 실행한다(exeucte)는 거서에 대해 식을 평가한다(evaluate)라고 한다. 식의 평가는 기본적으로 피연산자(의 식)를 먼저 평가 하고 연산자를 적용한다. 하지만 일부 연산자(||, &&, 삼항 연산자)의 경우 예외적으로 피연산자의 평가를 뒤로 미룬다. 따라서 피연산자를 평가하지 않는 일도 있다(ex. 삼항 연산자).

  식 안의 어디부터 평가를 하는 지는 연산자의 우선 순위에 의해 결정 된다. 식을 평가하면 결과를 얻고 이런 결과를 평가값이라 한다. 

 

delete연산

  delete연산의 연산 결과는 불린이다. 해당 프로퍼티를 제거할 수 있거나 지정한 프로퍼티가 존재하지 않으면 ture를, 그 밖의 모든 경우는 false를 반환 한다. 

  전역 변수는 내부적으로는 전역 객체의 프로퍼티이지만 전역 변수를 피연산자로 하는 delete의 경우 예외가 있다. var를 통해 선언한 전역 변수는 제거할 수 없고 암묵적 전역 변수는 제거할 수 있다.

 

콤마 연산자

  콤마 연산자는 이항 연산자이다. 콤마 연산자는 좌측 피연산자, 우측 피연산자 순으로 평가 한다. 콤마식의 연산 결과는 오른쪽 피연산자의 값이다. 따라서 연산 결과의 타입은 피연산자에 의존한다.

문자열과 참조

  js에서 문자열은 primitive type이지만 내부 구현상으로는 참조의 복사를 한다. 또 한 문자열 타입은 불변이다. 따라서 내부 구현은 참조 타입이라도 언어 상양상으로는 값 타입으로 여겨진다.

 

변수와 프로퍼티

  js에서 변수는 프로퍼티이다. 즉, 변수와 프로퍼티는 같다. 전역 변수는 전역 객체의 프로퍼티이다. 따라서 다음과 같이 글로벌 변수를 접근할 수 있다.

  함수가 호출 되었을 때 암묵적으로 객체가 생성 된다. 이때 이런 객체를 Call 객체라 한다.

  코드상에서 변수명을 쓸 때 그 값을 꺼내거나 좌변에 쓸 때 대입할 곳을 찾는 것을 변수명 해석이라 한다. 함수 안에서 변수명 해석은 Call객체의 프로퍼티, 전역 객체의 프로퍼티 순으로 찾는다. 

 

객체

https://iskull-dev.tistory.com/174

 

JavaScript에서의 객체

js의 객체를 형식적으로 정의 하면 프로퍼티의 집합이다. 프로퍼티는 name: value쌍으로 나타내어진다. 프로퍼티의 값에는 어떤 타입이라도 정의가 가능하다. 객체지향 프로그래밍의 흐름은 특징

iskull-dev.tistory.com

 

함수와 클로저

https://iskull-dev.tistory.com/199?category=918847 

 

함수와 클로저

함수를 절차를 정리할 목적으로 사용하지만 단순히 그 뿐만 아니라 함수 자체를 연산의 대상으로 삼는 것과 클로저를 이해해야 함수형 프로그래밍을 이해할 수 있다. 함수 선언문과 함수 리터

iskull-dev.tistory.com

 

배열의 내부

  여러 언어들에서 배열은 암묵적으로 연속된 메모리 영역을 의미한다. 하지만 JS에서 배열은 객체이므로 평범한 구현으로는 연속하는 메모리 영역이 되지 않는다. 현재 보급된 JS 엔진에서는 작은 배열에는 연속된 메모리를, 큰 배열에는 보통 객체의 프로퍼티처럶 처리한다.

 

클라이언트 사이드 자바스크립트

https://iskull-dev.tistory.com/201

 

Client-side Javascript

JS의 역할 JS의 역할 중 하나는 좋은 UX를 제공하는 것이다. 하지만 다음과 같은 이유로 JS만으로 모든 기능을 실현할 수 없다. 1. 많은 브라우저에서 JS를 실행하지 않게 설정할 수 있다. 2. 사용자

iskull-dev.tistory.com