모던 JS 튜토리얼 Part 1. 2강 자바스크립트 기본

ᴄsᴇ ᴘᴇʙʙʟᴇ·2023년 11월 18일
0
post-thumbnail

모던 JS 튜토리얼 Part 1. 코어 자바스크립트 문서를 읽고 내용을 정리합니다.


🐑 코드 구조

세미콜론

JS는 대부분의 경우 줄 바꿈이 있다면 세미콜론(semicolon)을 생략할 수 있다.

  • 생략 가능한 경우
alert('Hello')
alert('World')

하지만, 항상 그런 것은 아니다.

  • 생략 불가능한 경우
alert(3 +
1
+ 2);
alert("에러가 발생합니다.")

[1, 2].forEach(alert)

결론은, 줄 바꿈으로 문을 나눴더라도 statement 사이엔 세미콜론을 넣는 것이 좋다는 것이다.

에디터에서의 주석 단축키

  • 한 줄 주석 처리 : Ctrl+/
  • 여러 줄 주석 처리 : Ctrl+Shift+/

cf) 중첩 주석은 지원하지 않는다. 쓸 경우 에러가 발생한다.

/*
  /* 중첩 주석 ?!? */
*/
alert( 'World' );

🐑 엄격 모드(strict mode)

엄격 모드(strict mode)란?

자바스크립트가 발전함에 따라 새로운 기능이 추가되고 기존 기능 중 일부가 변경되었다. 이로 인한 하위 호환성 문제 때문에, 변경사항 대부분이 기본적으로 활성화되지 않도록 되어있다.

대신 use strict라는 특별한 지시자를 사용해 엄격 모드(strict mode)를 설정하면, 이 변경사항이 활성화된다.

use strict

use strict는 대부분 스크립트 최상단에 위치시킨다. use strict 위에는 주석만 위치할 수 있고, 그 외의 것들은 엄격모드가 적용되지 않는다.

alert("some code");
// 하단에 위치한 "use strict"는 스크립트 상단에 위치하지 않으므로 무시됩니다.

"use strict";

// 엄격 모드가 활성화되지 않습니다.

모던 자바스크립트에서의 use strict

모던 자바스크립트에는 '클래스’와 '모듈’이라는 것이 있는데, 이 둘을 사용하면 use strict가 자동으로 적용된다. 따라서 스크립트에 use strict를 붙일 필요가 없다.

결론은, 코드를 클래스와 모듈을 사용해 구성한다면 use strict를 생략해도 된다.

🐑 변수와 상수

변수

자바스크립트에선 let 키워드를 사용해 변수를 생성한다.

let message;

한 줄에 여러 변수를 선언하는 것도 가능하다.

let user = 'John', age = 25, message = 'Hello';

하지만 가독성을 위해 한 줄에는 하나의 변수를 작성한다.

let user = 'John';
let age = 25;
let message = 'Hello';

cf) var 키워드
만들어진 지 오래된 스크립트에서 let 대신 var가 작성되어있는 경우가 있다. varlet과 거의 동일하게 동작하지만, var는 ‘오래된’ 방식이다.

변수 이름 컨벤션

자바스크립트에서는 카멜 표기법(camelCase)을 흔히 사용한다.
ex) myVeryLongName

상수

변화하지 않는 변수를 선언할 때는 const를 사용한다.

const myBirthday = '18.04.1982';

상수를 사용하는 이유는 변숫값이 절대 변경되지 않을 것이라 확신하면, 값이 변경되는 것을 방지하면서 다른 개발자들에게 이 변수는 상수라는 것을 알리기 위해서이다.

대문자 상수

'상수’는 변수의 값이 절대 변하지 않음을 의미한다. 이 상수를 두 종류로 나눌 수 있다.

  1. 코드가 실행되기 전에 이미 그 값을 알고 있는 상수. 즉 ‘하드 코딩한’ 값. 실행 전에 이미 값을 알고 있고, 코드에서 직접 그 값을 쓰는 경우
    ex) 색상코드 #FFFFFF

  2. 런타임 과정에서 계산되지만 최초 할당 이후 값이 변하지 않는 상수

1번의 경우에 대문자 상수를 작성한다.

비교 예제)

const BIRTHDAY = '18.04.1982'; // 1번 

const age = someCode(birthday); // 2번

birthday는 이미 알고 있고, 정해져있다. 반면, age는 런타임에 평가된다. 올해의 나이와 내년의 나이는 다르다. 따라서 age는 대문자 상수에 적합하지 않다.

🐑 자료형

자바스크립트에는 여덟 가지 기본 자료형이 있으나, 변수는 어떤 순간에 문자열일 수 있고 다른 순간엔 숫자가 될 수도 있다.

// 에러 발생하지 않음
let message = "hello";
message = 123456;

이처럼 자료의 타입은 있지만 변수에 저장되는 값의 타입은 언제든지 바꿀 수 있는 언어를 동적 타입(dynamically typed) 언어라고 부른다.

숫자형 : 특수 숫자값

숫자형엔 일반적인 숫자 외에 Infinity, -Infinity, NaN같은 '특수 숫자 값(special numeric value)이 있다.

  1. Infinity : 무한대(∞)를 나타낸다.
alert( 1 / 0 ); // 무한대

alert( Infinity ); // 무한대
  1. NaN : 계산 중에 에러가 발생했다는 것을 나타내주는 값. 부정확하거나 정의되지 않은 수학 연산을 사용하면 계산 중에 에러가 발생하는데, 이때 NaN이 반환된다.
alert( "숫자가 아님" / 2 ); // NaN, 문자열을 숫자로 나누면 오류가 발생

alert( "숫자가 아님" / 2 + 5 ); // NaN

BigInt

자바스크립트에선 (2^53-1)보다 크거나 -(2^53-1) 보다 작은 정수는 '숫자형’을 사용해 나타낼 수 없다.

대신 BigInt형을 사용한다. 이는 길이에 상관없이 정수를 나타낼 수 있으며, 정수 리터럴 끝에 n을 붙이면 만들 수 있다.

const bigInt = 1234567890123456789012345678901234567890n;

사실 대부분의 상황에서 BigInt형을 쓸 일은 없지만, 암호 관련 작업같이 아주 큰 숫자가 필요한 상황이거나 아주 높은 정밀도로 작업을 해야 할 때는 필요하다.

문자형

let str = "Hello"; //큰따옴표
let str2 = 'Single quotes are ok too'; //작은 따옴표
let phrase = `can embed another ${str}`; //백틱

${…} 안에는 name 같은 변수나 1 + 2 같은 수학 관련 표현식을 넣을 수도 있다.

alert( "the result is ${1 + 2}" ); 

cf) JS에는 char형이 따로 없다.

null 값

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

let age = null;

자바스크립트의 null은 자바스크립트 이외 언어의 null과 성격이 다르다.

  • 다른 언어에서의 null : '존재하지 않는 객체에 대한 참조’, '널 포인터(null pointer)'
  • 자바스크립트에서의 null : ‘존재하지 않는(nothing)’ 값, ‘비어 있는(empty)’ 값, ‘알 수 없는(unknown)’ 값을 나타내는 데 사용

undefined 값

undefined 값도 null 값처럼 자신만의 자료형을 형성한다. undefined는 '값이 할당되지 않은 상태’를 나타낼 때 사용한다.

변수는 선언했지만, 값을 할당하지 않았다면 해당 변수에 undefined가 자동으로 할당된다.

let age;

alert(age); // 'undefined'가 출력됩니다.

null vs. undefined)

  • null : 변수가 ‘비어있거나’ ‘알 수 없는’ 상태라는 걸 나타낼 때 사용
  • undefined : 값이 할당되지 않은 변수의 초기값에 사용

객체와 심볼

다른 자료형은 문자열이든 숫자든 한 가지만 표현할 수 있기 때문에 원시(primitive) 자료형이라고 부르지만, 객체는 데이터 컬렉션이나 복잡한 개체(entity)를 표현할 수 있다.

심볼(symbol)형은 객체의 고유한 식별자(unique identifier)를 만들 때 사용된다.

typeof 연산자

두 가지 형태의 문법

  • 연산자: typeof x
  • 함수: typeof(x)

헷갈리는 예외

typeof null // "object"  

typeof alert // "function"  
  1. null은 별도의 고유한 자료형을 가지는 특수 값으로 객체가 아니지만, 하위 호환성을 유지하기 위해 이런 오류를 수정하지 않고 남겨둔 상황이다. 언어 자체의 오류! null이 객체가 아니다!
  2. typeof는 피연산자가 함수면 "function"을 반환한다. 그러므로 typeof alert는 "function"을 출력해준다. 그런데 '함수’형은 따로 없고, 함수는 객체형에 속한다. 이런 동작 방식이 형식적으론 잘못되긴 했지만, 아주 오래전에 만들어진 규칙이었기 때문에 하위 호환성 유지를 위해 남겨진 상태이다. 실무에선 이런 특징이 매우 유용하게 사용되기도 한다.

🐑 alert, prompt, confirm을 이용한 상호작용

브라우저 환경에서 사용되는 최소한의 사용자 인터페이스 기능인 alert, prompt, confirm에 대해 알아보자.

alert

사용자가 ‘확인(OK)’ 버튼을 누를 때까지 메시지를 보여주는 창이 계속 떠있게 된다.

alert("Hello");

alert은 undefined 값을 반환한다. (아무것도 반환하지 않는다.)

cf) 모달(modal)창
메시지가 있는 작은 창. 사용자는 모달 창이 떠있는 동안 모달 창 바깥에 있는 버튼을 누르는 등 페이지의 나머지 부분과 상호 작용이 불가능하다.

prompt

result = prompt(title, [default]);

메세지(title)와 사용자 input field, 확인(OK) 및 취소(Cancel) 버튼이 있는 모달 창이 뜬다. default 값을 넣어주면 기본 input 값이 field에 세팅되어있다.

사용자는 input field에 원하는 값을 입력하고 확인을 누르거나, 취솧 혹은 Esc를 누를 수 있다.

prompt 함수는 사용자가 input field에 기재한 문자열을 반환한다. 사용자가 입력을 취소한 경우는 null이 반환된다.

cf) 인수를 감싸는 대괄호 [...]의 의미
default를 감싸는 대괄호는 이 매개변수가 필수가 아닌 선택값이라는 것을 의미한다.

ex)

let age = prompt('나이를 입력해주세요.', 100);

alert(`당신의 나이는 ${age}살 입니다.`); 

confirm

result = confirm(question);

매개변수로 받은 question(질문)과 확인 및 취소 버튼이 있는 모달 창을 보여준다.

사용자가 확인 버튼을 누르면 true, 그 외의 경우는 false를 반환한다.

ex)

let isBoss = confirm("당신이 주인인가요?");

alert( isBoss ); // 확인 버튼을 눌렀다면 true가, 취소 버튼을 눌렀다면 false가 나온다.

🐑 형 변환

문자형으로 변환

  1. alert 함수
    alert메서드는 매개변수로 문자형을 받지만, 만약 다른 형의 값을 전달받으면 그 값이 문자형으로 자동 변환된다.

  2. String 함수
    String(value) 함수는 value를 문자형으로 명시해서 변환해준다.

let value = true;
alert(typeof value); // boolean

value = String(value); // 문자열 "true"로 변환됨
alert(typeof value); // string

false는 문자열 "false"로, null은 문자열 "null"로 변환된다.

숫자형으로 변환

  1. 숫자형이 아닌 값에 연산을 취하기
    숫자형이 아닌 값들에 연산을 취하면 해당 값들은 자동으로 숫자형으로 변환된다.
alert( "6" / "2" ); // 3, 문자열이 숫자형으로 자동변환된 후 연산이 수행된다
  1. Number 함수
    Number(value) 함수는 value를 숫자형으로 명시해서 변환해준다.
let str = "123";
alert(typeof str); // string

let num = Number(str); // 문자열 "123"이 숫자 123으로 변환됩니다.
alert(typeof num); // number

❗한편, 숫자 이외의 글자가 들어가 있는 문자열을 숫자형으로 변환하려고 하면, 그 결과는 NaN이 된다.

  • NaN 값의 자료형은 숫자형이고, 그 값이 NaN인 것이다!
let age = Number("임의의 문자열 123");
alert(age); // NaN

그 외 숫자형으로 변환 시 적용되는 규칙

alert( Number("   123   ") ); // 123
alert( Number("123z") );      // NaN 
alert( Number(true) );        // 1
alert( Number(false) );       // 0

Boolean형으로 변환

Boolean(value) 함수는 value를 Boolean형으로 바꿔준다.

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

alert( Boolean("hello") ); // true
alert( Boolean("") ); // false
  • false : 숫자 0, 빈 문자열, null, undefined, NaN
    (falsy한 값들)
    (그 외의 값은 true로 변환)

❗문자열 "0"은 true이다.

alert( Boolean("0") ); // true
alert( Boolean(" ") ); // 공백이 있는 문자열도 비어있지 않은 문자열이기 때문에 true! 숫자형이랑 헷갈리지 말 것

여기서 발표 + 퀴즈내자 !!!!!!!!!!!

🐑 기본 연산자와 수학

거듭제곱 연산자 **

alert( 2 ** 2 ); // 4  (2 * 2)
alert( 2 ** 3 ); // 8  (2 * 2 * 2)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)
alert( 4 ** (1/2) ); // 2 (1/2 거듭제곱은 제곱근)
alert( 8 ** (1/3) ); // 2 (1/3 거듭제곱은 세제곱근)

이항연산자 +로 연결하는 문자열과 숫자

  1. 문자열 + 문자열 : 문자열이 단순히 연결된다.
let s = "my" + "string";
alert(s); // mystring
  1. 문자열 + 숫자 / 숫자 + 문자열 : 숫자가 문자열로 변환된다.
alert( '1' + 2 ); // "12"
alert( 2 + '1' ); // "21"
  • 3개일 때도 마찬가지. 피연산자 중 어느 하나라도 문자열이면, 나머지가 다 문자열로 변환된다.

그 외 연산자로 연결하는 문자열과 숫자

+를 제외한 나머지 연산자로 연결되는 피연산자는 숫자로 변환된다.

alert( 6 - '2' ); // 4, '2'를 숫자로 바꾼 후 연산 진행
alert( '6' / '2' ); // 3, 두 피연산자가 숫자로 바뀐 후 연산이 진행

단항연산자 +의 숫자형으로의 변환

단항연산자가 숫자형이 아닌 다른 자료형 앞에 붙으면, 해당 피연산자는 숫자형으로 변환된다. 즉 Number 함수와 동일한 역할을 하게 된다.

alert( +true ); // 1
alert( +"" );   // 0
let apples = "2";
let oranges = "3";

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

여기서 알 수 있는 사실 : 단항연산자가 이항연산자보다 우선순위가 높다!

여기도 발표준비~

= 연산자의 복잡한 표현식

let a = 1;
let b = 2;

let c = 3 - (a = b + 1);

alert( a ); // 3
alert( c ); // 0

동작 원리 이해용으로 작성된 코드. 직접 코드를 작성할 땐 이런 방식을 사용하지 말 것. 가독성이 떨어진다!

증가, 감소 연산자의 전위형과 후위형

  • 전위형 : ++counter. 증가·감소 후의 새로운 값을 반환한다. 값을 증가시키고 난 후, 증가한 값을 바로 사용하려면 전위형 증가 연산자를 사용하면 된다.
  • 후위형 : counter++. 증가·감소 전의 기존 값을 반환한다. 값을 증가시키지만, 증가 전의 기존값을 사용하려면 후위형 증가 연산자를 사용하면 된다.

비교 예시)

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

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

반환 값을 사용하지 않는 경우라면, 전위형과 후위형엔 차이가 없다.

쉼표 연산자 ,

쉼표 연산자(comma operator) ,는 좀처럼 보기 힘들고, 특이한 연산자 중 하나이다. 코드를 짧게 쓰려는 의도로 가끔 사용된다.

쉼표 연산자 ,는 여러 표현식을 코드 한 줄에서 평가할 수 있게 해준다. 이때 표현식 각각이 모두 평가되지만, 마지막 표현식의 평가 결과만 반환되는 점에 유의해야 한다.

let a = (1 + 2, 3 + 4);

alert( a ); // 7 (3 + 4의 결과)

이렇게 마지막 표현식을 제외한 모든 것을 버리는 연산자는 어디서 사용되는 걸까? 여러 동작을 하나의 줄에서 처리하려는 복잡한 구조에서 이를 사용합니다.

// 한 줄에서 세 개의 연산이 수행됨
for (a = 1, b = 3, c = a * b; a < 10; a++) {
 ...
}

쉼표 연산자를 사용한 트릭은 여러 자바스크립트 프레임워크에서 볼 수 있다. 하지만 쉼표 연산자는 코드 가독성에 도움이 되지 않는다. 따라서 곰곰이 생각해 본 후, 진짜 필요한 경우에만 사용하기를 추천한다.

🐑 비교 연산자

문자열 비교

문자열은 왼쪽부터 사전순으로 문자열을 비교한다.
문자열의 길이가 다를 경우 긴 문자열이 더 크다고 결론낸다.

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

다른 형을 가진 값들에 대한 비교 연산

비교하려는 값의 자료형이 다르면 JS는 이 값들을 숫자형으로 바꾼다.

alert('2' > 1); //true. 2 > 1 → true
alert('01' == 1); //true. 1 == 1 → true
alert(true == 1); //true
alert(false == 0); //true
alert('' == false); //true 

일치 연산자

비교하려는 값의 자료형이 다르더라도 형 변환 없이 값을 비교한다. 즉 자료형의 동등 여부까지 검사하는 것이다. JS에서는 이것을 대부분 사용한다.

alert(false === 0); //false

null 값이 피연산자일 때

피연산자가 null일 때 비교 연산자는 형 변환을 하고, 동등 연산자는 형 변환을 하지 않는다.

alert(null > 0); // false. 0 > 0은 false
alert(null == 0); //false. null이 0으로 바뀌지 않음.
alert(null >= 0); //true. 0 >= 0은 true

undefined 값이 피연산자일 때

피연산자가 undefined일 때, 비교 연산자와 동등 연산자 모두 항상 false를 반환한다.

alert(undefined > 0); // false
alert(undefined < 0); //false
alert(undefined == 0); //false

null과 undefined가 피연산자일 때 동등 연산자

null과 undefined는 동등 연산자를 취하면 서로 같다. 다른 자료형과는 배타적이다. 둘만 동등하다.

🐑 if와 '?'를 사용한 조건 처리

조건부 연산자 '?'

조건부 연산자는 물음표?로 표시한다. 피연산자가 세 개이기 때문에 조건부 연산자를 '삼항(ternary) 연산자’라고 부르는 사람도 있다. JS에서 피연산자를 3개나 받는 연산자는 조건부 연산자가 유일하다.

let result = condition ? value1 : value2;

condition이 truthy라면 value1이, 그렇지 않으면 value 2가 result에 반환된다.

ex)

let accessAllowed = (age > 18) ? true : false;

age > 18 조건을 만족하면 accessAllowed에 true가, 만족하지 않으면 false가 반환된다.

다중 '?'

물음표 연산자?를 여러 개 연결하면 복수의 조건을 처리할 수 있다.

let age = prompt('나이를 입력해주세요.', 18);

let message = (age < 3) ? '아기야 안녕?' :
  (age < 18) ? '안녕!' :
  (age < 100) ? '환영합니다!' :
  '나이가 아주 많으시거나, 나이가 아닌 값을 입력 하셨군요!';

alert( message );

물음표 연산자?는 조건에 따라 반환 값을 달리하려는 목적으로 만들어졌다. 이런 목적에 부합하는 곳에 물음표를 사용해야 한다. 여러 분기를 만들어 처리할 때는 if를 사용하는 것이 좋다.

🐑 논리 연산자

첫 번째 truthy를 찾는 OR 연산자 '||'

JS에서만 제공하는 논리연산자 OR의 추가 기능에 대해 알아보자.

result = value1 || value2 || value3;

OR 연산자가 여러 개인 경우, OR 연산자 ||는 다음 순서에 따라 연산을 수행한다.

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

반환 값이 형 변환을 하지 않은 원래 값이라는 것이 중요하다.

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이므로, 마지막 값을 반환함)

cf) 구체적인 활용 방안은 문서 2.11 참고

첫 번째 falsy를 찾는 AND 연산자 '&&'

result = value1 && value2 && value3;

AND 연산자 &&는 아래와 같은 순서로 동작한다.

  1. 가장 왼쪽 피연산자부터 시작해 오른쪽으로 나아가며 피연산자를 확인한다.
  2. 각 피연산자는 불린형으로 변환됩니다. 변환 후 값이 false이면 평가를 멈추고 해당 피연산자의 변환 전 원래 값을 반환합니다.
  3. 피연산자 모두가 평가되는 경우(모든 피연산자가 true로 평가되는 경우)엔 마지막 피연산자가 반환된다.
// 첫 번째 피연산자가 truthy이면,
// AND는 두 번째 피연산자를 반환합니다.
alert( 1 && 0 ); // 0
alert( 1 && 5 ); // 5

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

NOT(!)을 2개 연달아 사용하면?

NOT을 두 개 연달아 사용(!!)하면 값을 Boolean형으로 변환할 수 있다.

alert(!!"non-empty string"); //true
alert(!!null); //false

🐑 nullish 병합 연산자 '??'

a ?? b

  • a가 null도 아니고 undefined도 아니면 a, 그 외의 경우는 b

아래의 코드와 동일한 기능을 한다.

x = (a !== null && a !== undefined) ? a : b;

nullish 병합 연산자 ??를 사용하면 값이 정해진 변수를 간편하게 찾아낼 수 있다.

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

// null이나 undefined가 아닌 첫 번째 피연산자
alert(firstName ?? lastName ?? nickName ?? "익명의 사용자"); // 바이올렛

'??'와 '||'의 차이

둘은 매우 비슷해보이지만 중요한 차이점이 있다.

  • ||는 첫 번째 truthy 값을 반환한다.
  • ??는 첫 번째 정의된(defined) 값을 반환한다.
let height = 0;

alert(height || 100); // 100
alert(height ?? 100); // 0

🐑 while과 for 반복문

'?'와 break, continue

'?' 오른쪽엔 breakcontinue가 올 수 없다.

if (i > 5) {
  alert(i);
} else {
  continue;
}

이 코드를 삼항 연산자로 나타낸다고 해보자.

(i > 5) ? alert(i) : continue; // 여기에 continue를 사용하면 안 됩니다.

이 코드는 에러를 발생시킨다.

break/continue와 레이블

평범한 break 지시자를 사용하면 안쪽에 있는 반복문만 빠져나올 수 있다. 그러나 중첩 반복문을 포함한 반복문 두 개 모두를 빠져나와야 한다면, 이럴 때 레이블을 사용할 수 있다.

레이블(label) 은 반복문 앞에 콜론과 함께 쓰이는 식별자이다.

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

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

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 outer는 outer라는 레이블이 붙은 반복문을 찾고, 해당 반복문을 빠져나오게 해준다. 따라서 제어 흐름이 (*)에서 alert('완료!')로 바로 바뀐다.

continue 지시자를 레이블과 함께 사용하는 것도 가능하다. 레이블이 붙은 반복문의 다음 이터레이션이 실행된다.

🐑 함수

함수의 선언과 사용

function showMessage() {
  alert( '안녕하세요!' );
}

showMessage();
showMessage();

매개변수와 인수

  • 매개변수 : 함수 선언문 괄호에 있는 변수. 선언 시 쓰이는 용어이다.
  • 인수 : 함수를 호출 할 때 매개벼수에 전달되는 값. 호출 시 쓰이는 용어이다.
function showMessage(from, text) { //  from, text : 매개변수
  alert(from + ': ' + text);
}

showMessage('Ann', 'Hello!'); // Ann, Hello : 인수

기본값

함수 호출 시 매개변수에 인수를 전달하지 않으면 그 값은 undefined가 된다.

function showMessage(from, text) {
  alert(from + ': ' + text);
}

showMessage("Ann");

이렇게 코드를 작성해도 에러가 발생하지 않고, 매개변수 text에 undefined가 할당될 뿐이다. 따라서 에러없이 "Ann: undefined"가 출력된다.

매개변수에 값을 전달하지 않아도 그 값이 undefined가 되지 않게 하려면 함수를 선언할 때 =를 사용해 '기본값(default value)'을 설정해주면 된다.

function showMessage(from, text = "no text given") {
  alert( from + ": " + text );
}

showMessage("Ann"); // Ann: no text given

기본값에 함수 표현식도 넣어줄 수 있다.

function showMessage(from, text = anotherFunction()) {
  // anotherFunction()은 text값이 없을 때만 호출됨
  // anotherFunction()의 반환 값이 text의 값이 됨
}

cf) 매개변수에 기본값을 설정하는 다양한 방법은 2.15 문서 참고

return

return문이 없거나, return 지시자만 있는 함수는 undefined를 반환한다.

함수 이름 짓기

함수는 어떤 동작을 수행하기 위한 코드를 모아놓은 것이므로, 함수 이름은 대개 동사이다.

그리고 함수는 동작 하나만 담당해야 한다. 함수는 간결하고 한 가지 기능만 수행할 수 있게 만들어야 한다.

🐑 함수 표현식

함수 선언 방식 외에 함수 표현식(Function Expression) 을 사용해서 함수를 만들 수 있다.

let sayHi = function() {
  alert( "Hello" );
};

함수를 생성하고 변수에 값을 할당하는 것처럼 함수가 변수에 할당되었다. 즉 함수를 만들고 그 함수를 변수 sayHi에 할당한 것이다.

함수는 값이기 때문에 alert를 이용하여 함수 코드를 출력할 수도 있다.

function sayHi() {
  alert( "Hello" );
}

alert( sayHi ); // 함수 코드 전체가 보임!

변수를 복사해 다른 변수에 할당하는 것처럼 함수를 복사해 다른 변수에 할당할 수도 있다.

function sayHi() {   // (1) 함수 생성
  alert( "Hello" );
}

let func = sayHi;    // (2) 함수 복사

func(); // Hello     // (3) 복사한 함수를 실행(정상적으로 실행됩니다)!
sayHi(); // Hello    //     본래 함수도 정상적으로 실행됩니다.

콜백 함수

function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}

function showOk() {
  alert( "동의하셨습니다." );
}

function showCancel() {
  alert( "취소 버튼을 누르셨습니다." );
}

// 사용법: 함수 showOk와 showCancel가 ask 함수의 인수로 전달됨
ask("동의하십니까?", showOk, showCancel);

함수 ask의 인수, showOk와 showCancel은 콜백 함수 또는 콜백이라고 불린다.

함수를 함수의 인수로 전달하고, 필요하다면 인수로 전달한 그 함수를 나중에 호출(called back)하는 것이 콜백 함수의 개념이다. 위 예시에선 사용자가 "yes"라고 대답한 경우 showOk가 콜백이 되고, "no"라고 대답한 경우 showCancel가 콜백이 된다.

아래와 같이 함수 표현식을 사용하면 코드 길이가 짧아진다.

function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}

ask(
  "동의하십니까?",
  function() { alert("동의하셨습니다."); },
  function() { alert("취소 버튼을 누르셨습니다."); }
);

함수 표현식 vs. 함수 선언문

1. 문법에서 차이가 있다

// 함수 선언문
function sum(a, b) {
  return a + b;
}
// 함수 표현식
let sum = function(a, b) {
  return a + b;
};

2. JS 엔진이 함수를 언제 생성하는지가 다르다

  • 함수 표현식은 실제 실행 흐름이 해당 함수에 도달했을 때 함수를 생성한다. 따라서 실행 흐름이 함수에 도달했을 때부터 해당 함수를 사용할 수 있다.

  • 위 예시에서, 스크립트가 실행되고 실행 흐름이 let sum = function…의 우측(함수 표현식)에 도달 했을때 함수가 생성된다. 이때 이후부터 해당 함수를 사용(할당, 호출 등)할 수 있다.

  • 하지만 함수 선언문은 함수 선언문이 정의되기 전에도 호출할 수 있다.

  • 따라서 전역 함수 선언문은 스크립트 어디에 있느냐에 상관없이 어디에서든 사용할 수 있다.

예시를 살펴보자.

//함수 선언문
sayHi("John"); // Hello, John

function sayHi(name) {
  alert( `Hello, ${name}` );
}
//함수 표현식
sayHi("John"); // error!

let sayHi = function(name) {  // (*) 마술은 일어나지 않습니다.
  alert( `Hello, ${name}` );
};

3. scope가 다르다

// 함수 선언문

let age = prompt("나이를 알려주세요.", 18);

// 조건에 따라 함수를 선언함
if (age < 18) {

  function welcome() {
    alert("안녕!");
  }

} else {

  function welcome() {
    alert("안녕하세요!");
  }

}

// 함수를 나중에 호출합니다.
welcome(); // Error: welcome is not defined

함수 선언문은 함수가 선언된 코드 블록 안에서만 유효하기 때문에 이런 에러가 발생한다.

let age = prompt("나이를 알려주세요.", 18);

let welcome;

if (age < 18) {

  welcome = function() {
    alert("안녕!");
  };

} else {

  welcome = function() {
    alert("안녕하세요!");
  };

}

welcome(); // 제대로 동작합니다.

함수 표현식을 사용하면 에러가 발생하지 않는다. if문 밖에 선언한 변수 welcome에 함수 표현식으로 만든 함수를 할당하면 되기 때문이다.

그럼 어떤 것을 사용하는게 좋을까?

함수 선언문을 이용해 함수를 선언하는 걸 먼저 고려하는 게 좋다. 함수 선언문으로 함수를 정의하면, 함수가 선언되기 전에 호출할 수 있어서 코드 구성을 좀 더 자유롭게 할 수 있고, 가독성도 좋아지기 때문이다. 그러나 어떤 이유로 함수 선언 방식이 적합하지 않거나, (위 예제와 같이) 조건에 따라 함수를 선언해야 한다면 함수 표현식을 사용해야 한다.

🐑 화살표 함수 기본

함수 표현식보다 단순하고 간결한 문법으로 함수를 만들 수 있는 방법이 있다. 바로 화살표 함수(arrow function)를 사용하는 것이다.

let func = (arg1, arg2, ...argN) => expression

이 함수는 아래 함수와 동일하다.

let func = function(arg1, arg2, ...argN) {
  return expression;
};

좀 더 구체적인 예시를 보자.

let sum = (a, b) => a + b;

/* 위 화살표 함수는 아래 함수의 축약 버전입니다.

let sum = function(a, b) {
  return a + b;
};
*/

alert( sum(1, 2) ); // 3
  • 인수가 하나밖에 없다면 인수를 감싸는 괄호를 생략할 수 있다.
let double = n => n * 2;
// let double = function(n) { return n * 2 }과 거의 동일합니다.

alert( double(3) ); // 6
  • 인수가 하나도 없을 땐 괄호를 비워놓으면 된다. 다만, 이 때 괄호는 생략할 수 없다.
let sayHi = () => alert("안녕하세요!");

sayHi();

화살표 함수는 함수 표현식과 같은 방법으로 사용할 수 있다. 아래 예시와 같이 함수를 동적으로 만들 수 있다.

let age = prompt("나이를 알려주세요.", 18);

let welcome = (age < 18) ?
  () => alert('안녕') :
  () => alert("안녕하세요!");

welcome();

본문이 여러 줄인 화살표 함수

let sum = (a, b) => {  // 중괄호는 본문 여러 줄로 구성되어 있음을 알려줍니다.
  let result = a + b;
  return result; // 중괄호를 사용했다면, return 지시자로 결괏값을 반환해주어야 합니다.
};

alert( sum(1, 2) ); // 3
profile
ꜱɪɴᴄᴇ 2021.09.01

0개의 댓글