JavaScript 소개

JavaScript(JavaScript)는 1995년 Netscape Navigator라는 웹 브라우저에 탑재되어 ECMAScript(ES) 라는 이름으로 처음 공개되었습니다. JavaScript는 다른 프로그래밍 언어에 비해 적은 양의 기능을 포함하고 있지만, 웹 브라우저, 웹 서버, 게임 엔진, 어도비 프로그램 등 다양한 구동 환경과 조합하여 유용한 기능들과 응용 프로그램을 만들 수 있습니다.

표준 명세 Standard Specification

표준 명세(Standard Specification)는 프로그래밍 언어의 문법 및 기능을 상세하게 선언한 일종의 프로그래밍 언어 사전입니다. JavaScript의 표준 명세인 ECMAScript(ES)는 1997년에 처음 제정되었고, 2015년에 제정된 ES2015(ECMAScript 6)이후 표준 명세가 빠르게 업데이트 되고있습니다. 초기의 자바스크는 웹 브라우저 간 표준문법 및 기능이 매우 달라 브라우저 간 호환성이 낮았기 때문에 잘 사용되지 않았습니다. 그러나 1999년 ES3, 2009년 ES5가 나오면서 차츰 호환성 문제가 해결되고, 새로운 문법과 기능들이 추가되면서 JavaScript의 활용도가 이전보다 훨씬 높아지면서 재조명되기 시작했습니다.

튜토리얼

JavaScript의 문법은 대부분 Java, C, C++로부터 빌려왔습니다. JavaScript에서는 대소문자를 구분하고, Unicode 문자셋을 이용하므로 영어 외의 다양한 언어를 작성할 수 있습니다. 스페이스(space-bar), 탭(tab), 줄바꿈 문자는 공백으로 간주됩니다.

JavaScript 기본 문법 보기 | MDN

명령문 Statement

프로그램(JavaScript의 스크립트)은 컴퓨터가 단계별로 수행할 명령들의 집합입니다. JavaScript에서 명령은 명령문(statement)으로 부르고, 세미콜론(;)으로 분리합니다. 명령문은 var, function과 같은 선언 키워드를 사용하여 변수, 함수를 생성하기도 하고 if, for, while 등의 제어문을 생성하여 프로그램의 흐름을 제어하기도 합니다.

명령문은 블록으로 그룹지을 수 있습니다. 블록이란 조건문, 반복문, 함수처럼 특정한 명령을 수행하는 여러개의 명령문들을 중괄호({})로 한데 묶은 코드를 뜻합니다.

              
                let z = x + y;
                /* 
                각각의 명령문이 실행되면 해당 명령에 의해 
                어떠한 일이 발생합니다.
                */

                function getZ(x, y) {
                  return x + y;
                }

                if(x == 1) {
                  // do sth
                }

                for(let i = 0; i < 3; i++) {
                  // do sth
                }
                /* 
                블록문은 여러 명령문을
                중괄호로 묶어놓은 명령문 그룹입니다.
                */
            

표현식 Expression

표현식은 값, 변수, 프로퍼티, 배열 요소, 함수 호출, 메소드 호출, 피연산자와 연산자의 조합 모두를 아우르는 개념으로, 이들은 하나의 값으로 평가(evaluation)됩니다. 하나의 표현식을 다른 표현식의 일부로 적용하여 더욱 복잡한 표현식을 만들 수 있습니다.

              
                x + y
                /* 
                표현식은 하나의 값으로 평가할 수 있습니다.
                */

                let z = x + y;
                /* 
                표현식은 명령문을 구성하는 요소가 될 수 있습니다.
                */
            

주석 Comment

주석은 공백으로 취급되고 스크립트 실행에서 제외됩니다.

              
                // 이 주석은 한 줄을 작동에서 제외시킵니다.

                /*
                이 주석은 
                여러 줄을 
                작동에서 제외시킵니다.
                */

                하나의 문장에서 /*이 부분만*/ 작동에서 제외시키고 싶을 때에도 쓸 수 있습니다.
            

JavaScript에서 주석 처리된 코드는 실제로 작동하지 않지만 사람들은 브라우저 검사기(inspector)나 코드 공유를 할 때 주석을 읽을 수 있습니다. 특히 접근이 쉬운 HTML 파일 안에 script 태그로 JavaScript를 작성한다면 브라우저 검사기를 통해 일반 유저도 쉽게 주석을 볼 수 있습니다.

그렇지만 너무 많거나 세세한 주석은 오히려 코드를 보기 힘들게 할 수 있습니다. 주석이 없어도 이해할 수 있도록 코드를 명료하게 작성하는 것이 좋습니다.

콘솔 Console

콘솔에 출력하기 console.log()

console.log();는 코드의 데이터와 액션을 모두 포함해 선언하는 데이터를 브라우저의 Console 창에 출력해서 보여줍니다.

                
                console.log("Hello World"); // "Hello World"
                /* 콘솔에 데이터 출력하기 */

                const name = "Ujin";
                const age = 26;
                const city = "Seoul";
                console.log(name, "/", age, "/", city);

                // "Ujin/26/Seoul"
                /* 콘솔에 여러 데이터를 한 번에 출력하기 */
              

변수

어떠한 값을 계속 사용(유지, 캐싱)할 필요가 있다면 그 값을 변수(variable)에 담아 사용할 수 있습니다. 변수는 특정한 값을 저장(할당)하고 저장된 값을 참조하기 위해 사용합니다. 즉, 변수는 특정한 값(value)을 메모리에 담아두고 그 값의 위치(메모리 상의 주소, memory address)에 쉽게 접근하기 위해 사람이 이해할 수 있는 언어로 이름(식별자, identifier)을 붙인 뒤 해당 주소에서 값을 불러오는 역할을 맡습니다. 변수와 변수값을 선언할 때는 선언 키워드(var, let, const), 식별자(identifier), 할당연산자 = (assignment operator), 값(value)을 할당합니다.

            
                let myName = "Ujin";
                console.log(myName); // "Ujin"

                /*
                var, let, const : 선언 키워드
                myName : 식별자
                = : 할당연산자
                "Ujin" : 값
                */
          

식별자(identifier)을 만들 때 알아두어야 할 몇 가지 규칙이 있습니다.

  1. 식별자의 첫글자는 문자, 밑줄(_), 달러 기호($)로 시작해야합니다. 첫글자를 숫자로는 시작할 수 없습니다.
  2. 식별자 안의 대문자와 소문자를 분명히 구분해야 합니다. 같은 식별자에 소문자와 대문자를 다르게 적으면 다른 식별자로 인식됩니다.
  3. 식별자의 첫 단어 이후 다음 단어들은 첫 글자를 대문자로 적어줍니다. (camel casing)
  4. 키워드는 식별자으로 쓸 수 없습니다. 키워드는 표준명세에서 약속된 예약어(resered words)로, 수행할 동작을 미리 규정한 것입니다. 메소드 또한 가져올 수 없습니다.
  5. 함수 식별자(function identifier), 객체 식별자(object identifier)의 이름은 가져올 수 없습니다.

변수 선언 Variable Declaring

기존에는 변수를 선언하기 위해 var 키워드를 사용했지만, var로 변수를 선언한 경우 변수값이 중간에 초기화 되는 등 엄격한 실행모드(strict mode)에서 의도치않은 결과를 출력할 수 있습니다. 따라서 ECMAScript 6 이후로 새로 등장한 let, const 키워드를 쓰는 것이 권장됩니다.

var

var로 변수를 선언하면 첫 변수값을 할당한 이후에도 (또는 변수값을 아직 할당하지 않은 경우에도) 기존의 값을 초기화하고 새 값을 할당할 수 있습니다.

                
                var designer = "Ujin";
                console.log(designer); // "Ujin"
                var designer = "Jiyoung";
                console.log(designer); // "Jiyoung"

                var designer;
                console.log(designer); // undefined
                var designer = "Jiyoung"
                console.log(designer); // "Jiyoung"
              

let

let도 변수를 선언하면 첫 변수값을 할당한 이후에도 (또는 변수값을 아직 할당하지 않은 경우에도) 기존의 값을 초기화하고 새 값을 할당할 수 있습니다. 그러나 let은 변수값을 다시 할당할 때 var과 달리 변수 앞에 let 키워드를 쓰지 않습니다.

                
                let designer = "Ujin";
                console.log(designer); // "Ujin"
                designer = "Jiyoung";
                console.log(designer); // "Jiyoung"

                let designer;
                console.log(designer); // undefined
                designer = "Jiyoung"
                console.log(designer); // "Jiyoung"

                let designer = "Ujin";
                console.log(designer);
                let designer = "Jiyoung";
                console.log(designer);
                // SyntaxError: Identifier "designer" has already been declared
              

const

const로 변수를 선언하면 첫 변수값을 할당한 이후에 새 값을 할당할 수 없고, 변수의 초기값을 반드시 적어야 합니다. 변수값을 중간에 초기화하는 경우가 아니라면 일반적으로 선언 키워드를 const로 사용합니다.

                
                const designer = "Ujin";
                console.log(designer); // "Ujin"
                designer = "Jiyoung"; // TypeError: Assingment to constant variable.

                const designer; // SyntaxError: Missing initializer in const declaration
              

변수 할당 Variable Assignment

변수 선언과 동시에 값을 할당할 수 있지만, 변수 선언을 먼저 한 이후에 값을 할당할 수도 있습니다. 할당이 완료되면 이후 변수는 할당받은 값을 대신 내보냅니다.

              
                let myName;
                myName = "Ujin";

                /*
                변수 선언과 동시에 값을 할당하지 않고, 
                선언 이후에 특정 값을 변수에 할당했습니다.
                */
            

var, let 선언 키워드를 앞에 둔 변수에 초기값을 할당하지 않았다면 그 변수는 undefined 값을 할당받습니다. 단, 스크립트에 선언된 적 없는 변수에 접근하면 ReferenceError가 발생합니다. 호이스팅 규칙에 의해 var과 let, const는 변수가 선언된 위치에 따라 다른 결과를 출력합니다.

              
                var a;
                console.log(`a는 ${a}`); 
                // "a는 undefined"

                console.log(`b는 ${b}`); 
                var b;
                // "b는 undefined"

                console.log(`c는 ${c}`); 
                // ReferenceError: c is not defiend


                let x;
                console.log(`x는 ${x}`); 
                // "x는 undefined"

                console.log(`y는 ${y}`); 
                let y;
                // ReferenceError: Cannot access "y" before initialization
            

호이스팅 Hoisting

호이스팅 개념은 ECMAScript 6 이후 등장했습니다. 호이스팅은 '끌어올리기'로 직역할 수 있습니다. 즉, 함수가 작동할 때 함수 안에서 선언된 변수는 선언된 위치에 상관없이 일단 함수 내의 맨 위로 끌어올려지는 것을 뜻합니다. 끌어올려진 변수는 임의의 값으로 undefined를 할당받습니다. 이후 함수가 실행되면서 undefined값을 할당받은 변수가 사용됩니다. 단, 끌어올려진 변수는 사용되거나 다시 선언되거나, 값을 초기화해도 여전히 undefined를 반환합니다.

호이스팅 규칙에 따라 함수 내의 모든 변수정의는 가능한 함수 위 쪽에 작성하는 것이 좋습니다.

              
                var a = "A";

                function getA() {
                  console.log("a는 " + a); // "a는 undefined"
                  var a = "a";
                  console.log("a는 " + a); // "a는 a"
                }
                getA();
                
                /*
                위 함수는 아래와 같은 과정으로 실행됩니다.
                */

                var a = "A";

                function getA() {
                  var a; // undefined
                  console.log("a는 " + a); // "a는 undefined"
                  var a = "a";
                  console.log("a는 " + a); // "a는 a"
                }
                getA();
            

하지만 let, const는 호이스팅을 지원하지 않습니다. let, const로 변수가 선언되기 전에 블록 안에서 변수를 참조하면 ReferenceError가 나타납니다. 이 때 변수는 블록이 시작할 때부터 선언될 때까지 작동하지 않습니다.

              
                let a = "A";

                function getA() {
                  console.log("a는 " + a); 
                  // ReferenceError: Cannot access "A" before initialization at getA
                  let a = "a";
                  console.log("a는 " + a);
                }
                getA();
            

데이터 타입

JavaScript에서 다루는 값은 모두 데이터 타입(Data Types)을 가지고 있습니다. 데이터타입은 프로그래밍 언어에서 사용할 수 있는 값의 종류를 뜻합니다. JavaScript의 데이터 타입은 기본적으로 7개로 분류합니다. 이 중에서 함수, 배열, 날짜, 정규식은 특별한 종류의 객체 타입으로 구분됩니다.

원시 타입 Primitive Data Type

  1. 숫자 number
  2. 문자열 string
  3. 불리언 boolean
  4. null
  5. undefined
  6. 심볼 symbol (ECMAScript 6 이후 등장)

객체 타입 Object Data Type

  1. 객체 object
  2. 함수 function
  3. 배열 array
  4. 날짜 date
  5. 정규식 regExp

JavaScript에서는 변수를 선언할 때 데이터 타입을 따로 지정해주지 않아도 할당된 값의 타입에 의해 자동으로 변수의 타입이 결정됩니다. 이를 동적 타이핑이라 합니다.

리터럴 Literal

리터럴은 코드 안에서 직접 만들어낸 상수 값 자체를 뜻하며, 값을 구성하는 최소 단위입니다. 원시 타입 리터럴은 연산자로 연산하여 하나의 값이 될 수 있습니다.

              
                1000101 
                // 숫자 리터럴

                "Hello" 
                // 문자 리터럴

                true 
                // 불리언 리터럴

                null 
                // null 리터럴

                undefined 
                // undefined 리터럴

                { name: "Ujin" city: "Seoul" } 
                // 객체 리터럴

                [1, 2, 3, 4, 5]
                // 배열 리터럴

                /ab+c/
                // 정규표현식 리터럴

                function() {}
                //함수 리터럴
                
            

typeof

연산자 typeof는 피연산자(operand)의 데이터 타입을 추출하여 문자열 형태로 반환합니다.

              
                typeof 1; // number
                typeof "hello"; // string
                typeof true; // boolean
                typeof null; // object
                typeof undefined; // undefined

                typeof typeof 1; // string
            

숫자 Number

숫자는 정수, 실수, 0, -0을 포함한 수입니다. 문자열의 형태를 지녔지만 숫자의 개념을 다루는 NaN(Not a Number), Infinity, -Infinity도 숫자 타입입니다.

              
                1, 23.42, -7, 0, -0, NaN, Infinity, -Infinity
            

Number(), parseFloat(), parseInt()

Number(), parseFloat(), parseInt()는 데이터의 첫글자부터 데이터를 숫자 타입으로 변환합니다.
데이터의 첫글자가 숫자로 나타낼 수 없는 문자열(알파벳, 공백 등)이라면 NaN을 출력합니다.

                
                const a = "10"; // 정수
                const b = "2.019"; // 소수점이 있는 실수
                const c = "3a"; // 정수 + 알파벳
                const d = "3.4 5.6 7.8" // 소수점이 있는 실수 + 공백
                const e = "40 sec" // 정수 + 공백 + 알파벳
                const f = "Hi 2019" // 알파벳 + 공백 + 정수

                Number(a); // 10
                Number(b); // 2.019
                Number(c); // NaN
                Number(d); // NaN
                Number(e); // NaN
                Number(f); // NaN

                /* 
                Number()는 
                숫자로 나타낼 수 없는 문자열이 포함되어있으면 
                NaN을 출력합니다.
                */

                parseFloat(a); // 10
                parseFloat(b); // 2.019
                parseFloat(c); // 3
                parseFloat(d); // 3.4
                parseFloat(e); // 40
                parseFloat(f); // NaN

                /* 
                parseFlaot()는 
                숫자로 나타낼 수 없는 문자열이 포함되어있으면 
                그러한 문자열을 제외한 뒤 앞글자부터 출력합니다. 
                그러한 문자열이 첫글자일 경우 NaN을 출력합니다.
                */

                parseInt(a); // 10
                parseInt(b); // 2
                parseInt(c); // 3
                parseInt(d); // 3
                parseInt(e); // 40
                parseInt(f); // NaN

                /* 
                parseInt()는 
                숫자로 나타낼 수 없는 문자열이 포함되어있으면 
                그러한 문자열을 제외한 뒤 앞글자부터 소수점을 지우고 정수(integer)로만 출력합니다. 
                그러한 문자열이 첫글자일 경우 NaN을 출력합니다.
                */
              

Math()

Math()는 수학적인 상수와 함수를 간단한 식으로 나타내거나 변환할 수 있는 내장객체입니다.

                
                Math.E(); // 자연상수 e (2.71...)
                Math.PI(); // 원주율 (3.14...)

                Math.sin(); // 사인
                Math.cos(); // 코사인
                Math.tan(); // 탄젠트
                Math.log(); // 밑을 자연상수로 두는 로그함수
                Math.exp(); // 밑을 자연상수로 두는 지수함수
                Math.sqrt(); // 제곱근
                
                Math.abs(); // 절댓값
                Math.ceil(); // 소수점 이하 올림
                Math.floor(); // 소수점 이하 내림
                Math.round(); // 소수점 이하 반올림
                Math.trunc(); // 소수점 이하 잘라내기

                Math.max(); // 최댓값
                Math.min(); // 최소값

                Math.random(); // 랜덤값
              

문자열 String

문자열은 "..."(single quotes) 또는 "..."(double quotes) 안에 둘러싸인 모든 글자, 숫자, 공백, 심볼 등을 포함합니다. Console.log로 문자열을 출력하면 "..."로 출력됩니다.

              
                "This is String!", "문자열은", "이러한 형태입니다."
            

템플릿 문자열 Template Literals

ECMAScript 6 이후 등장한 템플릿 문자열로 문자열을 간단하게 반환하고 채워넣을 수 있습니다. 템플릿 문자열은 "",""이 아닌 ``(backticks, 키보드의 1!키 왼쪽에 있는 키)로 둘러싸입니다. 반환될 문자열 변수를 ${} 안에 넣으면 해당되는 변수값이 출력됩니다.

                
                const name = "Jiyeong";
                const age = 37;
                const city = "Busan";
                console.log(`Hi, My name is ${name}. 
                I"m ${age} now. 
                And I"m from ${city}.`);

                /*
                "Hi, My name is Jiyeong. 
                I"m now 37. 
                And I"m from Busan."
                */
              

.length

문자열의 길이(개수)값, 또는 배열의 요소 개수를 반환합니다.

                
                const weather = "2019.04.14 Today: 날씨 맑음!!"
                weather.length; // 25
              

charAt(n), indexOf(), substring(n1, n2)

각각 n번째 문자열, 특정 문자열의 순서(n번째), n1번째부터 n2번째까지의 문자열을 반환합니다.

                
                const weather = "2019.04.14 Today: 날씨 맑음!!"

                weather.charAt(6); // 4
                weather[6]; // 4
                weather.indexOf("날"); // 18
                weather.substring(0, 4); // 2019
              

toUpperCase(), startsWith()

각각 문자열을 대문자로 반환하고, 전체 문자열이 해당 문자열로 시작하는지 알려주는 진리값을 반환합니다.

                
                const weather = "2019.04.14 Today: 날씨 맑음!!"

                weather.toUpperCase(); // "2019.04.14 TODAY: 날씨 맑음!!"
                weather.startsWith("2019.04"); // true
              

String()

변수의 타입을 문자열로 변환합니다. null, undefined값도 변환할 수 있습니다.

                
                const x = 1;
                const y = 2;
                let z;

                x + y; // 3
                String(x) + String(y); // "12"
                String(z); // "undefined"
              

불리언 Boolean

불리언은 데이터에 대한 조건이 참 또는 거짓(true or false, yes or no)을 나타냅니다. 이 조건을 불리언값 또는 진리값이라 부릅니다. 주로 서로 다른 데이터값의 일치 여부를 확인하기 위해 다양한 연산자와 함께 사용합니다.

              
                1 === 1 // true
                1 === 2 // false
            

불리언이 아닌 데이터 타입의 진리값 Truthy & Falsy

데이터 타입이 정확히 불리언은 아니지만 false로 판정되는 값을 falsy값이라 부르고, 나머지 값을 truthy값이라 부릅니다. JavaScript는 아래의 데이터 타입을 falsy 값으로 판단합니다.

  1. false (boolean)
  2. null (null-object)
  3. undefined (undefined)
  4. 0 (number)
  5. NaN (number)
  6. "" (string)
  7. document.all (object)

산술연산자로 진리값 확인하기

진리값을 산술연산자로 계산할 때 true(truthy)는 숫자 1, false(falsy)는 숫자 0으로 변환되어 계산됩니다.

                
                const x = true;
                const y = false;

                0 + x; // 1 
                0 + y; // 0
                "" + x; // true
                "" + y; // false
                false + x; // 1
                false + y; // 0
                null + x; // 1
                null + y; // 0
                undefined + x; // NaN
                undefined + y; // NaN
              

빈값 Null & Undefined

null(no value)과 undefined 모두 값이 없음을 나타내지만, 뜻이 약간 다릅니다. null은 "객체 값이 존재하지 않음"을 나타내고, undefined는 "아직 값을 할당한 적이 없음"을 나타냅니다. 그래서 typeof로 null을 반환하면 object로, undefined를 반환하면 undefined로 출력됩니다.

              
                typeof null; // object
                typeof undefined; // undefined
            

빈값 체크 Null & Undefiend Check

if문typeof를 사용해서 변수값이 비어있는지(초기화되어있는지, 또는 할당된 적이 없는지) 확인할 수 있습니다. if문의 매개변수(parameter) 값은 진리값을 반환하므로 매개변수로 쓰인 변수값이 truthy or falsy값을 반환하면 빈값 여부를 알 수 있습니다.

                
                if (value) {
                  // value가 truthy라면 빈값이 아닙니다. (falsy 값입니다.)
                }
              

어떠한 변수가 선언된 적이 있는지 잘 모를 때, 즉 변수가 이전에 선언된 적이 있는지 확인하고 싶을 때는 매개변수 안에 typeof 연산자를 추가한 뒤 엄격한 비교를 통해 undefined인지 확인합니다.특히 undefined 체크에서 typeof 연산자를 사용하면 속도가 향상될 수 있다고 합니다.

                
                if (typeof value !== "undefined") {
                  // value의 타입이 undefined가 아니라면 value값은 선언된 적이 있습니다.
                }
              

그런데 우리가 빈값이라고 생각하는 데이터타입이 null 또는 undefined가 아닌 경우가 많습니다. 아래에 헷갈릴 수 있는 데이터타입들을 모아봤습니다.

                
                typeof(); //undefined
                typeof(null); //null
                typeof(NaN); //number
                typeof(0); //number
                typeof({}); //object
                typeof([]); //array
                typeof(''); //string
                typeof(function () {}); //function
                typeof(/a/) //regexp
                typeof(new Date()) //date
                typeof(new WeakMap()) //weakmap
                typeof(new Map()) //map
              

연산자

JavaScript에서 주로 쓰이는 연산자는 아래와 같습니다.

  1. 산술연산자
  2. 할당연산자
  3. 비교연산자
  4. 논리연산자

산술연산자 Arithmetic Operator

산술연산자는 마치 계산기처럼 주로 간단한 숫자 계산을 할 때 사용하는 연산자입니다. 산술연산자는 숫자, 문자열, 또는 변수(variables)를 피연산자(operator)로 넣을 수 있고, 그 결과값을 숫자 또는 문자열로 반환합니다. 피연산자의 타입과 연산자의 종류에 따라 결과값의 타입이 달라집니다. 따라서 연산 전에 피연산자를 미리 숫자로 변환해두어야 일관적인 결과값을 얻을 수 있습니다.

              
                2 + 3 - 1; // 4
                5 * 3 / 2; // 15 / 2 
                // 7.5
                11 % 2 ** 3; // 11 % 8
                // 3
            

덧셈 + Addition

덧셈은 숫자 뿐만아니라 다양한 데이터 타입끼리의 연산이 가능합니다. 문자열과 숫자를 연결할 때(Concatenation) 또는 문자열끼리 연결할 때, 그리고 진리값끼리 연산할 때에도 덧셈을 사용할 수 있습니다.

                
                0 + 1; // 1 
                0 + true; // 1 
                false + false; // 0 

                "Hi" + 5; // "Hi5"
                "Hi" + false; // "Hifalse"
                "Hi" + "Five"; // "HiFive"
              

뺄셈 - Subtraction

뺄셈은 데이터끼리의 차이를 출력합니다. 덧셈을 제외한 나머지 산술연산에서는 문자열을 포함한 결과값이 NaN으로 출력됩니다.

                
                0 - 1; // -1 
                0 - true; // -1 
                true - false; // 1 

                "Hi5" - 5; // NaN
                "Hifalse" - false; // NaN
                "HiFive" - "Five"; // NaN
              

곱셈 * Multiplication

                
                1 * 2; // 2
                1 * -2; // -2
                1 * 0; // 0
                Infinity * 0; // NaN
                Infinity * Infinity; // Infinity
              

나눗셈 / Division

                
                1 / 2; // 0.5
                1.0 / 2.0; // 0.5
                0 / 2; // 0
                2 / 0; // Infinity
                2.0 / 0.0; // Infinity
                2 / -0; // -Infinity
              

나머지 % Modulo(Remainder)

                
                12 % 5; // 2
                1 % 2; // 1 
                -1 % 2; // -1
                1 % -2; // 1
                2 % 2; // 0
                -2 % 2; // -0
                0 % 2; // 0
                2 % 0; // NaN
                NaN % 2; // NaN
                5.5 % 2; // 1.5
              

거듭제곱 ** Exponentiation

JavaScript에서 거듭제곱을 사용할 때는 괄호 등으로 양음수 여부나 계산 순서를 명확히 해주어야 합니다.

                
                2 ** 3; // 8
                -2 ** 3; // Invalid
                -(2 ** 3); // -8
                (-2) ** 3; // 8

                3 ** 2.5 // 15.588457268119896
                10 ** -1 // 0.1
                NaN ** 2 // NaN

                2 ** 2 ** 3; // 128
                2 ** (2 ** 3) // 128
                (2 ** 2) ** 3 // 64
              

증가 ++, 증감 -- Increment, Decrement

증가, 증감 연산자는 피연산자1의 뒤에 붙으면 피연산자2의 값을 계산 전 값으로 반환하고, 피연산자1의 앞에 붙으면 피연산자2의 값을 계산 후 값으로 반환합니다.

                
                let x = 5;
                y = x++; // x = 6, y = 5
                y = x--; // x = 4, y = 5
                // 연산자가 피연산자의 뒤에 붙었을 때

                let a = 5;
                b = ++a; // a = 6, b = 6
                b = --a; // a = 4, b = 4
                // 연산자가 피연산자의 뒤에 붙었을 때
              

할당연산자 Assignment Operator

할당연산자는 연산자 기준으로 오른쪽 피연산자의 값을 왼쪽의 피연산자에 할당합니다.

              
                let a = 10;
                let b = 20;
                let c = 100;
                let d = 2000;
                let e = 45;

                a += 5; // 15
                b -= 30; // -10
                c *= 8; // 800
                d /= 50; // 40
                e %= 8; // 5
            

할당 = Assignment

한 개의 값을 여러개의 변수에 할당하기 위해 할당 연산자를 연속으로 사용할 수 있습니다.

                
                let x = 5;
                let y = 10;
                let z = 25;

                x = y; // x = 10;
              

덧셈 할당 += Addition Assignment

두개의 피연산자 데이터 타입에 따라 숫자의 덧셈 또는 문자열의 연결이 가능합니다.

                
                let str = "string";
                let num = 5;
                let boo = true;

                num += 2; // 7
                boo += 2; // 3
                boo += false; // 1
              
                num += "foo"; // "5foo"
                str += false; // "stringfalse"
                str += "foo"; // "stringfoo"
              

뺄셈 할당 -= Subtraction Assignment

                
                let str = "string";
                let num = 5;
                let boo = true;

                num -= 2; // 3
                boo -= 2; // -1
                str -= 2; // NaN
              

곱셈 할당 *= Multiplication Assignment

                
                let str = "string";
                let num = 5;
                let boo = true;

                num *= 2; // 10
                boo *= 2; // 2
                str *= 2; // NaN
              

나눗셈 할당 /= Division Assignment

                
                let str = "string";
                let num = 5;
                let boo = true;

                num /= 2; // 2.5
                boo /= 2; // 0.5
                str /= 2; // NaN
                num /= 0; // Infinity
              

나머지 할당 %= Modulo(Remainder) Assignment

                
                let str = "string";
                let num = 5;
                let boo = true;

                num %= 2; // 1
                boo %= 2; // 1
                str %= 2; // NaN
                num %= 0; // Infinity
              

비교연산자 Comparison Operator

서로 다른 두 값을 비교할 때 사용하는 비교연산자는 두 종류, 엄격한 비교(strict comparison)와 형변환 비교(abstract comparison)로 나뉩니다. 엄격한 비교는 피연산자끼리 동일한 값과 데이터타입을 가져야만 true를 출력하고, 형변환은 비교 전 피연산자들을 같은 자료형으로 바꾼 뒤 엄격한 비교를 진행합니다.
엄격한 비교연산자는 형변환 비교연산자의 뒤에 = 하나를 더 붙입니다.

동등 ==, 일치 === Equality, Identity

피연산자 둘 다 객체라면, 객체 내부 내용을 비교한 뒤 엄격한 비교를 진행합니다. 단, null과 undefined는 엄격한 비교로서는 다르지만, 형변환 비교로는 같습니다.

                
                1 == 1
                "1" == 1
                0 == false // true

                0 == null
                0 == undefined // false

                1 === 1 // true
                1 === "1" // false

                null == undefined // true
                null === undefined // false
              

부등 !=, 불일치 !== Inequality, Non-Identity

                
                1 != 2
                0 != true // true

                "1" != 1
                0 != false // false

                1 !== 2 // true
                "1" !== 1 // true
              

관계 >, <, >=, <= Relational Operator

                
                3 > 3 
                3 < 3 // false

                3 >= 3
                3 <= 3 // true
              

논리연산자 Logical Operator

논리연산자는 진리값을 가진(또는 truthy & falsy 값을 반환하는) 두 피연산자의 집합을 관계지은 뒤, 주로 진리값 또는 다른 데이터형으로 출력합니다. 논리연산자가 포함된 논리표현식은 왼쪽부터 오른쪽 방향으로 진행됩니다.

&& AND

피연산자1이 false로 변환되면 피연산자1을 반환하고 표현식 진행을 종료합니다. 그렇지 않으면 피연산자2를 반환합니다.

                
                true && true // true (피연산자2)
                true && false // false 
                false && true // false 
                /*
                피연산자 둘 다 불리언 타입인 경우
                */

                false && (3 === 4) // false (피연산자1)
                /*
                피연산자 중 하나가 불리언 타입일 경우
                */

                "A" && "B" // "B"
                false && "B" // false (피연산자1)
                "A" && false // false (피연산자2)
                /*
                피연산자 중 하나가 불리언 타입이고, 
                다른 하나는 문자열인 경우
                */

                "" && false // "" (피연산자1)
                false && "" // false (피연산자1)
                /*
                피연산자 중 하나가 불리언 타입이고, 
                다른 하나는 빈 문자열인 경우
                */
              

|| OR

피연산자1이 true로 변환되면 피연산자1을 반환하고 표현식 진행을 종료합니다. 그렇지 않으면 피연산자2를 반환합니다.

                
                true || true // true (피연산자1)
                true || false // true 
                false || true // true
                /*
                피연산자 둘 다 불리언 타입인 경우
                */

                false || (3 === 4) // false (피연산자2)
                /*
                피연산자 중 하나가 불리언 타입일 경우
                */

                "A" || "B" // "A" (피연산자1)
                false || "B" // "B"
                "A" || false // "A"
                /*
                피연산자 중 하나가 불리언 타입이고, 
                다른 하나는 문자열인 경우
                */

                "" || false // false (피연산자2)
                false || "" // "" (피연산자2)
                /*
                피연산자 중 하나가 불리언 타입이고, 
                다른 하나는 빈 문자열인 경우
                */
                
              

!, !! NOT, Double NOT

Not은 단일 피연산자가 true로 변환되면 false를 반환합니다. 그렇지 않으면 true를 반환합니다. Double Not은 반대로 피연산자의 진리값을 그대로 반환합니다.

                
                !true // false
                !false // true
                !"" // true
                !"A" // false

                !!true // true
                !!false // false
                !!"" // false
                !!"A" // true
              

논리표현식 단락 평가 Logical Expression Short-Circuit

논리연산자를 사용하는 논리표현식은 왼쪽부터 오른쪽 방향으로 진행되므로, 피연산자1에서 표현식 진행이 마무리 되면 다음 피연산자2를 계산할 필요가 없습니다. 따라서 되도록 계산을 빨리하기 위해 피연산자1만 계산하도록 하는 단락 평가를 수행합니다.

                
                function getA() {
                  console.log("A는 1");
                  return false;
                }
                function getB() {
                  console.log("B는 2");
                  return true;
                }

                console.log( getA() && getB() );
                // "A는 1"
                // false

                console.log( getB() || getA() );
                // "B는 2"
                // true
                
                /*
                논리표현식에서 피연산자1에서 계산이 완료되어 
                피연산자2를 계산하지 않음
                */
              

조건문

조건문(conditional statement)은 주어진 조건식(conditional expression)의 평가 결과에 따라 블록문의 실행 여부를 결정합니다. 조건식은 불리언값으로 평가될 수 있는 표현식입니다. JavaScript에서 조건문은 크게 if문과 switch문으로 표현합니다.

if...else

if문은 미리 지정된 변수를 자식개체로 삼아 ()에 넣고, 자식개체의 진리값(true & false, truthy & falsy)에 따라 어떤 행동을 출력할 지 중괄호({})에 적어냅니다. if문의 a값이 true이면 첫 번째 행동을, false이면 else에 적힌 두 번째 행동을 출력합니다. 중간에 else if()를 추가해 조건을 여러 개 걸어놓을 수 있습니다.

              
                let a = true;

                if (a) {
                  console.log("a is true!");
                  return a * 2;
                } else if(a == 0) {
                  console.log("a is zero!");
                  return a;
                } else {
                  console.log("a is false!");
                  return typeof(a);
                }

                // "a is true!"
            

블록문이 간단하게 표현되는 한 줄이라면 중괄호를 생략할 수 있습니다.

              
                let a = true;

                if (a)          console.log("a is true!");
                else if(a == 0) console.log("a is zero!");
                else            console.log("a is false!");
                
                // "a is true!"
            

논리연산자를 이용한 조건문 Truthy & Falsy Assignment

논리연산자 ||(or)를 사용해 조건문을 만들 수 있습니다. ||는 왼쪽에서부터 오른쪽으로 진행되면서 true값을 반환합니다. 조건 결과가 true이면 피연산자1을, || false이면 피연산자2를 반환합니다.

                
                let name = "Ujin";
                let userId = name || "익명의 사용자";
                
                console.log(`${userId}님이 로그인 하셨습니다.`);
                
                // "Ujin님이 로그인하셨습니다."
              

삼항 연산자 Ternary Operator

대부분의 if문은 삼항 연산자로 바꿔쓸 수 있습니다. 삼항 연산자는 세 개의 피연산 함수를 쓸 수 있는 연산자입니다. 조건 결과가 true이면 ? 피연산자1을, : false이면 피연산자2를 반환합니다.

                
                let name = "Ujin";
                let userId = name || "";

                const welcome = name ? console.log(`${userId}님, 안녕하세요!`) : console.log(`ID를 입력해주세요.`);
              

삼항 연산자로 세 가지 경우의 수를 표현할 때는 두 가지 경우의 수를 표현한 표현식을 피연산자1로 넣고 남은 경우의 수를 피연산자2로 넣습니다.

                
                let name = "Ujin";
                let userId = name || "";

                const welcome = name ? (name !== "운영자" ? console.log(`${userId}님, 안녕하세요!`) : console.log(`관리 모드로 로그인했습니다.`) : console.log(`ID를 입력해주세요.`);
              

switch

조건문의 조건이 단일의 숫자나 문자열처럼 비교적 단순한 구조라면 switch문으로 더욱 간단하게 표현할 수 있습니다. switch문의 표현식은 진리값보다 다양한 case에 따라 실행할 블록문을 결정합니다. case에는 표현식의 결과값을 적고, 결과값은 또 다른 표현식이 될 수 있습니다. switch문의 표현식과 일치하는 case 표현식이 없다면 switch문의 맨 마지막에 위치한 default문으로 이동합니다.

switch문에서는 각 조건(case)이 끝날 때마다 break를 반드시 써야합니다. break를 쓰지 않으면 switch문이 끝날 때까지 모든 case문과 default문을 차례로 실행하고, 가장 마지막의 case(주로 default)를 실행합니다. 이를 폴 스루(fall through)라고 합니다. default문이 실행되면 switch문이 종료되므로 default문에는 break를 쓰지 않아도 됩니다.

              
                let weather = "";

                switch (weather) {
                  case "sunny":
                    console.log("Great to walk!");
                    break;
                  case "gloomy":
                    console.log("Not so bad.");
                    break;
                  case "rainy":
                    console.log("Take your umbrella!");
                    break;
                  default:
                    console.log("How"s the weather outside?");
                }
            

switch문에서 case 여러 개가 같은 결과값을 나타내도록 하려면 case 옆에 다른 case를 붙여서 작성합니다.

              
                let weather = "";

                switch (weather) {
                  case "sunny": case "cloudy"
                    console.log("Great to walk!");
                    break;
                  case "gloomy": case "windy"
                    console.log("Not so bad.");
                    break;
                  case "rainy": case "snowy"
                    console.log("Take your umbrella!");
                    break;
                  default:
                    console.log("How"s the weather outside?");
                }
            

반복문

반복문(loop statement)은 주어진 조건식의 평가 결과가 true이면 블록문을 실행합니다. 이후 조건식을 다시 검사하며 조건식의 평가 결과가 false일 때까지 블록문을 반복 실행합니다. JavaScript에서 반복문은 크게 for문과 while문, do...while문으로 표현합니다.

for

for문은 가장 일반적으로 사용되는 반복문입니다. for문의 조건식은 ()안에 넣고, 조건식의 평가 결과가 true이면 블록문을 실행합니다. 조건식의 평가 결과가 false이라면 블록문을 실행하지 않습니다. for문은 변수를 초기화하는 초기화식(initialization), 조건식(stopping condition), 증감식(iteration statement), 블록문으로 구분됩니다. 각각의 표현식은 세미콜론;으로 나뉩니다. for문에 어떤 식도 선언하지 않으면 무한 루프가 됩니다. 무한루프가 우려될 때 switch문과 마찬가지로 break로 블록문을 탈출합니다.

for문의 실행 순서는 아래와 같습니다.

  1. 초기화식 실행
  2. 조건식 평가
  3. true, 블록문 실행
  4. 증감식 실행
  5. 다시, 조건식 평가
  6. false, for문 종료
              
                for(let i = 1; i < 4; i++) {
                  console.log(i + "번째 반복");
                }

                // 1번째 반복
                // 2번째 반복
                // 3번째 반복

                /* 
                let i = 0; => 초기화식
                i < 3; => 조건식
                i++; => 증감식
                {} => 블록문
                */
            

반복 감소문 Loop Backwards

증감문에서 변수에 ++이 아닌 --를 사용하면 변수가 조건식의 최대값이 아닌 최소값이 될 때 까지 반복문이 실행됩니다.

                
                for(let i = 3; i >= 0; i--) {
                  console.log((i + 1) + "번 남음");
                }

                // 3번 남음
                // 2번 남음
                // 1번 남음
                // 0번 남음
            

내부 for문 Nested for Loops

for문 안에 for문을 중첩해서 사용할 수 있습니다. 두 조건식의 평가 결과값을 비교하거나 경우의 수를 구할 때 주로 사용합니다. 내부 for문 안에 if문을 넣어 비교 조건을 통과한 값을 출력하기도 합니다.

내부 if문이 포함된 내부 for문의 실행 순서는 구구단 행렬을 만들 때와 비슷합니다. 구체적인 순서는 아래와 같습니다.

  1. forA 초기화식 실행, forA 조건식 평가
  2. true, forA 블록문 (forB) 실행
  3. forB 초기화식 실행, forB 조건식 평가
  4. true, forB 블록문 (if문) 실행
  5. if문 조건식 평가
  6. true, if문 블록문 실행 / false, if문 종료
  7. forB 증감식 실행
  8. 다시, forB 조건식 평가
  9. false, forB 종료
  10. forA 증감식 실행
  11. 다시, forA 조건식 평가
  12. false, forA 종료
                
                for(let i = 1; i <= 12; i++) {
                  for(let j = 1; j <= 5; j++) {
                    if(i + j === 5) console.log(`[${i}, ${j}]`);
                  } 
                }

                // [1, 4]
                // [2, 3]
                // [3, 2]
                // [4, 1]

                /*
                변수값의 변화 과정은 아래와 같습니다.
                i = 1 : j = 1, 2, 3, 4 / [1, 4] / 5
                i = 2 : j = 1, 2, 3 / [2, 3] / 4, 5
                i = 3 : j = 1, 2 / [3, 2] / 3, 4, 5
                i = 4 : j = 1 / [4, 1] / 2, 3, 4, 5
                i = 5 : j = 1, 2, 3, 4, 5
                i = 6 : j = 1, 2, 3, 4, 5
                .
                .
                .
                */
              

while

while문은 for문과 비슷하게 작동합니다. 대신 초기화식을 while문 바깥 위에 미리 선언하고, 블록문 안에 증감식을 넣습니다. 조건식의 결과값이 언제나 true이면 무한루프가 됩니다. 무한루프가 우려될 때 switch문과 마찬가지로 break로 블록문을 탈출합니다.

              
                let i = 1; 
                while(i < 4) {
                  console.log(i + "번째 반복");
                  i++;
                }

                // 1번째 반복
                // 2번째 반복
                // 3번째 반복
            

while문은 특정한 반복 횟수를 정하지 않고 원하는 값이 나올 때까지 계속 블록문을 반복하고 싶을 때 자주 사용합니다.

              
                const season = ["spring", "summer", "fall", "winter"]; 
                let seasonWant;
                while(seasonWant !== "spring") {
                  seasonWant = season[Math.floor(Math.random() * 4)];
                  console.log(seasonWant); 
                }

                /*
                원하는 계절이 나올 때까지 랜덤하게 계절을 보여줍니다.
                */
            

do...while

do while문은 do의 블록문을 우선 실행한 뒤에 조건식을 평가합니다. 따라서 블록문은 조건의 결과값에 상관없이 무조건 처음에 한 번 실행됩니다.

do while문의 진행 순서는 아래와 같습니다.

  1. 초기화식 실행
  2. 조건식 평가
  3. true, 블록문 실행
  4. 증감식 실행
  5. 다시, 조건식 평가
  6. false, for문 종료
              
                let i = 1;
                do {
                  i++;
                  console.log(i + "번째 반복");
                } while(i < 4)

                // 2번째 반복
                // 3번째 반복
                // 4번째 반복
            

break

break는 반복문, label문(label statement)의 블록문을 탈출하기 위해 사용합니다. 반복문에서는 break만, label문에서는 break 뒤에 label 식별자를 추가해서 작성합니다. 위 경우의 블록문이 없는 if문, 함수 등의 블록문에 break문을 사용하면 SyntaxError가 발생합니다.

              
                for(let i = 0; i < 4; i++) {
                  if(i === 2) {
                    console.log(i);
                    break;
                  }
                  console.log(i);
                }
                console.log("I found the answer!");
              
                // 0
                // 1
                // 2
                // I found the answer!


                let j = 2;
                if(j === 2) {
                  console.log(i);
                  break; // Uncaught SyntaxError : Illegal break statement
                }
            

label

label문은 식별자가 맨 앞에 붙은 문을 말합니다. switch문의 case, default도 label문에 속합니다. label문을 break로 탈출하기 위해서는 break 뒤에 label문의 식별자를 추가합니다.

                
                labelA: console.log("label A");

                /* label문은 식별자가 앞에 붙은 문을 뜻합니다. */


                labelB: {
                  console.log("label B-1");
                  break labelB;
                  console.log("label B-2");
                }
                console.log("I found the answer!");

                // label B-1
                // I found the answer!
                /* 
                label문에서 break를 사용하려면
                break 뒤에 label 식별자를 추가합니다. 
                */
              

label문은 내부 for문에서 외부 for문을 종료할 때 유용합니다. 그 외에는 label문의 사용을 권장하지 않는데, 프로그램 흐름이 복잡해지고 가독성이 나빠지기 때문입니다.

                
                forA: for(let i = 0; i < 4; i++) {
                  for(let j = 0; j < 2; j++) {
                    if(i + j === 2) {
                      console.log(`[${i}, ${j}]`)
                      break forA;
                    }
                  }
                }
                console.log("I found the answer!");

                // [1, 1]
                // I found the answer!
                /* 
                내부 반복문에서 외부 반복문을 종료시킬 때
                label문을 사용하면 편리합니다.
                그렇지만, 다소 가독성이 떨어집니다.
                */
              

continue

continue문은 반복문의 블록문 실행을 중단하고 반복문의 증감식으로 이동하게 합니다. break처럼 반복문을 탈출하지는 않고, 블록문 중간에 실행을 중단한 뒤 다음 조건을 바로 알아보고싶을 때 유용합니다.

                
                for(let i = 0; i < 4; i++) {
                  if(i !== 2) continue;
                  console.log((i + 1) + "번째 반복");
                }

                // 3번째 반복

                /* 위 예제는 아래와 같습니다. */

                for(let i = 0; i < 4; i++) {
                  if(i === 2) console.log((i + 1) + "번째 반복");
                }
              

함수

함수는 스크립트 안에서 특별한 역할을 수행하는 명령문을 한데 모은 블록문이며, 데이터 타입은 객체 타입의 함수 타입입니다. 함수는 키워드 function 으로 선언한 뒤 함수명 식별자(identifier), 함수 안에서 쓰일 매개변수(parameter), 함수 안의 명령문(statement)으로 구성됩니다. 함수는 블록문 이후 함수를 호출해야 실행됩니다. 함수를 호출할 때는 함수명을 사용하는데, 특정 값을 얻고싶을 때 함수에 적용할 인수(arguement)가 필요한 경우도 있습니다.

함수는 기본값으로 undefined를 갖고있지만, 특정한 값을 반환하고 싶다면 return문이 함수 블록문 안에 있어야 합니다.

            
                function funcA() {
                  console.log("Function A is working!");
                }
                funcA(); // "Function A is working!"
                /*
                function 기본문 + 호출
                */

                function getArea(width, height) {
                  console.log(width * height);
                }
                getArea(60, 20); // 1200
                /*
                function 기본문 + param + 호출 + 인수
                */

                function timeFlies(year){
                  yearAgo = year - 1; 
                  return yearAgo;
                }
                timeFlies(); // if (year = 2019) {yearAgo = 2018}
                /*
                function 기본문 + param + return + 호출
                */
          

JavaScript의 함수는 일급 객체입니다. 일급 객체의 특징들을 이용해서 함수표현식, 콜백함수 등 함수를 다양하게 응용할 수 있습니다. 일급 객체는 아래와 같은 특징을 지닙니다.

  1. 식별자 없이 익명의 리터럴로 표현하기
  2. 변수나 자료 구조(객체, 배열 등)에 할당, 저장하기
  3. 다른 함수의 파라미터로 전달하기
  4. return 값으로 사용하기

함수표현식 Function Expression

함수가 일급 객체라는 특징을 응용해서 함수를 익명의 리터럴 방식으로 정의하고, 변수의 할당값에 함수를 할당할 수 있습니다. 이러한 표현 방식을 함수표현식이라 합니다. 함수표현식의 문법은 함수 기본문과 거의 같습니다. 앞에 선언 키워드로 식별자를 선언한 뒤, function 키워드를 param 앞에 붙입니다. 이를 다른 말로 익명 함수표현식(anonymous function expression)이라 부릅니다. 일반 함수 작성 문법처럼 function 키워드 뒤에 함수명을 쓸 수도 있습니다. 이를 가명 함수표현식(named function expression)이라 합니다. 일반적으로는 익명 함수표현식을 사용합니다. 가명 함수표현식은 변수명과 함수명이 다른데, 변수명에 name 프로퍼티를 사용해서 할당된 함수명을 반환할 수 있습니다.

              
                const getArea1 = function(width, height) { 
                  return width * height;
                }

                /* 일반적으로 함수표현식은 함수명 식별자를 생략합니다. */


                const getArea2 = function multiply(width, height) { 
                  return width * height;
                }
                console.log(getArea2.name); // multiply

                /* 
                함수명 식별자를 그대로 쓸 수도 있습니다. 
                할당된 함수명을 알아내기 위해
                변수명.name 프로퍼티를 사용합니다.
                */
            

변수에 함수를 할당하면 변수는 함수명이 아니라 할당된 함수를 가리키는 참조값을 저장합니다. 함수표현식으로 작성된 함수를 호출할 때는 함수명이 아니라 함수를 가리키는 변수명을 사용합니다.

              
                const getAreaA = function(width, height) { 
                  return width * height;
                }
                console.log(getArea1(10, 10)); // 100


                const getArea2 = function multiply(width, height) { 
                  return width * height;
                }
                console.log(Multiply(10, 10)); 
                // Uncaught ReferenceError: multiply is not defined

                /* 
                함수표현식으로 작성된 함수를 호출할 때는
                함수명이 아니라, 함수를 할당한 변수명을 사용합니다.
                 */
            

아래의 예제들은 함수를 더욱 간단하게 쓸 수 있도록 ECMAScript 6 이후 등장한 함수 표현식입니다. 그러나 브라우저간 호환성 문제가 있기 때문에 호환성을 확인한 뒤 사용하거나 로컬 작업에만 사용할 것을 권장합니다.

화살표 함수식 Arrow Function Notation

함수표현식에서 function 키워드가 arrow로 바뀌고, arrow는 param 뒤에 붙습니다.

                
                const getArea = (width, height) => {
                  return width * height;
                }


                /* 이 함수식은 아래와 같습니다. */

                const getArea = function(width, height) {
                  return width * height;
                }

                function getArea(width, height) {
                  return width * height;
                }
              

축약 화살표 함수식 Concise Arrow Notation

더욱 축약된 함수식은 함수 안에 따로 return값을 선언하지 않고도 함수값을 출력합니다. 중괄호도 제외됩니다. ECMAScript 6에서는 이 표현식을 사용할 것을 권장합니다.

                
                const getArea = (width, height) => width * height;


                /* 이 함수식은 아래와 같습니다. */

                const getArea = (width, height) => {
                  return width * height;
                }

                const getArea = function(width, height) {
                  return width * height;
                }

                function getArea(width, height) {
                  return width * height;
                }
              

고차함수 Higher-Order Function

함수가 일급 객체라는 특징을 응용해서 변수에 함수를 할당할 수 있고, 다른 함수의 return값으로 함수를 사용할 수 있습니다. 고차함수는 return으로 다른 함수를 반환하거나, 호출할 때 다른 함수를 인수로 받는 함수입니다. 고차함수는 코드의 가독성을 높이고 디버깅을 더 편하게 하는 역할을 합니다. 고차함수의 인수로 다른 함수가 사용될 때, 해당 함수를 콜백함수라고 합니다. 콜백함수는 실행 즉시 호출되는 것이 아니라 고차함수를 호출하는 과정에서 나중에 자동으로 호출됩니다.

              
                const multiply = (x, y) => return x * y;
                const plusTen = (a) => return a + 10;
                
                multiply(plusTen(2), 3); // 36

                /* 
                위 호출식은 아래와 같이 해석됩니다.
                multiply(x = ((a = 2) + 10), y = 3);

                multiply => 고차함수(higher-order func)
                plusTen => 콜백함수(callback func)
                */
              
            

콜백함수 Callback Function

고차함수의 인수로 다른 함수가 사용될 때, 해당 함수를 콜백함수라고 합니다. 콜백함수는 고차함수가 호출된 뒤 고차함수의 실행 과정에서 특정 시점이 되면 나중에 실행됩니다.

스코프

스크립트 내의 모든 변수는 식별자가 어디에서 선언되었는지에 따라, 다른 코드가 해당 변수에 접근할 수 있는 일정한 범위를 가집니다. 이러한 규칙을 스코프(유효범위, scope)라 부릅니다. JavaScript에서 스코프는 스크립트 내 어느 위치에서든 변수에 접근할 수 있는 전역 스코프(global scope)와 블록문(block statement) 중에서 함수 안에서만 접근할 수 있는 지역 스코프(local scope)로 나뉩니다. 변수는 선언된 위치에 따라 스코프를 가지고, 스코프에 따라 전역 변수(global variable)와 지역 변수(local variable)로 나뉩니다.

            
                var globalScope = "global scope"

                function scopeA(paramA) {
                  var localScope_A = "local scope A"

                  function scopeB(paramB) {
                    var localScope_B = "local scope B"

                    function scopeC(paramC) {
                      var localScope_C = "local scope C"
                    }
                  }
                }
                /*
                전역 변수는 함수 바깥에 전역 스코프를, 
                지역 변수와 각 함수의 parameter는
                각 함수 안에 지역 스코프를 지닙니다.
                */
          

전역 스코프 Global Scope

스크립트 안, 함수 바깥에서 선언된 변수는 어디서든지 참조할 수 있는 전역 스코프를 지니고 전역 변수가 됩니다. var 키워드로 선언한 전역 변수는 전역 객체(global object) window의 프로퍼티가 됩니다.

스코프 공해 Scope Pollution

JavaScript는 변수의 선언을 시작하는 특별한 위치가 없고 스크립트 내 어느 곳에서나 변수 선언을 시작할 수 있습니다. 그리고 변수의 이름이 중복될 수 있기 때문에, 전역 변수를 자주 사용하기 쉽습니다. 그런데 전역 변수를 자주 사용하면 의도치않게 변수가 재할당되어 변수값을 예측하기 어려워지는 경우가 많습니다. 이 현상을 스코프 공해(scope pollution)이라 부릅니다. 따라서 변수를 선언할 때 전역 변수는 최대한 사용을 자제하는 것이 좋습니다.

                
                  var designer = "NaYeong"
                  function scopeDesigner() {
                    var designer = "Ujin";
                    console.log(designer);
                    var designer = "HyoMin";
                    console.log(designer);
                  }
                  console.log(designer); // "NaYeong"
                  scopeDesigner(); // "Ujin" "HyoMin"
                  /*
                  전역 변수를 많이 사용하면 의도한 값과 
                  다른 결과값이 출력될 수 있습니다.
                  */
              

지역 스코프 Local Scope

JavaScript에서 스코프는 블록문 단위(block-level)가 아닌 함수 단위(function-level)로 설정됩니다. 함수 안에 조건문(for), 반복문(if), 또는 다른 함수가 포함되어있어도 같은 함수 안에 있는 모든 변수는 같은 지역 스코프를 가집니다. 이때 함수 안에 선언된 변수를 지역 변수(local scope)라고 부릅니다. 함수 안에서 선언된 변수는 함수값이 return되어 함수 밖으로 나오지 않으면 함수 밖에서 참조할 수 없습니다.

              
                function localScope() {
                  let a = 0;
                  if (true) {
                    let a = 1;
                    for (let a = 2; a < 5; a++) {
                      console.log(`a는 ${a} (for문)`); 
                    }
                    console.log(`a는 ${a} (if문)`); 
                  }
                  console.log(`a는 ${a} (함수)`); 
                }
                localScope();   
                //a는 2, 3, 4 (for문)   
                //a는 1 (if문)          
                //a는 0 (함수)
                /*
                함수 안에 다른 블록문이 들어있어도
                블록문과 함수 안에 선언된 변수는 모두 지역 변수입니다.
                같은 스코프를 가진 변수이므로 서로 참조할 수 있습니다.
                */
            

함수레벨 스코프 Function-Level Scope

JavaScript에서, 함수 안에서 선언된 매개변수와 변수는 함수 밖에서는 유효하지 않습니다. 전역 스코프에서는 전역 변수만 참조할 수 있고 함수 내 지역 스코프에서는 지역 변수와 전역 변수를 모두 참조할 수 있습니다. 만약 전역변수와 지역변수가 같은 식별자로 중복 선언되었다면, 함수는 지역 변수를 우선 참조하여 결과값을 냅니다.

                
                let ceo = "Seon-Jung";
                function scopeFunc() {
                  let ceo = "Ari";
                  console.log(ceo); 
                }
                scopeFunc(); // "Ari"
                console.log(ceo); // "Seon-Jung"
                /*
                전역 변수와 지역 변수가 같은 식별자로 중복 선언되었다면
                함수는 지역 변수를 우선 참조합니다.
                */

                let ceo = "Seon-Jung";
                function scopeFunc() {
                  ceo = "Ari";
                  console.log(ceo);
                }
                scopeFunc(); // "Ari"
                console.log(ceo); // "Ari"
                /*
                함수 내에서는 전역 변수의 값을 참조하고 변경할 수 있습니다.
                */
              

내부함수의 스코프 Nested Function Scope

함수 안에 있는 다른 함수, 즉 내부 함수는 자신을 포함하고 있는 외부 함수의 변수에 접근할 수 있습니다.

                
                let ceo = "Seon-Jung";
                function scopeFunc2() {
                  let ceo = "Ari";
                  console.log(ceo);

                  function scopeFunc3() {
                    console.log(ceo);
                  }
                  scopeFunc3();
                }
                scopeFunc2(); // "Ari" "Ari"
                console.log(ceo); // "Seon-Jung"
                /*
                함수 안에 있는 함수는 
                외부 함수의 변수에 접근할 수 있습니다.
                */

                let ceo = "Seon-Jung";
                function scopeFunc2() {
                  let ceo = "Ari";
                  console.log(ceo);

                  function scopeFunc3() {
                    ceo = "HaYun";
                    console.log(ceo);
                  }
                  scopeFunc3();
                }
                scopeFunc2(); // "Ari" "HaYun"
                console.log(ceo); // "Seon-Jung"
                /*
                함수 안에 있는 함수는 
                외부 함수의 변수를 참조하고 변경할 수 있습니다.
                */
              

비 블록레벨 스코프 Non Block-Level Scope

블록문 안에 있지만 함수 안에 있지 않다면 JavaScript 에서는 전역 변수로 처리됩니다.

                
                let programmer = "HanSun"
                if(true) {
                  let programmer = "Yura";
                  console.log(programmer); // "Yura"
                }
                console.log(programmer); // "Yura"
                /*
                함수가 아닌 블록문 안에 선언된 변수는
                전역 변수입니다.
                */
              

블록레벨 스코프 Block-Level Scope

ECMAScript 6에서 등장한 let 키워드로 변수를 선언하면 함수 단위 스코프가 아닌 블록문 단위 스코프로 설정할 수 있습니다. 이 경우, 함수 내 블록문에 선언된 변수들은 블록문마다 스코프를 가지고, 해당 블록문을 벗어나면 더 이상 참조되지 않습니다.

                
                function scopeBlock() {
                  if (true) {
                    let b = 0;
                    for (let b = 1; b < 5; b++) {
                      console.log(`b는 ${b} (for문)`); //b는 1, 2, 3, 4 (for문)
                    }
                    console.log(`b는 ${b} (if문)`); //b는 0 (if문)
                  }
                  console.log(`b는 ${b} (함수)`); //ReferenceError: b is not defined
                }
                scopeBLock();
                /*
                let 키워드로 변수를 선언하면 
                블록문마다 서로 다른 지역 스코프를 가집니다.
                이 경우 블록문 안에 선언된 변수는
                지역 변수가 됩니다.
                */
              

객체

객체(object)는 마치 컨테이너 박스처럼 한꺼번에 여러 값을 담을 수 있는 구조를 지닌 데이터 타입입니다. JavaScript의 객체는 키(이름)와 값으로 구성된 프로퍼티(property)의 집합입니다. 객체는 각각 데이터와 액션의 역할을 수행하는 프로퍼티와 메소드를 모두 포함할 수 있기 때문에 이들을 각각이 아닌 하나의 단위로 구조화할 수 있습니다.

            
                const weather = {
                  city: "Seoul"
                  temperature: "20" + "℃"
                  todayWeather: function() {
                    console.log(this.city + " is " + this.temperature + " today.");
                  }
                }   
                
                /*
                weather는 객체의 이름입니다.
                weather의 프로퍼티는 city, temperature, todayWeather 이고
                todayWeather는 메소드입니다.
                */
          

프로퍼티 Property

프로퍼티는 객체에서 변수처럼 이름 붙여지는 객체의 데이터들입니다. 객체의 이름(name) 뒤에 .(점 연산자, dot operator)를 쓰고 프로퍼티를 붙입니다. JavaScript에서 사용할 수 있는 모든 값은 프로퍼티가 될 수 있습니다.

              
                const weather = {
                  city: "Seoul"
                  temperature: "20" + "℃"
                  todayWeather: function() {
                    console.log(this.city + " is " + this.temperature + " today.");
                  }
                }   
                
                /*
                프로퍼티는 객체의 데이터를 뜻합니다.
                this 뒤에 weather의 프로퍼티가 사용되었습니다.
                */
            

메소드 Method

객체에서 쓰이는 프로퍼티 값이 함수일 경우 일반 함수와 구분하기 위해 메소드라고 부릅니다. 메소드는 프로퍼티의 역할을 하기 때문에 프로퍼티처럼 객체의 이름 뒤에 .(점 연산자)를 쓴 뒤 메소드를 붙입니다.

              
                const weather = {
                  city: "Seoul"
                  temperature: "20" + "℃"
                  todayWeather: function() {
                    console.log(this.city + " is " + this.temperature + " today.");
                  }
                }   
                weather.todayWeather(); // "Seoul is 20℃ today."
            

배열

배열(array)은 하나의 변수에 여러 개의 값을 순차적으로 저장합니다. JavaScript에서 배열은 배열 타입의 객체입니다. 그렇지만 배열은 객체와 달리 프로퍼티 식별자가 없고, 배열 요소값(element)만 작성합니다.

            
                let arr = [];
                console.log(typeof arr); // object
          

배열 요소 Array Elements

배열은 0개 이상의 요소값을 쉼표로 구분해 대괄호[]로 묶습니다. 첫 번째 값은 문자열과 마찬가지로 index 0부터 읽을 수 있습니다. 존재하지 않는 요소에 접근하면 undefined값을 반환합니다. 배열 요소값으로 모든 데이터타입을 사용할 수 있습니다. 배열 요소로 다른 배열을 사용할 때 해당 배열 요소를 내부 배열(nested array)이라고 합니다.

              
                let arr = [1, 2, 3, "four", "five", [1, 2, 3]];
                console.log(arr[0]); // 1
                console.log(arr[5]); // [1, 2, 3]
                console.log(arr[6]); // undefined
            

배열 요소 추가 Update Array Elements

배열의 index에 값을 할당해서 원하는 순서에 요소를 추가할 수 있습니다. 값이 할당되지 않은 index 요소는 생성되지 않고, 참조하면 undefined를 반환합니다. 배열의 길이 .length는 마지막 index를 기준으로 정해집니다. index값은 length - 1 과 같으므로, index 위치에 length를 작성하면 배열의 마지막에 요소를 추가할 수 있습니다.

                
                let arr = [];
                console.log(arr[0]); // undefined

                arr[1] = 1;
                arr[3] = 3;
                arr[arr.length] = 5;
                console.log(arr); // (5)[empty, 1, empty, 3, 5]
                console.log(arr.length); // 5
                console.log(Object.keys(arr)); // ["1", "3", "5"]
              

배열 요소 삭제 Delete Array Elements

배열의 요소를 삭제하기 위해 delete 연산자와 splice 메소드를 사용할 수 있습니다. delete 연산자를 사용하면 요소값만 삭제하고 요소위치는 유지되어 length값이 변하지 않습니다. splice 메소드를 사용하면 요소 위치까지 완전히 삭제하면서 length값이 변합니다.

                
                let arr = [1, 2, 3, "four"];

                delete arr[3]; // (4)[1, 2, 3, empty]
                console.log(arr);

                /* delete 연산자는 요소의 값만 삭제합니다. */


                /* .splice(시작하는 index, 삭제할 요소 개수) */
                arr.splice(3, 1); // (3)[1, 2, 3]
                console.log(arr);

                /* .splice 메소드는 요소의 위치까지 삭제합니다. */
              

배열 프로퍼티 Array Property

Array.length

문자열의 길이를 구하는 것과 마찬가지로, 배열 요소의 index 개수를 length 프로퍼티로 구할 수 있습니다. length 프로퍼티 값을 기존 값보다 작게 변경하면 변경된 length 값보다 크거나 같은 index에 위치한 요소가 모두 삭제됩니다.

                
                let arr = [1, 2, 3, 4, 5];
                console.log(arr.legnth); // 5

                arr.length = 3;
                console.log(arr); // (3)[1, 2, 3]
              

배열 메소드 Array Method

Array.indexOf(), Array.includes()

배열 요소의 index값을 알아내기 위해 indexOf 메소드를 사용할 수 있습니다. 중복된 요소가 있는 경우 첫 번째 index만 반환하고, 해당 요소가 없다면 -1 index를 반환합니다. includes 메소드는 배열 요소가 배열에 포함되는지의 여부를 진리값으로 반환합니다.

                
                let arr = [1, 2, 3, 3, "four", "five", "six"];
                console.log(arr.indexOf("six")); // 6
                console.log(arr.indexOf(3)); // 2
                console.log(arr.indexOf("ten")); // -1
                console.log(arr.includes("six")); // true
              

Array.concat(), Array.push(), Array.unshift(), Array.splice()

concat메소드는 인수를 원본 배열이 아닌 배열 복사본의 마지막에 추가하고, 복사본을 반환합니다.

                
                let arr = [1, 2, 3, 4, 5];
                console.log(arr.concat(6, 7)); // (7)[1, 2, 3, 4, 5, 6, 7]
                console.log(arr); // (5)[1, 2, 3, 4, 5]

                /* concat 메소드는 배열의 복사본을 반환합니다. */
              

이와 달리 push, unshift, splice 메소드는 원본 배열을 직접 변경한 뒤 변경된 배열의 length값을 반환합니다. 배열의 마지막에 추가할 때는 push, 맨 앞에 추가할 때는 unshift, 중간에 추가할 때는 splice 메소드를 사용합니다. 다만, push, unshift 메소드는 concat 메소드, length로 요소를 추가하는 방법보다 성능이 더 낮습니다.

                
                let arr = [1, 2];
                console.log(arr.push(3, 4)); // 4
                console.log(arr); // (4)[1, 2, 3, 4]

                console.log(arr.unshift(5, 6)); // 6
                console.log(arr); // (6)[1, 2, 3, 4, 5, 6]

                /* push, unshift 메소드는 원본 배열의 length값을 변경시킵니다. */


                console.log(arr.splice(3, 0, 10)); // []
                console.log(arr); // (7)[1, 2, 3, 10, 4, 5, 6];

                /* 
                splice 메소드로 배열 중간에 요소를 추가하거나 제거할 수 있습니다.
                (index, 제거할 요소 개수, 추가할 요소) 
                */


                arr[arr.length] = 100
                console.log(arr); // (8)[1, 2, 3, 10, 4, 5, 6, 100];

                console.log([-1].concat(arr)); // (9)[-1, 1, 2, 3, 10, 4, 5, 6];
                console.log(arr); // (8)[1, 2, 3, 10, 4, 5, 6, 100];

                /* 성능을 고려할 때는 length, concat을 사용하는 것이 좋습니다. */
              

Array.pop(), Array.shift()

pop, shift 메소드는 배열의 원본 배열을 직접 변경한 뒤 제거한 요소를 반환합니다. 배열의 마지막 요소를 제거할 때는 pop, 맨 앞의 요소를 제거할 때는 shift 메소드를 사용합니다.

                
                let arr = [1, 2, 3, 4, 5];
                console.log(arr.pop()); // 5
                console.log(arr); // (4)[1, 2, 3, 4]
                console.log(arr.shift()); // 1
                console.log(arr); // (3)[2, 3, 4]

                /* pop, shift 메소드는 원본 배열에서 제거한 요소를 반환합니다. */
              

Array.join()

join 메소드는 배열 요소를 전체로 연결해 생성한 문자열을 반환합니다. join 메소드는 add 연산자보다 빠릅니다.

                
                let arr = [1, 2, 3, 4, 5];
                console.log(arr.join()); // "1,2,3,4,5"
                console.log(arr.join("")); // "12345"
                console.log(arr.join(":")); // "1:2:3:4:5"
              

Array.slice()

slice 메소드는 인수로 지정된 배열의 부분을 다른 배열로 복사한 뒤 반환합니다.

                
                let arr = [1, 2, 3, 4, 5];
                console.log(arr.slice()); // [1, 2, 3, 4, 5]
                console.log(arr.slice(1, 2)); // [2]
                console.log(arr.slice(1, 4)); // [2, 3, 4]
                console.log(arr.slice(1)); // [2, 3, 4, 5]
                console.log(arr.slice(-1)); // [5]
                /* 
                slice 메소드로 배열 요소를 중간부터 복사해서 배열로 반환합니다.
                (indexA, indexB - 1) 
                인수가 음수라면 배열의 끝에서부터 요소를 반환합니다.
                (-index)
                */
              

Array.reverse()

reverse 메소드는 원본 배열 요소 순서를 반대로 변경한 후 변경된 원본 배열을 반환합니다.

                
                let arr = [1, 2, 3, 4, 5];
                console.log(arr.reverse()); // [5, 4, 3, 2, 1]
              

배열 순회 메소드 Array Iterator

일반 반복문처럼, 배열 요소를 순회해서 반환하고 싶을 때 배열 순회 메소드를 사용합니다.

Array.forEach()

forEach 메소드는 배열 요소를 순회하며 순서대로 반환합니다. forEach 메소드 안에 콜백 함수를 넣어 배열요소를 이용한 함수를 실행합니다.

                
                let arr = [1, 2, 3];

                const arr2 = arr.forEach(items => console.log(items));
                // 1
                // 2
                // 3

                /* 위의 함수표현식은 아래와 같습니다. */

                const arr2 = (items) => console.log(items);
                arr.forEach(arr2);



                const arr3 = arr.forEach(items => items);
                console.log(arr3); // undefined 

                /* 
                forEach 메소드는
                새로운 배열을 생성하지 않습니다. 
                */
              

Array.map()

map 메소드는 배열 요소를 순회하며 순서대로 새로운 배열로 반환합니다. 원본 배열을 반환하는 forEach 메소드와 달리, map 메소드는 원본 배열에 영향을 주지 않습니다.

                
                let arr = [1, 2, 3];

                const arr2 = arr.map(items => console.log(items));

                // 1
                // 2
                // 3

                /* 위의 함수표현식은 아래와 같습니다. */

                const arr2 = (items) => console.log(items);
                arr.map(arr2);



                const arr3 = arr.map(items => items);
                console.log(arr3); // (3)[1, 2, 3] 

                /* 
                map 메소드는 원본 배열과 다른
                새로운 배열을 생성합니다. 
                */
              

Array.filter()

filter 메소드는 배열 요소 중 특정 조건에 true값을 반환하는 요소들을 골라 순서대로 새로운 배열로 반환합니다. map 메소드와 마찬가지로 원본 배열에 영향을 주지 않습니다.

                
                let arr = [1, 2, 3];

                const arr2 = arr.filter(items => {
                  return items < 3;
                });

                // 1
                // 2

                console.log(arr2); // (2)[1, 2] 

                /* 
                filter 메소드는 원본 배열과 다른
                새로운 배열을 생성합니다. 
                */