자바스크립트 공부 일지 2

자바스크립트 학습

컴파일과 인터프리터


대표적인 컴파일 언어로는 C언어, Java가 있다. 인터프리터는 대표적으로 Python, Javascript가 있다. 컴파일언어는 소스코드로 이루어진 프로그램이 실행되기 전 기계 언어로 변환한 후 실행할 수 있다. 인터프리터 언어의 경우 실행과 동시에 한 줄 한 줄 해석하고 실행합니다. 중간언어로 해석해 실행하여 별도 컴파일이 필요없어 빠른 실행이 가능하지만 중간중간 오류를 검출할 가능성이 있다.

성능/안정성과 편의성의 Trade-off


프로그래밍 언어가 실행되는 환경에서 언어 자체적으로 더 많은 것을 지원(메모리 관리, 타입 추론) 등의 언어는 성능과 때로 안정성이 "일반적"으로 떨어집니다. 프로그램이 동작하기 위해 메모리라는 저장 공간과 연산 능력이 필요합니다. 직접 프로그램에 필요한 메모리 시작 주소 및 크기를 할당, 관리하고 저장공간이 필요없어지면 풀어주는 코드까지 작성한다면 개발 난이도는 매우 높아질 것입니다.
반대로 적당히 프로그래밍 언어가 최적화 해서 메모리를 할당, 필요없어진다면 공간을 비워주는 등 여부를 추론해 개발을 도와준다면 난이도와 시간은 줄어들 것입니다. 다만 편의성을 제공해준 만큼 별도의 처리를 더 하는 것이기 때문에 프로그램 전체적인 성능에서 손해를 볼 수 있습니다. 이러한 관계를 Trade-off로 볼 수 있죠.

프로그래밍 패러다임


대표적인 프로그래밍 패러다임으로 객체지향, 함수형이 있습니다. 프로그래머들이 프로그램을 상호작용하는 객체들의 집합을 정의한 객체지향 프로그래밍, 상태값을 지니지 않는 함수값들의 연속으로 프로그램이 실행 되는 함수형 프로그래밍이 있습니다. 패러다임이란 프로그래머밍의 관점을 갖게 해주고. 결정적인 역할을 한다고 볼 수 있습니다.
프로그램을 작성 할 때 설계단계와 개발, 문제 해결을 위한 특정한 방법론과 사고의 흐름을 공유하는 것을 패러다임이라고 정의할 수 있습니다.
이러한 패러다임은 프로그래밍 언어가 지원하지 않습니다. 이 부분은 ECMA에 대해 찾아보는 것이 좋습니다. 최근에 들어서는 패러다임으로 절차형 프로그래밍,객체지향 프로그래밍,함수형 프로그래밍으로 나뉘고 있습니다.

자바스크립트의 역사


초창기 자바스크립트는 웹 페이지의 보조적인 기능을 수행하기 위한 한정적인 용도로 사용되었습니다. 대부분의 로직은 웹 서버에서 실행되었고 자바스크립트는 주로 브라우저에서 이 서비스를 이용하는 클라이언트단에서 보조적으로 사용되어 왔습니다. 브라우저는 서버로부터 전달받은 HTML, CSS를 단순히 렌더링하는 수준이였습니다.
이후 1999년, 자바스크립트를 이용해 서버와 브라우저가 비동기 방식으로 데이터를 교환하는 등 통신 기능 Ajax. XMLHttpRequest라는 이름으로 등장했습니다. 이전 웹 페이지는 html태그로 시작해. html태그로 끝나는 완전한 HTML코드를 서버로부터 전송받아 웹 페이지 전체를 렌더링 하는 방식으로 동작했습니다. 따라서 화면이 서버로부터 새로운 HTML을 전송 받아 웹페이지 전체를 처음부터 다시 렌더링하여 클라이언트에게 전송했습니다.

이런 방식은 변경할 필요 없는 부분까지 포함된 HTML코드를 서버로부터 전송받기 때문에 불필요한 데이터 통신 및 성능 면에서 불리합니다. 이로 인해 화면이 전환되면 순간적으로 화면이 깜빡이는 등 웹페이지의 어쩔 수 없는 한계로만 받아들여졌습니다.

Ajax 등작은 이전 패러다임을 획기적으로 전환했습니다. 웹 페이지의 변경이 필요 없는 부분은 다시 렌더링하지 않고, 서버로부터 필요한 데이터만 전송받아 변경해야 하는 부분만 한정적으로 렌더링 하는 방식이 가능해진 것입니다. 이로써 웹 브라우저에서도 데스크톱 애플리케이션과 유사한 빠른 성능과 부드러운 화면 전환이 가능해졌습니다.

이후 2005년, 구글이 발표한 구글 맵스부터 시작해 자바스크립트의 크로스브라우징 및 여러 이슈로 변모하게 되었습니다. 웹 브라우저에서 자바스크립트와 Ajax를 기반으로 한 구글 맵스가 데스크톱 애플리케이션과 비교했을 때 손색 없을 정도로 부드러운 화면 전환을 보여주었습니다.

2006년, 제이쿼리의 등장으로 DOM을 더욱 쉽게 제어할 수 있게 되어 크로스 브라우징 이슈도 어느정도 해결이 되었습니다.

이후 V8자바스크립트 엔진의 등장으로 더욱 주목받게 되었습니다. 구글 맵스를 통해 웹 애플리케이션 프로그래밍 언어로 가능성이 확인된 자바스크립트로 웹 애플리케이션을 구축하려는 시도가 늘어감에 따라 더욱 빠르게 동작하는 자바스크립트 엔진의 필요성이 대두되었습니다. 2008년 등장한 구글의 V8 자바스크립트 엔진은 이런 요구에 부합한 빠른 성능을 보여주었고 이후 이를 이용해 데스크톱 애플리에이션과 유사한 사융자 경험(UX)를 제공하는 웹 애플리케이션 언어로 정착하게 되었습니다. 이 엔진의 등장으로 과거 웹 서버에서 수행되던 로직들이 대거 클라이언트(브라우저)로 이동하고, 웹 애플리케이션 개발에서 프런트엔드 영역이 주목받는 계기로 작용되기도 했습니다.

자바스크립트를 이용해 서버도 구현할 수 있다는 가능성도 대두되었습니다. 이 가능성은 곧 2009년 발표된 라이언 달(Ryan Dahl)의 V8 자바스크립트 엔진으로 빌드된 자바스크립트 런타임 환경 Node.js 으로 이어졌습니다.
Node.js는 브라우저에서만 동작하던 자바스크립트를 이외 환경에서 작동할 수 있도록 자바스크립트 엔진을 브라우저에서 독립시킨 실행 환경을 제공했습니다. Node.js는 서버 애플리케이션 개발에 주로 사용되며 이외 필요한 파일시스템, HTTP통신 등의 API를 제공합니다.
무엇보다 자바스크립트를 프런트엔드, 백엔드 영역에서 사용할 수 있는 장점이 크게 작용했습니다. 이는 별도의 언어 학습을 덜게 해주었습니다.
Node.js의 등장으로 자바스크립트는 브라우저를 벗어나 서버 사이드 애플리케이션 개발에도 사용할 수 있는 범용 프로그래밍 언어가 되었습니다. 브라우저에서만 작동하던 자바스크립트는 이제 프런트엔드 영역은 물론 백엔드 영역까지 어우르는 웹 프로그래밍 언어의 표준으로 자리잡게 된 것입니다.

이에 자바스크립트는 더욱 발전해 크로스 플랫폼(PC, 스마트폰, 테블릿) 등에서 사용할 애플리케이션 제작에 쓰이기 시작했습니다. 웹은 물론 모바일까지 지원하는 하이브리드 앱, 서버 사이드(Node.js), 데스크톱(Electron), 머신러닝(TensorFlow.js) 환경을 위한 프로그래밍 언어로서 세계에서 가장 인기 있는 프로그래밍 언어로 변모하게 된 것입니다.

자바스크립트 기본 정리


모던 자바스크립트 튜토리얼을 공부하고 자바스크립트에 대해 정리하겠습니다.

1. 모던 마크업


script태그 <script> 태그를 이용하면 자바스크립트 프로그램을 HTML 문서 대부분 위치에 삽입할 수 있습니다.

html

<!DOCTYPE HTML>
<html>

<body>

  <p> 스크립트 전</b>

  <script>
    alert('Hello, world');
  </script>

  <p> 스크립트 후</b>

</body>

</html>

<script>태그엔 몇 가지 속성(attribute)가 존재합니다.

type 속성

HTML4에선 스크립트에 type 명시가 필수였습니다. type=text/javascript속성이 붙은 스크립트를 자주 볼 수 있었죠, 이젠 이런 타입 명시가 필수가 아닙니다.

language 속성

이 속성은 현재 사용하는 스크립트 언어를 나타냅니다. 지금은 기본 언어가 js이므로 속성의 의미가 없게됐습니다.

외부 스크립트

자바스크립트 코드의 양이 많은 경우 파일로 소분하여 작업한 뒤 이를 HTML 태그 내 삽입합니다.

html
<script src="/js/script1.js"></script>

절대 경로 및 상대 경로 혹은 URL로도 삽입할 수 있습니다.

주의

HTML 안에 직접 스크립트를 작성하는 방식은 스크립트가 아주 간단한 경우에 사용합니다. 스크립트를 별도 파일에 작성하면 브라우저가 스크립트를 다운받아 캐시에 저장하기 때문에 성능상 이점이 존재합니다.

요약
  • 웹 페이지에 자바스크립트 코드를 추가하기 위해 <script> 태그를 사용합니다.
  • type 과 language 속성은 필수가 아닙니다.
  • 외부 스크립트 파일은 <script src="path/to/script.js"></script>와 같이 삽입합니다.

2. 코드 구조

처음으로 배울 것은 코드 블록을 만드는 방법입니다.

문(statement)은 어떤 작업을 수행하는 문법 구조(syntax structure)와 명령어(command)를 의미합니다. 이때 서로 다른 문은 세미콜론으로 구분합니다.

세미 콜론

자바스크립트에서 줄 바꿈이 존재한다면 세미콜론을 생략할 수 있습니다. 단 다음과 같은 경우 세미콜론을 자동으로 삽입해주지 않습니다.

js
[1, 2].forEach()

지금은 에러가 발생하지 않습니다.

js
alert("에러가 발생합니다.")
[1, 2].forEach(alert)

세미콜론이 없을 때 에러가 발생하는 이유는 자바스크립트가 대괄호 [...]앞에는 세미콜론이 있다고 가정하지 않기 때문입니다. 서로 다른 이기 때문입니다. 문이 잘못 합쳐지면서 에러가 발생하게 되는 것입니다. 자바스크립트 커뮤니티에서도 문 사이에는 세미콜론을 넣기를 권장하고 있습니다.

3. 엄격 모드

자바스크립트는 기존에 작성한 코드는 변경하지 않으면서 새로운 기능들이 대거 추가되어 왔습니다. 하지만 자바스크립트 창시자들이 했던 실수나 불완전한 결정이 언어 안에 영원히 박제되기도 합니다. 이는 ES5가 등장하기 전 2009년까지 지속되어왔습니다. ES5의 등장으로 새로운 기능이 추가되고 기존 기능 중 일부가 변경되었습니다. 변경이 됨에 따라 하위 호환성 문제가 발생할 수 있습니다. 그래서 변경사항 대부분은 ES5 기본 모드에선 활성화되지 않도록 설계되었습니다. 대신 use strict라는 특별한 지시자를 사용하면 염격 모드가 활성화 되어 변경사항이 활성화됩니다.

use strict

지시자 use strict, 혹은 use strict는 단순 문자열처럼 생겼습니다. 하지만 이 짓시자가 스크립트 최상단에 존재하면 스크립트 전체가 모던한 방식으로 동작합니다.

js
"use strict";

// 이 코드는 모던한 방식으로 실행됩니다.

"use strict"는 스크립트 최상단이 아닌 함수 본문 맨 앞에 올 수 있습니다. 엄격 모드가 스크립트 전체에 작용하는 것이죠.

🚧 use strict는 반드시 최상단에. "use strict"는 스크립트 최상단에 존재해야 합니다. 그렇지 않으면 엄격 모드가 활성화되지 않을 수 있습니다.

🚧 use strict를 취소할 방법은 없습니다. 자바스크립트 엔진을 이전 방식으로 되돌리는 "no use strict"같은 지시자는 존재하지 않습니다.

브라우저 콘솔

개발한 기능을 테스트하기 위해 브라우저 콘솔. 즉 개발자 도구를 이용하는 경우 use strict는 적용되어 있지 않습니다. 개발자 도구로 use strict를 이용하려면 다음과 같이 코드를 작성하는것이 좋습니다.

js
// 개발자 도구 console
'use strict';
// ...테스트하려는 코드
반드시 사용해야 하나요?

꼭 그렇지 않습니다. 클래스와 모듈을 사용하게 된다면 use strict는 자동으로 적용됩니다. 결론은 코드를 클래스와 모듈을 사용해 구성한다면 use strict를 생략해도 됩니다. 엄격 모드를 사용하면 개발자의 삶의 질이 조금 더 높아지게 됩니다. 이후 알아보도록 하겠습니다.

4. 좋은 주석

설명이 담긴 주석은 대개 좋지 않습니다. 아래와 같은 주석을 사용하는것이 좋습니다.

아키텍처를 설명하는 주석

고차원 수준 컴포넌트의 개요, 상호작용에 대한 설명 및 상황에 따른 제어 흐름 등 주석을 첨부하는 것이 좋습니다. 고차원 수준의 아키텍처 다이어그램을 그리는 데 쓰이는 언어인 UML도 시간을 내어 공부해보도록 합시다.

함수 용례와 매개변수 정보를 담은 주석

JSDoc이라는 특별한 문법을 사용하면 함수에 관한 문서를 쉽게 작서할 수 있습니다. 함수 용례, 매개변수, 반환 값 정보가 들어갑니다.

js
/**
 * x를 n번 곱한 수를 반환함
 *
 * @param {number} x 거듭제곱할 숫자
 * @param {number} n 곱할 횟수, 반드시 자연수여야 함
 * @return {number} x의 n 거듭제곱을 반환함
 */
function pow(x, n) {
  ...
}

코드를 보지 않고도 함수의 목적과 사용법을 한눈에 알 수 있습니다.

5. 자료형

null

null 값은 자료형 중 어느 자료형에도 속하지 않는 값입니다. null값은 오로지 null값만 포함하는 별도의 자료형을 만듭니다.

js
let age = null

자바스크립트에서 null은 '존재하지 않는 값(nothing)', '비어 있는 값(empty)', '알 수 없는(unknown)'값을 나타내는데 사용합니다.
위 구문에서는 나이(age)를 알 수 없거나 비어있음을 보여주죠.

undefined

undefined 값 undefined 값도 null 값처럼 자신만의 자료형을 형성합니다. 'undefined'는 값이 할당되지 않는 상태를 나타낼 때 사용합니다.
변수는 선언했지만, 값을 할당하지 않았다면 해당 변수에 undefined가 자동으로 할당됩니다.

js
let age;

alert(age); // undefined

만일 변수가 '비어있거나', '알 수 없는' 상태라는 걸 나타내려면 null을 사용하는것이 좋습니다. undefined는 값이 할당되지 않은 변수의 초기값을 위해 예약어로 남겨두는 것이 좋습니다.

typeof

typeof구문은 인수의 자료형을 나타냅니다.

js
typeof undefined // "undefined"
typeof 0 // "number"
typeof 10n // "bigint"
typeof true // "boolean"
typeof "foo" // "string"
typeof Symbol("id") // "symbol"
typeof Math // "object" (1)
typeof null // "object" (2)
typeof alert // "function" (3)

Math

  • Math는 수학 연산을 제공하는 내장 객체입니다.

typeof null

  • object로 타입이 표시되는것은 null은 별도의 고유한 자료형을 가지는 특수 값으로 객체는 아니지만, 하위 호환성을 유지하기 위해 오류를 수정하지 않고 남겨둔 상황입니다. 언어 자체의 오류이므로 null 은 객체가 아님에 유의해야합니다.

typeof

  • typeof alert는 "function"을 출력해줍니다. 그런데 '함수'형은 따로 존재하지 않습니다. 함수는 객체형에 속합니다. 이는 아주 오래전 만들어진 규칙이기 때문에 하위 호환성 유지를 위해 남겨진 상태입니다.
자료형의 종류

자바스크립트에는 여덟 가지 기본 자료형이 존재합니다.

  • 숫자형 – 정수, 부동 소수점 숫자 등의 숫자를 나타낼 때 사용합니다. 정수의 한계는 ±253 입니다.
  • bigint – 길이 제약 없이 정수를 나타낼 수 있습니다.
  • 문자형 – 빈 문자열이나 글자들로 이뤄진 문자열을 나타낼 때 사용합니다. 단일 문자를 나타내는 별도의 자료형은 없습니다.
  • 불린형 – true, false를 나타낼 때 사용합니다.
  • null – null 값만을 위한 독립 자료형입니다. null은 알 수 없는 값을 나타냅니다.
  • undefined – undefined 값만을 위한 독립 자료형입니다. undefined는 할당되지 않은 값을 나타냅니다.
  • 객체형 – 복잡한 데이터 구조를 표현할 때 사용합니다.
  • 심볼형 – 객체의 고유 식별자를 만들 때 사용합니다.

6. 형 변환

형 변환은 무엇일까요? 형을 누나로 변형시키면 형 변환일까요? 죄송합니다..
함수와 연산자에 전달되는 값은 대부분 적절한 자료형으로 자동 변환됩니다. 이 과정을 "형 변환"이라고 합니다. 이 외 전달받은 값을 의도를 갖고(주로 연산자를 통해) 원하는 타입으로 변환(명시적 반환)해 주는 경우도 형 변환이라고 할 수 있습니다.

js
alert("6" / "2"); // 3

String(true) // "ture"
Number("123") // 123
Number("임의의 문자열 123") // NaN

alert( Number("   123   ") ); // 123
alert( Number("123z") );      // NaN ("z"를 숫자로 변환하는 데 실패함)

alert( Number(true) );        // 1
alert( Number(false) );       // 0

alert( Boolean("hello") ); // 문자열(true)
alert( Boolean("") ); // 빈 문자열(false)

Boolean 타입으로 형 변환 시
숫자 0, 빈 문자열, null, undefined, NaN과 같이 직관적으로 "비어있다고" 느껴지는 값은 false가 됩니다. 그 외 값은 true가 됩니다.

Number 타입으로 형 변환 시
숫자형으로 변환 시 undefined는 0이 아닌 NaN이 됩니다. 문자열 "0"과 " "같은 공백은 불린형으로 변환 시 true가 됩니다.

7. 기본 연산자

자바스크립트에서 지원하는 수학 연산자는 다음과 같습니다.

  • 덧셈 연산자 +,
  • 뺄셈 연산자 -,
  • 곱셈 연산자 *,
  • 나눗셈 연산자 /,
  • 나머지 연산자 %,
  • 거듭제곱 연산자 **
나머지 연산자

나머지 연산자 %는 기호로 나타내지만, 비율을 나타내는 퍼센트와 관련이 없습니다. 나머지 연산자는 사용한 표현식을 나눈 후 나머지를 정수로 반환해줍니다.

js
alert( 5 % 2 ) //1 
거듭제곱 연산자

거듭제곱 연산자 a ** b는 a를 b번 곱한 값이 반환됩니다.

js
alert( 2 ** 4 ) // 16 (2 * 2 * 2 * 2)

// 제곱근
alert( 8 ** (1/3) ); // 2 (1/3 거듭제곱은 세제곱근)
이항 연산자와 문자열 연결

이항 연산자 +의 피연산자로 문자열이 전달되면 덧셈 연산자는 문자열을 병합합니다

js
alert( '1' + 2 ); // "12"
alert(2 + 2 + '1' ); // "41"
alert( 6 - '2' ); // 4, '2'를 숫자로 바꾼 후 연산이 진행됩니다.
alert( '6' / '2' ); // 3, 두 피연산자가 숫자로 바뀐 후 연산이 진행됩니다.
이항 덧셈 연산자

문자열을 숫자로 변환해야 하는 경우 다음과 같이 피연산자에 기호를 붙여 숫자로 변환할 수 있습니다.

js
let apples = "2";
let oranges = "3";

// 이항 덧셈 연산자가 적용되기 전에, 두 피연산자는 숫자형으로 변화합니다.
alert( +apples + +oranges ); // 5
연산자 우선순위

연산자는 우선순위를 가집니다. 우선순위 테이블의 일부입니다.

증감 연산자의 전위형과 후위형

증감 연산자의 전위형, 후위형은 다음 차이점이 존재합니다. 전위형은 증가, 감소 후 새로운 값을 반환하는 한편, 후위형은 증가, 감소 전 기존 값을 반환합니다.

js
let counter = 1;
let a = ++counter; // (*)

alert(a); // 2

let counter = 1;
let a = counter++; // (*) ++counter를 counter++로 바꿈

alert(a); // 1

전위형과 후위형의 내용을 정리하면 아래와 같습니다.

  • 반환 값을 사용하지 않는 경우라면, 전위형과 후위형엔 차이가 없습니다.
  • 값을 증가시키고 난 후, 증가한 값을 바로 사용하려면 전위형 증가 연산자를 사용하면 됩니다.
js
let counter = 0;
alert( ++counter ); // 1
  • 값을 증가시키지만, 증가 전의 기존값을 사용하려면 후위형 증가 연산자를 사용하면 됩니다.
js
let counter = 0;
alert( counter++ ); // 0

증감 연산자는 다른 연산자보다 우선순위가 높습니다.

js
let counter = 1;
alert( 2 * ++counter ); // 4

let counter = 1;
alert( 2 * counter++ ); // counter++는 '기존'값을 반환하기 때문에 2가 출력됩니다.
총 정리

연산에 따른 형 변환 및 우선순위에 따른 결과는 다음과 같습니다.

js
"" + 1 + 0 = "10" 
"" - 1 + 0 = -1
true + false = 1
6 / "3" = 2
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
7 / 0 = Infinity
"  -9  " + 5 = "  -9  5"
"  -9  " - 5 = -14
null + 1 = 1 // 숫자형으로 변환 시 null은 0이 됩니다.
undefined + 1 = NaN // undefined는 숫자형으로 변환시 NaN이 됩니다.
" \t \n" - 2 = -2 // 문자열이 숫자형으로 변할 땐 문자열 앞뒤의 공백이 삭제됩니다. 뺄셈 연산자 앞의 피연산자는 공백을 만드는 문자 \t와 \n, 그 사이의 “일반적인” 공백으로 구성됩니다. 따라서 " \t \n"는 숫자형으로 변환 시 길이가 0인 문자열로 취급되어 숫자 0이 됩니다.

8. 비교 연산자

비교 연산자는 다음과 같이 사용할 수 있습니다.

  • 보다 큼·작음: a > b, a < b
  • 보다 크거나·작거나 같음: a >= b, a <= b
  • 같음(동등): a == b. 등호 =가 두 개 연달아 오는 것에 유의하세요. a ​​= b와 같이 등호가 하나일 때는 할당을 의미합니다.
  • 같지 않음(부등): 같지 않음을 나타내는 수학 기호 ≠는 자바스크립트에선 a != b로 나타냅니다. 할당연산자 = 앞에 느낌표 !를 붙여서 표시합니다.
문자열 비교

자바스크립트는 '사전'순으로 문자열을 비교합니다. 등재 순서를 정하는 것과 같이 자바스크립트도 문자열을 구성하는 문자 하나하나 비교하며 문자열을 비교합니다.

js
alert( 'Z' > 'A' ); // true
alert( 'Glow' > 'Glee' ); // true
alert( 'Bee' > 'Be' ); // true

문자열 비교시 적용되는 알고리즘은 다음과 같습니다.

text
1. 두 문자열의 첫 글자를 비교합니다.
2. 첫 번째 문자열의 첫 글자가 다른 문자열의 첫 글자보다 크면(작으면), 첫 번째 문자열이 두 번째 문자열보다 크다고(작다고) 결론 내고 비교를 종료합니다.
3. 두 문자열의 첫 글자가 같으면 두 번째 글자를 같은 방식으로 비교합니다.
4. 글자 간 비교가 끝날 때까지 이 과정을 반복합니다.
5. 비교가 종료되었고 문자열의 길이도 같다면 두 문자열은 동일하다고 결론 냅니다. 비교가 종료되었지만 두 문자열의 길이가 다르면 길이가 긴 문자열이 더 크다고 결론 냅니다.

정확히는 문자열이 사전 순이 아닌 유니코드 순입니다.

다른 형을 가진 값 간의 비교

비교 값의 자료형이 다르다면 자바스크립트는 이 값들을 숫자형으로 변환한 뒤 비교합니다.

js
alert( '2' > 1 ); // true, 문자열 '2'가 숫자 2로 변환된 후 비교가 진행됩니다.
alert( '01' == 1 ); // true, 문자열 '01'이 숫자 1로 변환된 후 비교가 진행됩니다.

Boolean값의 경우 true는 1, false는 0으로 변환된 후 비교가 이뤄집니다.

js
alert( true == 1 ); // true
alert( false == 0 ); // true

물론 다음과 같은 명시적 형 변환을 통한 경우 규칙이 다르게 적용됩니다.

js
let a = 0;
alert( Boolean(a) ); // false

let b = "0";
alert( Boolean(b) ); // true

alert(a == b); // true!
동등 연산자 사용 시 피연산자의 형 변환

동등 연산자 ==은 0과 false를 구분하지 못합니다.

js
alert( 0 == false ); // true
alert( '' == false ); // true

동등 연산자 ==가 형이 다른 피연산자를 비교할 때 피연산자를 숫자형으로 바꾸기 때문에 발생합니다. 빈 문자열 false는 숫자형으로 변환 시 0이 됩니다.

그렇다면 형 변환 없이 0과 false 자체를 구별하려면 어떻게 해야할까요? 일치 연산자 ===를 사용하면 됩니다.
일치 연산자는 엄격한(strict) 동등 연산자입니다. 자료형의 여부도 검사하죠. 때문에 피연산자 a와 b의 형이 다른 경우 a === b 는 즉시 false를 반환합니다.

js
alert( 0 === false ); // false, 피연산자의 형이 다르기 때문입니다.
alert( null === undefined ); // false
alert( null == undefined ); // Object == Object. true
null vs 0

null과 0을 비교해 봅시다.

js
alert( null > 0 );  // (1) false
alert( null == 0 ); // (2) false
alert( null >= 0 ); // (3) true

비교 연산자 >와 >= 이 적용되었을때 null은 숫자형으로 변경됩니다. 동등 연산자의 경우는 형 변환을 하지 않습니다.

비교 불가능한 undefined

undefined는 다른 값과 비교해서는 안 됩니다.

js
alert( undefined > 0 ); // false (1)
alert( undefined < 0 ); // false (2)
alert( undefined == 0 ); // false (3)
  • (1)과(2)에선 undefined가 NaN으로 변환되는데(숫자형으로의 변환), NaN이 피연산자인 경우 비교 연산자는 항상 false를 반환합니다.
  • undefined는 null이나 undefined와 같고, 그 이외의 값과는 같지 않기 때문에 (3)은 false를 반환합니다.

9. 논리 연산자

자바스크립트엔 세 종류의 논리 연산자 ||(OR), &&(AND), !(NOT)이 있습니다. 연산자에 '논리'라는 수식어가 붙긴 하나 피연산자로 Boolean형 뿐 아닌 모든 type의 값을 받을 수 있습니다. 연산 결과 역시 모든 타입이 될 수 있습니다.

OR 연산

OR 연산은 ||을 이용합니다. 조건 중 하나라도 참 이라면 결과는 이죠.

첫 번째 true를 찾는 OR 연산자

기존에 알고 있던 OR 연산에 추가적인 기능에 대해 알아봅시다. 다음과 같이 OR연산자와 피연산자가 여러 개인 경우

js
result = value1 || value2 || value3;

이때, OR 연산은 다음 순서에 따라 연산을 수행합니다.

  • 가장 왼쪽 피연산자부터 시작해 오른쪽으로 나아가며 피연산자를 평가합니다.
  • 각 피연산자를 불린형으로 변환합니다. 변환 후 그 값이 true이면 연산을 멈추고 해당 피연산자의 변환 전 원래 값을 반환합니다.
  • 피연산자 모두를 평가한 경우(모든 피연산자가 false로 평가되는 경우)엔 마지막 피연산자를 반환합니다.

여기서 핵심은 반환 값이 형 변환을 하지 않은 원래 값이라는 것입니다.

js
alert( 1 || 0 ); // 1 (1은 truthy임)

alert( null || 1 ); // 1 (1은 truthy임)
alert( null || 0 || 1 ); // 1 (1은 truthy임)

alert( undefined || null || 0 ); // 0 (모두 falsy이므로, 마지막 값을 반환함)
변수 또는 표현식으로 구성된 목록에서 true값 추출하기

OR(||)을 이용하면 실제 값이 들어있는 변수를 찾고, 값을 보여줍니다. 다음 코드를 봅시다. 조건에 부합하지 않는 다면 마지막 true인 익명을 반환 할 것입니다.

js
let firstName = "";
let lastName = "";
let nickName = "바이올렛";

alert( firstName || lastName || nickName || "익명"); // 바이올렛
단락 평가를 통해 나머지 값의 평가 멈추기

단락 평가의 동작 방식은 두 번째 피연산자가 변수 할당과 같은 부수적인 효과를 가지는 표현식 일 때 명확히 볼 수 있습니다.

아래 코드에서 첫 번째 줄 ||연산자는 true를 만나자마자 평가를 멈추어 alert가 실행되지 않습니다. 이런 단락 평가는 연산자 왼쪽 조건이 false인 경우 명령을 실행하고자 할 때 쓰입니다.

js
true || alert("not printed"); // 미실행
false || alert("printed"); // 실행
AND 연산

AND 연산은 &&을 이용합니다. 모든 조건이 참이면 을 반환하죠.

첫 번째 false를 찾는 AND 연산자

AND 연산자와 피연산자가 여러 개인 경우를 봅시다.

js
result = value1 && value2 && value3;

AND 연산자 &&는 다음과 같이 동작합니다.

  • 가장 왼쪽 피연산자부터 시작해 오른쪽으로 나아가며 피연산자를 평가합니다.
  • 각 피연산자는 불린형으로 변환됩니다. 변환 후 값이 false이면 평가를 멈추고 해당 피연산자의 변환 전 원래 값을 반환합니다.
  • 피연산자 모두가 평가되는 경우(모든 피연산자가 true로 평가되는 경우)엔 마지막 피연산자가 반환됩니다.

AND 연산은 OR연산자와 달리 첫 번째 false값을 반환합니다.

js
// 첫 번째 피연산자가 truthy이면,
// AND는 두 번째 피연산자를 반환합니다.
alert( 1 && 0 ); // 0
alert( 1 && 5 ); // 5

// 첫 번째 피연산자가 falsy이면,
// AND는 첫 번째 피연산자를 반환하고, 두 번째 피연산자는 무시합니다.
alert( null && 5 ); // null
alert( 0 && "아무거나 와도 상관없습니다." ); // 0

만일 모든 조건이 참이라면 마지막 피연산자가 반환됩니다.

js
alert( 1 && 2 && 3 ); // 마지막 값, 3
OR 그리고 AND 는 연산자죠. 우선순위가 존재합니다. 여기서 AND가 우선순위가 더 높습니다.
NOT 연산

NOT 연산은 !를 이용합니다. NOT연산은 인수를 하나만 받으며 다음 순서로 연산을 진행합니다.

  • 피연산자를 불린형(true / false)으로 변환합니다.
  • 1에서 변환된 값의 역을 반환합니다.
js
alert( !true ); // false
alert( !0 ); // true

NOT 연산을 두번 !!를 사용하면 값을 Boolean형으로 변환할 수 있습니다.

js
alert( !!"non-empty string" ); // true. 피연산자로 받은 값을 불린형으로 변환한 후 이 값의 역을 반환
alert( !!null ); // false. 첫 번째 NOT 연산자가 반환한 값의 역을 반환
NOT 연산은 모든 논리 연산 중 가장 높습니다.

10. 반복문

자바스크립트의 반복문으로 while do-while for이 세 가지가 있다.

여러 개의 중첩 반복문을 한 번에 빠져나오기

다음은 i와 j를 반복하면서 프롬프트 창에 (0,0)부터 (2,2)까지를 구성하는 좌표 (i,j)를 입력하게 해주는 예시입니다.

js
for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`(${i},${j})의 값`, '');

    // 여기서 멈춰서 아래쪽의 `완료!`가 출력되게 하려면 어떻게 해야 할까요?
  }
}

alert('완료!');

prompt에서 취소 버튼을 누르는 경우 중첩 for문을 즉시 탈출할 수 있어야합니다. break지시자 만으로는 한 개의 루프만 탈출할 수 있죠. 이럴 때 레이블을 사용할 수 있습니다.

js
labelName: for (...) {
  ...
}

반복문 안에서 break <labelName> 문을 사용하면 레이블에 해당하는 반복문을 빠져나올 수 있습니다.

js
outer: for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`(${i},${j})의 값`, '');

    // 사용자가 아무것도 입력하지 않거나 Cancel 버튼을 누르면 두 반복문 모두를 빠져나옵니다.
    if (!input) break outer; // (*)

    // 입력받은 값을 가지고 무언가를 함
  }
}
alert('완료!');

위 코드를 보면 break outerouter라는 레이블이 붙은 반복문을 찾고, 해당 반복문을 빠져나오게 해줍니다. 레이블은 다음과 같이 별도의 줄에 써주는 것도 가능합니다.

js
outer:
for (let i = 0; i < 3; i++) { ... }

레이블을 이용한 소수 출력하기

js
let num = prompt('상수 입력',0)

nextPrime:
for(let i = 2; i < num; i++){
  // 소수가 맞다면 이 구문부터 시작된다.
  for(let j = 2; j < i ; j++){
    if(i % j == 0) continue nextPrime;
  }
  alert(i);
}

오늘의 문제 풀이

문제 설명 문제 설명 명함 지갑을 만드는 회사에서 지갑의 크기를 정하려고 합니다. 다양한 모양과 크기의 명함들을 모두 수납할 수 있으면서, 작아서 들고 다니기 편한 지갑을 만들어야 합니다. 이러한 요건을 만족하는 지갑을 만들기 위해 디자인팀은 모든 명함의 가로 길이와 세로 길이를 조사했습니다.

아래 표는 4가지 명함의 가로 길이와 세로 길이를 나타냅니다.

명함 번호 가로 길이 세로 길이.

image

가장 긴 가로 길이와 세로 길이가 각각 80, 70이기 때문에 80(가로) x 70(세로) 크기의 지갑을 만들면 모든 명함들을 수납할 수 있습니다. 하지만 2번 명함을 가로로 눕혀 수납한다면 80(가로) x 50(세로) 크기의 지갑으로 모든 명함들을 수납할 수 있습니다. 이때의 지갑 크기는 4000(=80 x 50)입니다.

모든 명함의 가로 길이와 세로 길이를 나타내는 2차원 배열 sizes가 매개변수로 주어집니다. 모든 명함을 수납할 수 있는 가장 작은 지갑을 만들 때, 지갑의 크기를 return 하도록 solution 함수를 완성해주세요.

각 요소마다 최댓값, 최솟값을 구해 놓고 반환 시 최댓값 중 큰 값, 최솟값 중 큰 값을 곱해 반환하면 될 것 같다.
최댓값, 최솟값을 구할 수 있는 내장 함수로 Math.max, Math.min 함수가 존재한다. 이를 이용해 풀이를 진행해보자.

js
// MAth 함수를 사용하는 경우
function solution(sizes) {
  const width = [];
  const height = [];

  for(let i = 0; i < sizes.length; i++) {
      const max = Math.max(sizes[i][0], sizes[i][1]);
      const min = Math.min(sizes[i][0], sizes[i][1]);
      width.push(max); // 60 70 60 80
      height.push(min); // 50 30 30 40
  }

  // 80 * 50 = 4000
  return Math.max(...width) * Math.max(...height);
}

// Math 함수를 사용하지 않는 경우
function solution(sizes) {
  var biggerSideMax = 0;
  var smallerSideMax = 0;

  let max = []
  let min = []

  for(let i = 0; i < sizes.length ; i++) {
      if (sizes[i][0] > sizes[i][1]) {
        max.push(sizes[i][0])
        min.push(sizes[i][1])
      }else {
        max.push(sizes[i][1])
        min.push(sizes[i][0])
      }
  }

  biggerSideMax = max[0]
  smallerSideMax = min[0]

  for(let i = 1; i < sizes.length ; i++) {
    if (biggerSideMax < max[i]) {
      biggerSideMax = max[i];
    }
    if (smallerSideMax < min[i]) {
      smallerSideMax = min[i];
    }
  }

  return biggerSideMax * smallerSideMax
}

// 배열을 사용하지 않은 경우 항해의 백승호님의 코드.
function solution(sizes) {
  var biggerSideMax = 0;
  var smallerSideMax = 0;

  for(let i = 0; i < sizes.length ; i++) {
    if(sizes[i][0] > sizes[i][1]){
      if(sizes[i][0] >= biggerSideMax){
        biggerSideMax = sizes[i][0]
      }
      if(sizes[i][1] >= smallerSideMax){
        smallerSideMax = sizes[i][1]
      }
    }else{
      if(sizes[i][1] >= biggerSideMax){
        biggerSideMax = sizes[i][1]
      }
      if(sizes[i][0] >= smallerSideMax){
        smallerSideMax = sizes[i][0]
      }
    }
  }

  return biggerSideMax * smallerSideMax
}

2주차 1일 마무리

그동안 간과했던 자바스크립트 기초에서 부족한 부분을 많이 느꼈다. 복습 또 복습이 중요할 것 같다.