[LG CNS AM CAMP 1기] 프론트엔드 2 | JavaScript

letthem·2024년 12월 26일
0

LG CNS AM CAMP 1기

목록 보기
2/16
post-thumbnail

node.js 설치

npm - node package manager

모던 자바스크립트 특징

가상 DOM(Virtual DOM)을 이용하는 라이브러리/프레임워크를 사용

  • 실제 DOM을 효율적으로 업데이트하기 위한 기법
  • UI 변경 최적화 위해 사용
  • 실제 DOM을 미리 메모리 상에서 가상으로 만들어 두고, UI 업데이트를 가상 DOM에서 먼저 처리하는 방식
  • DOM(Document Object Model)
    • root > header > body ... 태그들을 트리 형태로 만들어서 관리
    • 문서를 효율적으로 찾을 수 있다.

DOM

가상 DOM

패키지 관리자를 사용

  • 라이브러리나 도구를 쉽게 설치, 관리, 업데이트, 제거할 수 있게 해주는 도구
  • 의존성 관리와 버전 관리
  • npm, yarn, pip, gem, composer, ...

ES6(ECMAScript 2015) 이후의 문법을 사용

  • let, const
    MDN
  • 화살표 함수(arrow function)
  • 템플릿 리터럴(template literal) : `${expression}`
    MDN
  • 구조 분해 할당(destructuring assignment)
    Poiema
  • 기본 매개변수
  • for ~ of 구문 (데이터 하나씩 가져오는 것. for ~ in은 인덱스를 가져오는 것)
  • 모듈 시스템 (import, export)
  • 클래스
  • Promise
  • Map, Set
  • Rest, Spread 연산자

모듈 번들러(module bundler)를 사용

  • 여러 모듈을 하나의 번들로 묶는 번들링을 처리하는 도구
  • CSS, 이미지, 소스 코드 등을 따로 서버에 요청하면 빈번하게 요청이 왔다갔다 해야한다. → 속도가 느려지므로 하나의 큰 덩어리로 만들어서(번들링) 요청해야한다!
  • build해서 번들링
  • 네트워크 요청 수를 줄이고 로딩 속도를 향상
  • Webpack, Parcel, Rollup, ...

트랜스파일러(transpiler)를 사용

  • 한 프로그램 언어로 작성된 소스 코드를 다른 프로그래밍 언어로 변환하는 도구
    ex) 데이터 타입 제한하는 문법인 타입스크립트를 순수 자바스크립트로 바꾸어주는 것
    = 런타임이 계속 생겨나면 브라우저가 무거워지므로 새로운 런타임이 없이 자바스크립트 언어로 바꿔주는 것
    = 새로운 문법 체계를 기존 문법 체계로 변환해 주는 것
  • 언어 간 변환, 버전 호환성(ES6 지원 안 하는 곳 이전 버전에 맞춰서 바꿔주기), 플랫폼 호환성(Windows, Linux), 언어 확장, ... 등
  • Babel

SPA(Single Page Application)로 작성

  • Content가 데이터!

  • MPA는 페이지가 그때그때 바뀐다.

  • SPA는 페이지가 한 번 내려온다. 그 이후에는 JS로 데이터만 가져와서 그때그때 뿌려주는 것이다.
    → 동작이 더욱 빠르고 부드럽게 느껴진다.


자바 스크립트 작성 및 실행

REPL(Read-Eval-Print-Loop)

읽고, 해석하고, 보여주고, 반복한다

node.js 내부에 탑재된 프로그램.

명령 프롬프트에 node 라고 입력하면 REPL 이 실행된다.

c:\javascript> node				⇐ REPL 실행
Welcome to Node.js v22.12.0.
Type ".help" for more information.
>
> let i = 100
undefined
> let j = 200
undefined
> i + j
300
> .exit						    ⇐ REPL 종료

자바스크립트 파일 실행

  • node를 이용해서 실행

브라우저를 이용해서 실행 => HTML 문서를 통해서 실행

sample.html 파일을 생성하고, inline, internal, external 방식으로 스크립트 코드를 추가 ⬇️

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- external JS -->
  <script src="helloJavaScript.js"></script>
</head>
<body>
  <!-- inline JS -->
  <button onClick="javascript: alert('button clicked')">버튼</button>
  
  <!-- internal JS -->
  <script>
    let s = "abc";
    console.log(s);
  </script>
</body>
</html>

웹 서버 기동(live server) 후 해당 페이지를 요청하면 응답 전달된 페이지의 스크립트 코드가 실행된다.

웹 브라우저의 개발자 도구에서 제공하는 콘솔을 이용해서 스크립트를 실행


JavaScript

  • 정식 명칭 : ECMAScript

표현식(expression)

값을 생성하거나 반환하는 코드

// 리터럴 : 값 그자체
5
"hello"
true
[1, 2, 3]
{a: 1}
var a = 5;
var b = 5 + 3;
var c = true && false;

function add(a, b) {
  return a + b;
}
add(10, 20);

문장(statement)

프로그램이 수행할 동작을 정의하는 코드
하나 이상의 표현식으로 구성될 수 있으며, 대부분의 경우 세미콜론(;)으로 끝난다.
ex) 선언문, 조건문, 반환문, 반복문, ...

let x = 6;

// 조건문
if (x > 10) {
  console.log("크다");
} else {
  console.log("작다");
}

// 반복문
for (let i = 0; i < 10; i++) {
  console.log(i);
}

키워드(keyword)

특정 동작을 정의하거나 예약어로 사용되어 특정 기능을 수행하는 단어들
변수 이름이나 함수 이름으로 사용할 수 없다.

식별자(identifier)

변수, 함수, 클래스, 모듈 등의 이름을 정의하는 데 사용

  • 문자, 숫자, $, 밑줄을 사용할 수 있다.
  • 문자, $, 밑줄로 시작해야 한다. (= 숫자로는 시작할 수 없다.)
  • 예약어는 식별자로 사용할 수 없다.
  • 대소문자를 구분한다.

관례)

  • 클래스 이름은 항상 대문자로 시작
  • 변수, 함수, 속성, 메서드의 이름은 항상 소문자로 시작
  • 여러 단어로 구성된 식별자는 각 단어의 첫 글자를 대문자로 사용한다. → 카멜 표현식

cf) Case Style

snake_case: C
kebab-case: AWS S3 버킷 이름, url
camelCase: javascript
PascalCase: pascal

자료형(identifier)

cf_1
cf_2

원시 자료형(Primitive values)

  • null - "값이 없음"을 명시적으로 나타내는 데이터 타입
  • undefined - 변수는 선언되었지만 값이 할당되지 않은 상태
  • boolean - true, false
  • number - 숫자(정수, 실수, Infinity, -Infinity, NaN)
    • -2^53 ~ 2^53-1
  • BigInt - 매우 큰 정수
    • -2^53 ~ 2^53-1 범위 밖의 숫자를 표현
  • string - 문자열
  • Symbol - 고유하고 변경 불가능한 원시 값 → 주로 객체 속성의 고유한 식별자를 만들기 위해 사용

BigInt ⬇️

> i = 1234567890
1234567890
> console.log(typeof(i))
number

> i = 1234567890n
1234567890n
> console.log(typeof(i))
bigint

뒤에 n을 붙이면 BigInt type이 된다.

Symbol ⬇️

> a = "abc"
'abc'
> b = "abc"
'abc'
> a == b
true
> a === b
true

> a = Symbol("abc")
Symbol(abc)
> b = Symbol("abc")
Symbol(abc)
> a == b
false
> a === b
false

객체 타입

user defined objects

const person = { name: "홍길동", age: 23 };

built-in objects
: 미리 정의되어 있는 객체 타입
ex) objects, arrays, dates, map, sets, intarrays, floatarrays, promises, ...

  • map : key, value
> a = new Map()
Map(0) {}
> a.set("name", "홍길동")
Map(1) { 'name' => '홍길동' }
> a.set("age", 23)
Map(2) { 'name' => '홍길동', 'age' => 23 }

😇 map이 object보다 더 빠르고 유연하기 때문에 반복적인 데이터는 map 사용 권장!!

  • set : 중복을 허용하지 않는 데이터
> a = new Set([1, 2, 3, 4, 1, 2, 3])
Set(4) { 1, 2, 3, 4 }				⇐ 중복되지 않은 값을 가짐

동적 타이핑(dynamic typing)

변수에 데이터가 대입되는 시점에 변수의 자료형이 결정된다.

자바에서는 int i = 10; 와 같이 변수를 선언할 때 변수가 가질 수 있는 데이터 타입을 지정
자바스크립트에서는 var i = 10; 와 같이 변수를 선언할 때 데이터 타입을 지정하지 않고,
변수에 값이 할당되는 시점에 할당되는 값에 따라 변수의 데이터 타입이 결정

자바에서는 int i = "abc"; 변수의 데이터 타입과 할당되는 값의 데이터 타입이 일치하지 않아서 오류가 발생하는 반면, 자바스크립트에서는 var i = "abc"; 변수가 할당되는 시점에 데이터 타입이 결정되므로 정상적으로 동작한다.


정리

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>자바 스크립트 자료형</h1>
  <script>
    // number type
    let n1 = 1234;
    let n2 = 56.78;
    console.log(n1, typeof(n1)); // 1234 number
    console.log(n2, typeof(n2)); // 56.78 number

    // string type
    let s1 = 'Hello';
    let s2 = "World";

    // 이스케이프 처리
    let s3 = 'Hello, \'Inho\'';
    let s4 = "Hello, \"Inho\"";

    // 템플릿 문자열(template literal)
    let name = 'Inho';
    let age = 23;
    let hello1 = '이름은 "' + name + '"이고, 나이는 ' + age + '세 입니다.';
    console.log(hello1);

    let hello2 = `이름은 "${name}"이고, 나이는 ${age}세 입니다. 내년에는 ${age + 1}세가 됩니다.`;
    console.log(hello2);

    // boolean type
    let b1 = true;
    let b2 = false;
    console.log(b1, typeof(b1)); // true boolean
    console.log(b2, typeof(b2)); // false boolean

    // falsy value
    console.log(Boolean('')); // false
    console.log(Boolean(0)); // false
    console.log(Boolean(-0)); // false
    console.log(Boolean(null)); // false
    console.log(Boolean(undefined)); // false
    console.log(Boolean(NaN)); // false
    console.log(Boolean(false)); // false

    if (undefined) {
      console.log('undefined는 true');
    } else {
      console.log('undefined는 false'); // undefined는 false 출력
    }

    if (-1) {
      console.log('-1은 true'); // -1은 true 출력
    } else {
      console.log('-1은 false')
    }

    // null type
    let nullValue = null;

    // undefined type
    let u1;
    let u2 = undefined;

    // array type = 다양한 데이터 타입을 순서(index)대로 모아놓은 것
    let arr1 = [1, 2, 3, 4, 5];
    let arr2 = [1, 2.3, 'hello', true, null, undefined, [1, 2, 3]];
    console.log(arr1[3]); // 4
    console.log(arr2[5]); // undefined
    arr1[3] = 100;
    console.log(arr3); // [1, 2, 3, 100, 5]

    // object type = key와 value로 이루어진 데이터 타입
    //               key는 문자열 또는 심볼이어야 하고, 값은 모든 데이터 타입이 가능
    let obj1 = { a: 'apple', b: 'banana', c: 'cherry'};
    let obj2 = { name: 'Inho', age: 23, 'favorite colors': ['red', 'blue', 'green']};
    console.log(obj1['a']) // apple
    console.log(obj1.a) // apple
    console.log(obj2['favorite colors']); // ['red', 'orange', 'green']
  </script>
</body>
</html>

var, let, const, 호이스팅

var

⭐️⭐️⭐️⭐️⭐️호이스팅: 어떤 변수의 선언이 그 문서의 가장 위로 올라가는 현상

<script>
  console.log(x1); // undefined => x1 변수가 hoisting되어 선언된 것으로 처리
  var x1;
  console.log(x1); // undefined
</script>

내부적으로 아래와 같이 해석되어 실행

<script>
  var x1;
  console.log(x1); // undefined => x1 변수가 hoisting되어 선언된 것으로 처리
  console.log(x1); // undefined
</script>
<script>
  console.log(x1); // undefined => x1 변수가 hoisting되어 선언된 것으로 처리
  var x1;
  console.log(x1); // undefined
    
  x1 = 100;
  console.log(x1, typeof(x1)); // 100 'number'
</script>

선언부와 할당부가 나뉘고, 선언부가 맨 위로 호이스팅 된다.

<script>
  console.log(x2); // undefined
  var x2 = 100;
  console.log(x2); // 100
</script>

내부적으로는 선언부만 위쪽으로 올라간다.

<script>
  var x2;
  console.log(x2);    // undefined
  x2 = 100;
  console.log(x2);    // 100
</script>

let

<script>
  console.log(x3);    // Cannot acces 'x3' before initialization
  let x3 = 100;
  console.log(x3);    // 100
</script>

let, const 를 사용하면 내부적으로는 선언부가 호이스팅돼서 올라가긴 하는데
초기화 되기 전까지 변수는 TDZ(Temporal Dead Zone) 라는 공간에 머물러있다.
이러한 변수는 초기화 전까지는 참조(사용)를 할 수 없다.
→ 초기화되고 쓸 수 있다는 것을 보장해주므로 명확하게 사용할 수 있다!!! var 쓰지 말고 let, const 쓰자 😍

<script>
  console.log(x3);    // Cannot acces 'x3' before initialization
  let x3 = 100;
  console.log(x3);    // 100
</script>
<script>
  let x3;             // 변수 선언이 호이스팅되지만 초기화되지 않음 
                      // ⇒ TDZ 본관 ⇒ 초기화되기 전에 참조할 수 없음 		
  console.log(x3);    // Cannot access 'x3' before initialization
  x3 = 100;
  console.log(x3);    // 100
</script>

const

const로 선언한 변수는 값을 변경할 수 없다.

<script>
  const a = 100;
  a = 200;        // Uncaught TypeError: Assignment to constant variable.
</script>

호이스팅

function으로 함수 선언 - 함수 호이스팅

함수 자체가 scope로 호이스팅 되어 어디서든지 읽을 수 있다.

<script>
  hello(); // Hello, JavaScript 이것도 가능하다! 함수 호이스팅 때문에

  // 함수 선언문 형식으로 정의. 함수 정의 부분이 scope로 호이스팅 되어 가장 높은 곳으로 올라간다.
  function hello() {
    console.log("Hello, JavaScript");
  }

  hello(); // Hello, JavaScript
</script>

var로 선언

선언부만 올라가므로 undefined로 보인다.

<script>
  hello(); // hello is not a function
   
  var hello = function() {
    console.log("Hello, JavaScript");
  };

  hello(); // Hello, JavaScript
</script>

위 코드는 아래와 같이 해석된다.

<script>
  var hello; // undefined 상태

  hello(); // hello is not a function
   
  hello = function() {
    console.log("Hello, JavaScript");
  };

  hello(); // Hello, JavaScript
</script>

let으로 선언

var 대신 let으로 선언하면
참조할 수 없어서 reference error 가 뜬다.

<script>
  hello(); // Uncaught ReferenceError: Cannot access 'hello' before initialization
   
  let hello = function() {
    console.log("Hello, JavaScript");
  };

  hello(); // Hello, JavaScript
</script>

위 코드는 아래와 같이 해석된다.

<script>
  let hello; // 초기화되기 전까지 TDZ(Temporal Dead Zone)에 머물러 있다.

  hello(); // Uncaught ReferenceError: Cannot access 'hello' before initialization
   
  hello = function() {
    console.log("Hello, JavaScript");
  };

  hello(); // Hello, JavaScript
</script>

연산자

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>연산자</h1>
  <script>
    // 나눗셈, 나머지 연산자
    console.log(`7 / 5 = ${7 / 5}`); // 1.4
    console.log(`7 % 5 = ${7 % 5}`); // 7 % 5 = 2 <= 나머지

    // 나머지 연산의 부호는 왼쪽 피연산자의 부호를 따른다.
    console.log(`7 % 5 = ${7 % 5}`); // 7 % 5 = 2 <= 나머지
    console.log(`7 % -5 = ${7 % -5}`); // 7 % -5 = 2 <= 나머지
    console.log(`-7 % 5 = ${-7 % 5}`); // -7 % 5 = -2 <= 나머지
    console.log(`-7 % -5 = ${-7 % -5}`); // -7 % -5 = -2 <= 나머지

    // 문자열 결합
    console.log("Hello, " + 'JavaScript' + `!!!!`);

    // 문자열의 일부를 선택 => 문자열[인덱스]
    // 0         1         2
    // 012345678901234567890
    const message = "Hello, JavaScript!";
    console.log(message[0]); // H <= 첫 번째 글자
    console.log(message[17]); // !
    console.log(message[message.length - 1]); // <= 마지막 글자
    console.log(message[18]); // undefined

    // prefix(전위 연산자) 방식, postfix(후위 연산자) 방식
    let x = 100;
    console.log(x); // 100
    if (x++ > 100) { // 후위 연산자
      console.log("100 초과");
    } else {
      console.log("100 이하");  // 100 이하 (100)
    }
    console.log(x); // 101

    let y = 100;
    console.log(y); // 100
    if (++y > 100) { // 전위 연산자
      console.log("100 초과"); // 100 초과 (101)
    } else {
      console.log("100 이하"); 
    }
    console.log(y); // 101
  </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>연산자</h1>
  <script>
   // 동등 연산자(equality operator), 일치 연산자(strict equality operator)
   let n = 100;
   let s = "100";

   console.log(n, s); // 100 "100"
   console.log(typeof n, typeof s); // number string

   // 동등 연산자 <= 두 변수의 값을 비교
   if (n == s) {
    console.log("동등 연산자: 같다"); // 같다
   } else {
    console.log("동등 연산자: 다르다");
   }

   // 일치 연산자 <= 두 변수의 타입과 값을 함께 비교
   if (n === s) { // if (typeof n == typeof s && n == s)
    console.log("일치 연산자: 같다");
   } else {
    console.log("일치 연산자: 다르다"); // 다르다
   }

   // Object.is() 메서드
   console.log(-0 === +0); // true (모호하다ㅜㅜ)
   console.log(Object.is(-0, +0)); // false (더욱 정확하다)

   console.log(typeof NaN); // number
   console.log(Number.NaN === NaN); // false (모호하다ㅜㅜ NaN는 Number 타입인데..)
   console.log(Object.is(Number.NaN, NaN)) // true

   // 삼항 연산자 => (조건식) ? 참일 때 : 거짓일 때
   let x = 100;

   // if - else 구문으로 조건식을 구현
   if (x > 100) {
    console.log("100 초과");
   } else {
    console.log("100 이하");
   }

   // 동일한 로직을 삼항 연산자로 표현
   console.log(x > 100 ? "100 초과" : "100 이하");
  </script>
</body>
</html>

일치연산자 보다는 object ~ is 쓰는 게 더 정확하다!!
삼항 연산자는 조건부 렌더링할 때 많이 쓰인다.

자료형 변환

강제 자료형 변환 => String(), Number(), Boolean()**

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>자료형 변환</h1>
  <script>
    // 문자열로 변환
    console.log(String(52)); 
    console.log(String(true));

    // 숫자로 변환
    console.log(Number('52'));
    console.log(Number(true)); // 1
    console.log(Number(false)); // 0
    console.log(Number("숫자")); // NaN

    // 숫자로 변환 가능 여부를 확인할 때 NaN 로 확인하면 안 된다.
    console.log(Number("52") == NaN); // false -> 숫자로 바꿀 수 있다!
    console.log(Number("오십이") == NaN); // false -> 이상하다..
    console.log(NaN == NaN); // false NaN 끼리는 무조건 다르다고 나온다. OMG

    // isNaN으로 확인해야 한다.
    console.log(isNaN(Number("52"))); // false
    console.log(isNaN(Number("오십이"))); // true
    console.log(isNaN(NaN)); // true

    // boolean 으로 변환
    // 0, -0, null, false, NaN, undefined, '' => false
    // 그 외의 값은 true
  </script>
</body>
</html>

number인지 확인하려면 isNaN으로 체크해보자.
falsy 한 값 외엔 true 로 반환된다.

자동 자료형 변환

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>자료형 변환</h1>
  <script>
    // 숫자와 문자열에 + 연산자를 사용하면 숫자를 문자열로 자동 변환
    // + 연산자는 문자열 결합 연산자로 사용된다.
    console.log(10 + 20); // 30
    console.log("10" + 20); // 1020
    console.log(10 + "20"); // 1020
    console.log("10" + "20"); // 1020

    // 숫자와 문자열에 + 가 아닌 다른 연산자를 사용하면 문자열을 숫자로 자동 변환
    console.log(20 - 10); // 10
    console.log("20" - 10); // 10
    console.log(20 - "10"); // 10
    console.log("20" - "10"); // 10

    console.log("20" * 10); // 200
    console.log("20" / 10); // 2
    console.log("20" % 10); // 0

    // 부정 연산자를 두 번 사용(!!)하면 Boolean() 함수를 사용하는 것과 동일해진다.
    console.log(Boolean(0), !!0); // false false
    console.log(Boolean(1), !!1); // true true
  </script>
</body>
</html>

함수

함수를 정의하는 방법

cf_1
cf_2

1. 함수 선언문
function add(x, y) { <= 반드시 함수 이름이 정의되어야 한다. x, y는 매개변수
return x + y;
}

console.log(add(10, 20)); <= 함수 이름으로 호출. 10, 20은 인자

2. 함수 표현식

변수는 값을 주고 받을 수 있다.
함수 표현식을 이용하면 함수를 변수처럼 사용할 수 있다.

변수 ⬇️

let a = 100;
let b = a;
console.log(a); 	// 100
console.log(b); 	// 100

(익명) 함수 표현식 ⬇️ ← 보통 이렇게 많이 쓴다.

let add = function (x, y) { return x + y; };
    ~~~   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |	  |
    |     <- 익명 함수 표현식
    <- 함수 변수
    
let sum = add;
console.log(add(10, 20)); // 30
console.log(sum(10, 20)); // 30

기명 함수 표현식 ⬇️

let sum1 = function add(x, y) { return x + y };
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           기명 함수 표현식 => **함수 외부에서는 함수 이름으로 사용할 수 없으나**, 
                           함수 내부에서 자기 호출할 (=재귀 호출) 사용할 수 있다.
let sum2 = sum1;

console.log(sum1(10, 20)); // 30
console.log(sum2(10, 20)); // 30
console.log(**add(10, 20)**); // **add is not defined**

→ 재귀 호출 예시 - 기명 함수 표현식을 쓴다.

let myFactorial = function **factorial**(n) {
  if (n === 1) {
    return 1;
  }
  return n * **factorial**(n - 1);
};

console.log(myFactorial(10));

함수 선언문으로 정의한 함수는


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

자바스크립트 내부에서 함수 이름함수 변수 이름이 동일한 함수 표현식으로 자동 변경

            __________________________________________
            let _add_ = function **add** (x, y) {return x + y; };

3. Function() 생성자 함수를 이용 ← 잘 안 쓴다.

cf_1

  • 동적으로 함수를 만들어야 하는 경우에 쓴다.
    new Function(arg1, arg2, / …, / argN, functionBody)

let add = new Function('x', 'y', 'return x + y');

4. 함수를 활용
함수를 다른 함수의 인자로 전달

<script>
  let click = function(fname) {
    fname();
  };

  let loginButtonClick = function() {
    console.log("로그인 되었습니다.");
  };

  let logoutButtonClick = function() {
    console.log("로그아웃 되었습니다.");
  };

  click(loginButtonClick);
  click(logoutButtonClick);
</script>

click이라는 똑같은 interface를 이용하고 있음에도 불구하고 넣는 함수에 따라 동작이 달라진다.
인터페이스는 미리 정의되어 있는데 구체적인 동작은 실행시점에 결정된다!!

ex2) calculator

let calculator = function(f, x, y) {
  f(x, y);
};

let add = function(x, y) {
  console.log(\`x + y = ${x + y}\`);
};
let sub = function(x, y) {
  console.log(\`x - y = ${x - y}\`);
};
let mul = function(x, y) {
  console.log(\`x * y = ${x * y}\`);
};
let div = function(x, y) {
  console.log(\`x / y = ${x / y}\`);
};

calculator(add, 10, 20);
calculator(sub, 10, 20);
calculator(mul, 10, 20);
calculator(div, 10, 20);

외부에 있는 함수만 고치면 된다. 인터페이스 내부를 고칠 필요가 없다! 굳. 코드가 아름다워진다!!! ⭐️

비교) 변화가 생기면 내부에 변화가 생겨야 한다,,, 다음과 같이.. 😭

let cal = function(f, x, y) {
  switch(f) {
    case "add": console.log(`x + y = ${x + y}`); break;
    case "sub": console.log(`x - y = ${x - y}`); break;
    case "mul": console.log(`x * y = ${x * y}`); break;
    case "div": console.log(`x / y = ${x / y}`); break;
  }
};

5. 함수를 다른 함수의 리턴값으로 활용
변수처럼 움직일 수 있다 - 1급 객체

일급 객체(first-class object): 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체

let foo = function() {
  return function() {
    console.log("반환함수");
  };
};

let bar = foo();
bar(); // 반환함수

특정 함수가 어떻게 동작해야하는지 미리 규정해놓고 이를 사용하도록 강제할 때 사용한다.
함수의 실행결과가 함수가 됨.

multiplier는 함수를 반환하여 동작을 규정한다.
factor에 어떤 수가 들어오면 그 수를 곱한다.

ex2)

function twoTimes() {
  return function(number){
    return number * 2;
  };
}

const no3 = twoTimes();
console.log(no3(3)); // 6

const no4 = twoTimes();
console.log(no4(4)); // 8

-----
  
function multiplier(factor) {
  return function(number) {
    return number * factor;
  };
}

const double = multiplier(2);
console.log(double(3)); // 6
console.log(double(4)); // 8

const triple = multiplier(3);
console.log(triple(3)); // 9
console.log(triple(4)); //12

내부에서 정의해놓고 외부에서 쓰도록 한다. 4. 함수를 활용과 반대 개념

함수 종류

ex)

// 방법1. 반복문을 이용
function factorial_loop(n) {
  let result = 1;
  for (let i = 1; i <= n; i++) {
    result *= i;
  }

  return result;
}

// 방법2. 재귀함수를 이용
function factorial_recursive(n) {
  if (n == 1) {
    return 1;
  }
  return n * factorial_recursive(n - 1);
}

console.log(factorial_loop(10));
console.log(factorial_recursive(10));

// 함수 실행에 소요된 시간을 측정
// 방법1. 함수 내부에 시작과 끝에 시간을 측정하는 코드를 추가
// => 루프 방식은 구현이 가능한데, 재귀함수는 구현이 불가(어려움)
// 방법2. 함수를 호출하는 부분에 시간을 측정하는 코드를 추가

let start = performance.now();
console.log(factorial_loop(10));
let end = performance.now();
console.log(`실행시간: ${(end - start).toFixed(10)}ms`);

start = performance.now();
console.log(factorial_recursive(10));
end = performance.now();
console.log(`실행시간: ${(end - start).toFixed(10)}ms`);

시간을 측정하고, 계산하고, 출력하는 부분이 중복되어 있다.
반복되는 공통 기능 부분을 콜백함수를 이용하면 좋다!

// 함수를 반환하는 함수를 이용해서 실행 시간을 측정
function performance_checker(func) {
  return function(n) {
    let start = performance.now();
    console.log(func(n));
    let end = performance.now();
    console.log(`실행시간: ${(end - start).toFixed(10)}ms`);
  }
}

performance_checker(factorial_loop)(10);
performance_checker(factorial_recursive)(10);

→ 기존 함수의 동작을 확장하거나 새로운 동작을 정의 (기존 함수를 변경하지 않고)

closer(p59) : 바깥쪽 변수(func)는 performance_checker가 종료되면 사라져야 하는데 내부의 함수(function(n)에 의해서 상태가 유지되는 현상 -> 이 함수가 실행되어야 사라진다-!
→변수의 상태를 계속해서 지속시키는 역할을 한다.

AoP(관심사의 분리) 개념

// 함수 실행에 필요한 인자값과 결과값을 출력하는 함수
const add = (a, b) => a + b;
add(10, 20);
const sub = (a, b) => a - b;

function logger(func) {
  return function(...args) {
    console.log(`인자값: ${args}`);
    const result = func(...args);
    console.log(`결과값: ${result}`);
    return result;
  }
}
logger(add)(10, 20);
logger(sub)(20, 10);

콜백 함수(callback function)

개발자가 코드를 이용해서 명시적으로 호출하는 함수가 아니고,
개발자는 단지 함수를 등록하기만 하고,
어떤 이벤트가 발생하거나 특정 시점에 도달했을 때 시스템에서 호출하는 함수

💡 콜백 함수 예시 : 이벤트 핸들러 !!!

이벤트 핸들러 => 특정 이벤트가 발생했을 때 실행되는 함수
\<input type="button" onClick="function() {....}" />

즉시 실행 함수 = 자기 호출 함수

함수 정의와 동시에 바로 실행하는 함수

익명 함수 표현식을 괄호로 둘러싼바로 호출(실행)할 수 있도록 괄호 쌍을 추가

( function (name) { console.log(`${name}는 즉시 실행됩니다.`)} )('이 함수');

name : 매개변수
('이 함수') : 함수 실행에 필요한 인자값을 전달

⭐️⭐️⭐️⭐️⭐️ 화살표 함수

// 익명 함수 표현식을 이용해서 함수를 정의
const add1 = function (x, y) { return x + y; };
console.log(add1(2, 3)); // 5

// 화살표 함수
// function 키워드를 제거하고, 함수 파라미터와 본문 사이에 화살표(=>)를 추가
const add2 = (x, y) => { return x + y; };
console.log(add2(2, 3)); // 5

// 화살표 함수 본문이 결과를 반환하는 구문으로 되어 있는 경우, 중괄호와 return 키워드를 생략할 수 있다.
const add3 = (x, y) => x + y;
console.log(add3(2, 3)); // 5

// 매개변수가 하나인 경우, 매개변수를 감싸고 있는 소괄호도 생략이 가능하다.
const add4 = x => x + 4;
console.log(add4(2)); // 6

// 객체를 반환하는 경우에는 반환 객체를 소괄호로 감싸야 한다.
const add5 = (x, y) => { return { result: x + y }; };
console.log(add5(2, 3)); // { result: 5 }

const add6 = (x, y) => ({ result: x + y });
console.log(add6(2, 3)); // { result: 5 }

😍 장점 : 읽기가 좋다. 마치 자연어를 읽듯이 코드를 만들고 해석할 수 있다.

cf_1
cf_2


프로그램 내에서 데이터를 처리하는 데 필요한 기술 ⬇️
문자열, 배열, 객체 등으로 데이터를 표현한다.

배열

// 배열 선언
let values = [ "빨강", "노랑", "파랑", true, 20 ];
console.log(values);
console.dir(values);

// 배열 길이는 배열 객체의 length 속성(property)을 이용하여 확인할 수 있다.
console.log(values.length);
console.log(values["length"]);

// 배열 데이터를 추가
values.push("검정");
values[values.length] = "하양"; // 마지막 인덱스 + 1에 새로운 것 추가
console.log(values); // ['빨강', '노랑', '파랑', true, 20, '검정', '하양']
values[values.length + 10] = "보라";
console.log(values); // ['빨강', '노랑', '파랑', true, 20, '검정', '하양', <10 empty items>, '보라']
console.dir(values);
console.log(values[10]); // undefined
// 배열의 모든 요소를 순차적으로 가져와서 출력
let values = [ "빨강", "노랑", "파랑", "초록" ];

console.log("방법1. 개별 요소를 직접 참조해서 출력");
console.log(values[0]);
console.log(values[1]);
console.log(values[2]);
console.log(values[3]);

console.log("방법2. for loop를 이용");
for (let i = 0; i < values.length; i++) {
  console.log(values[i]);
}

console.log("방법3. for - in를 이용 => 개별 요소의 인덱스를 반환");
for (let index in values) {
  console.log(index, values[index]); // index를 사용
}

console.log("방법4. for - of를 이용 => 개별 요소의 값을 반환"); 
let idx = 0;
for (let value of values) {
  console.log(idx++, value); // 값 자체 그대로 사용
}

console.log("방법5-1. forEach  => 개별 요소를 콜백 함수로 전달"); // 배열 내장 메서드 사용
// 함수 선언문 형태로 콜백 함수를 정의
function printData(data) {
  console.log(data);
}
values.forEach(printData);

console.log("방법5-2. forEach ---");
// 함수 표현식 형태로 콜백 함수를 정의
let printData2 = function (data) {
  console.log(data);
}
values.forEach(printData2);

console.log("방법5-3. forEach ---");
// 콜백 함수를 직접 정의
values.forEach(function(data) {
  console.log(data);
});

console.log("방법5-4. forEach ---");
// 화살표 함수로 콜백 함수를 정의
values.forEach((data) => {
  console.log(data);
});

console.log("방법5-5. forEach ---");
// 화살표 함수로 콜백 함수를 정의 => 화살표 함수 축약
values.forEach(data => console.log(data)); // 가장 간단하다 !

console.log("인덱스와 값을 함께 출력");
values.forEach((data, index) => {
  console.log(index, data);
});

console.log("인덱스와 값을 함께 출력 => 화살표 함수 축약");
values.forEach((data, index) => console.log(index, data));

forEach cf


객체

// 객체 선언
let person = { 
  "name": "홍길동",
  'age': 23,
  isMarried: false, // 따옴표 생략 시 문자열로 인식. but, JSON에서는 따옴표 생략하면 안 된다.
  "favorited colors": [ "red", "blue" ],
  hello: function() {
    console.log(`안녕하세요, 나는 ${this.name}입니다.`);
  }
};

// 객체 항목을 참조 => 객체이름.키이름 또는 객체이름["키이름"]
console.log(person.name);
console.log(person["name"]);  
console.log(person["favorite colors"]); // 공백이 있으면 따옴표로 감싸야 한다.
person.hello();

// 객체 항목의 값을 변경
person.name = "김철수";
person.hello(); // 이름 부분이 김철수로 바뀌었다 !

// 객체 속성 추가
person.email = "chulsu@test.com";
person["address"] = "서울시 강남구";
console.log(person); // email, address 추가되었다.
// 객체 선언
let person = { 
  "name": "홍길동",
  'age': 23,
  isMarried: false, // 따옴표 생략 시 문자열로 인식. but, JSON에서는 따옴표 생략하면 안 된다.
  "favorited colors": [ "red", "blue" ],
  hello: function() {
    console.log(`안녕하세요, 나는 ${this.name}입니다.`);
  }
};

// 객체의 모든 항목을 가져와서 출력
console.log("for-in 구문을 이용 => 객체의 key를 반환")
for (let key in person) {
  console.log(key, person[key]);
}

// for-of 구문은 값을 반환하는데 iterable 해야하므로 사용 불가능하다. (순서라는 개념이 있어야 함)
// Uncaught TypeError: person is not iterable
/*
for (let value of person) {
  console.log(value);
}
*/

// 객체의 키를 배열로 만들어서 반환
// key들을 배열로 가져와서 순회하면서 추출
for (let key of Object.keys(person)) {
  console.log(key, person[key]);
}

function print(key, value) {
  console.log(key, value);
}
Object.keys(person).forEach(key => print(key, person[key]));

Object.keys(person).forEach(function(key) {
  console.log(key, person[key]);
})

Object.keys(person).forEach(key => {
  console.log(key, person[key]);
})

Object.keys(person).forEach(key => console.log(key, person[key]));

객체는 배열과 달리 index가 없기 때문에 key 값을 가져와서 작업해야한다.


하루만에 자바스크립트 A to Z 다 배운 것 같다. 와 너무 빠르다 ㅜ.ㅜ 그래도 예시가 많아서 + 깊게 이해부터 하는 느낌으로 배워서 화살표 함수가 어떻게 줄여지는 지 등등 소름돋는 포인트가 많아서 좋았다.!!!!

0개의 댓글