[JavaScript] 정리

wonyu·2022년 5월 15일
0

2. script 태그

HTML에서 JavaScript를 포함할 때 어떤 방식이 더 효율적일까?

  1. <script> 태그를 <head> 에 작성할 경우 브라우저가 한 줄씩 파싱하다가, 해당 태그를 만나면 block되고 필요한 JS 파일을 다운 받아서 실행한 다음, 다시 파싱으로 넘어감

    • 단점: 파일 크기가 클 경우 사용자가 웹 사이트 보기까지 많은 시간 소요
  2. <body> 의 가장 끝 부분에 추가할 경우, HTML 파싱이 끝난 다음 JS 파일을 fetching하고 실행하게 됨

    • 장점: 사용자는 JS 파일을 다운받기 전에도 페이지를 볼 수 있음
    • 단점: 웹사이트가 JS에 의존적일 경우 (서버에서 데이터를 받아와야 하거나 DOM 요소를 스타일링 하는 등) 사용자가 정상적인 페이지를 보기까지 기다려야 함
  3. <head> 에 작성하되 async 속성을 이용

    → HTML 파싱을 하다가 해당 <script> 태그를 만나면 병렬적으로 fetch
    → JS 파일 다운로드가 끝나면 HTML 파싱을 잠깐 멈추고 실행
    (JS 파일을 실행하는 동안 HTML 파싱이 block됨 & 여러 스크립트가 있을 경우, 순서 상관 없이 먼저 다운로드 되는 파일이 먼저 실행됨 (순서 보장 X))

    <head>
    	<script async src="main.js"></script>
    </head>
    • 장점: 다운로드 시간 절약 가능
    • 단점: JS 파일이 HTML 파싱이 끝나기 전에 실행되기 때문에 원하는 요소가 정의되어 있지 않을 수 있음, HTML 파싱을 멈추는 구간이 있기 때문에 사용자가 페이지를 보기까지 여전히 시간이 어느 정도 소요
  4. <head> 에 작성하되 defer속성을 이용

    → HTML 파싱을 하다가 해당 <script> 태그를 만나면 다운로드 명령만 내리고 파싱 끝까지 실행. 파싱이 다 끝난 다음에 JS 파일 실행

    • 장점: HTML 파싱을 끝내고 사용자에게 페이지를 보여준 다음 JS 파일 실행, 여러 스크립트가 있을 경우 파싱하는 동안 병렬적으로 다운받은 뒤 파싱이 끝나면 순서대로 실행 (순서 보장)

use strict

  • JavaScript 파일 맨 윗 부분에 정의 (typescript 사용할 때는 정의할 필요 없음)
    'use strict';
    • ECMAScript 5 에 정의되어 있음
    • 선언되지 않은 변수에 값을 할당하는 등 flexible하지만 비상식적인 행동을 할 경우 에러를 발생시킴
    • JS엔진이 좀 더 효율적으로 분석할 수 있음 → 성능 개선

3. 데이터 타입

  • let
    • ES6에 추가됨
  • var
    • 선언하기 전에 값을 할당/출력할 수 있음 (쓰지 마세요)
      → var 호이스팅 현상 (어디에 선언하든 항상 선언을 제일 위로 끌어올려주는 것)
    • block scope이 없음 -> block 안에 선언했더라도 block 바깥 어디서든 사용 가능
  • const
    • favor immutable data type always : 웬만하면 변경 불가능한 데이터 타입을 사용해라
      • security : 해커가 코드 삽입해서 값을 변경하는 것을 방지
      • thread safety : 스레드가 동시에 값을 변경하는 것을 방지해줌
      • reduce human mistakes

Variable types

  • primitive, single item
    • number, string, boolean, null, undefined, symbol
      • boolean
        • false: 0, null, undefined, NaN, ‘’
          • null: 너는 빈 값이야! 라고 할당해주는 것 let nothing = null;
          • undefined: 값이 지정되어 있지 않은 것 let x;
      • symbol
        • object를 위해 고유한 식별자가 필요하거나, concurrent하게 일어날 수 있는 코드에서 우선순위를 정해주고 싶을 때 사용
          const symbol1 = Symbol('id');
          const symbol2 = Symbol('id');
          console.log(symbol1 === symbol2);   // false
          
          const symbol1 = Symbol.for('id');
          const symbol2 = Symbol.for('id');
          console.log(symbol1 === symbol2);   // true
          
          console.log(`value: ${symbol1.description}`);   // value: id
          // .description을 이용해서 출력
  • object
    • single 아이템들을 한 단위로 묶어서 관리할 수 있게 해줌 ex) const yj = { name: 'yj', age: 20 };
  • function, first-calss function
    • 일급 함수: 함수를 변수에 할당하거나, 함수에 인자로 전달하거나, 함수에서 리턴할 수 있는 함수

Dynamic typing

  • runtime에서 타입이 정해지기 때문에 에러가 발생하는 경우가 많음
    ex) 위에서 내가 string 타입이라고 생각하고 string method를 썼는데, 이후에 number로 바뀌고 또 다시 string method를 썼다면..? 에러 발생
    (그래서 나온 게 typescript)
    let text = 'hello';
    console.log(`value: ${text}, type: ${typeof text}`);  // value: hello, type: string
    
    text = 1;
    console.log(`value: ${text}, type: ${typeof text}`);  // value: 1, type: number
    
    text = '7' + 5;
    console.log(`value: ${text}, type: ${typeof text}`);  // value: 75, type: string
    
    text = '8' / '2';
    console.log(`value: ${text}, type: ${typeof text}`);  // value: 4, type: number

5. 함수

  • 자바스크립트에서 함수는 object로 간주됨
    → 함수를 변수에 할당하거나, 함수에 인자로 전달하거나, 함수의 결과로 리턴할 수 있음
  • primitive 타입 파라미터의 경우 value가, object 파라미터의 경우 레퍼런스가 전달됨
    • 함수 내에서 object의 값을 변경할 수 있음
  • Default parameter
    function showMessage(message, from='unknown') {
    	console.log(`${message} by ${from}`);
    }
    showMessage('Hiii');
  • First-class function
    • 변수처럼 다뤄지는 함수
    • 변수에 할당할 수 있고, 다른 함수에 인자로 전달할 수 있고, 다른 함수로부터 리턴될 수 있다.
  1. Function expression
    • function declaration은 hoist 되지만, function expression은 hoist 되지 않는다. (선언 이전에 호출 불가)
      const print = function() {  // 익명 함수 (function expression)
      	console.log('print');
      }
  2. Callback function
    • 함수를 인자로 전달해서 상황에 맞게 호출해~ 라고 하는 것
      // 여기서는 printYes, printNo가 콜백 함수
      function randomQuiz(answer, printYes, printNo) {
      	if (answer === 'love you') {
      		printYes();
      	} else {
      		printNo();
      	}
      }
  • anonymous function
    const printYes = function() {
    	console.log('yes');
    }
  • named function
    • 디버깅을 편리하게 하거나, 재귀 호출을 하기 위해 사용

      const printNo = function print() {
      	console.log('yes');
      	print();
      }
  • arrow function
    • always anonymous

      const simplePrint = () => console.log('print');
  • 선언과 동시에 함수 호출하기
    • IIFE: Immediately Invoked Function Expression

      (function hello() {
      	console.log('IIFE');
      })()

6. 클래스

  • 클래스
    • 붕어빵을 만들 수 있는 틀, 템플릿 같은 것
      - declare once
      - no data in

      class Person {
        // constructor
        constructor(name, age) {
          // 속성 (field)
          this.name = name;
          this.age = age;
          
        }
        // 행동 (method)
        speak() {
          console.log(`${this.name} hello!`);
        }
      }
      
      const yj = new Person('yujin', 20);
      console.log(yj.name);
      yj.speak();
    • Getters & Setters

      • 오브젝트에 설정한 프로퍼티에 조작을 하고 싶을 경우 이를 처리하기 위해 setter 사용

        class User {
          constructor(firstName, lastName, age) {
            this.firstName = firstName;
            this.lastName = lastName;
            // getter를 정의하면 this.age는 메모리에서 읽어오는 게 아니라 getter 호출
            // setter를 정의하면 값 할당 시 바로 메모리에 할당하는 게 아니라 setter 호출
            // -> 따라서 콜스택 에러가 날 수 있으므로 getter/setter 안에서는 변수명을 조금 다르게 해줌
            this.age = age;
          }
        
          get age() {
            return this._age;
          }
        
          set age(value) {
            this._age = value < 0 ? 0 : value;
          }
        }
        
        const user1 = new User('Yj', 'Won', -1);
        console.log(user1.age);
    • 상속

      class Shape {
        constructor(width, height, color) {
          this.width = width;
          this.height = height;
          this.color = color;
        }
      
        draw() {
          console.log(`drawing ${this.color} color of`);
        }
      
        getArea() {
          return this.width * this.height;
        }
      }
      
      // extends를 통해 상속하면 부모 클래스의 필드, 메서드 사용 가능
      class Rectangle extends Shape {}
      class Triangle extends Shape {
        // 메서드 오버라이딩 가능
        draw() {
          // 오버라이딩 시 부모 클래스에 있는 같은 이름의 메서드는 호출되지 않음
          // 호출하고 싶을 경우 super 키워드 사용
          super.draw();
          console.log('^_^');
        }
        getArea() {
          return (this.width * this.height) / 2;
        }
      
      }
  • 오브젝트
    • 틀을 이용해서 만든 붕어빵, 클래스를 이용해서 생성한 인스턴스
      • created many times
      • data in

7. 오브젝트

  • 오브젝트
    • 참고: mdn Object
    • 생성자 함수
      function Person(name, age) {
        // this = {};
        this.name = name;
        this.age = age;
        // return this;
      }
    • Cloning
      const user = { name: 'wyj', age: 20};
      const user2 = user;
      user2.name = 'hi';  // 같은 곳을 가리키기 때문에 user의 name이 바뀜
      console.log(user);
      
      // 복사해서 같은 곳을 가리키지 않는 새로운 오브젝트를 사용하고 싶다면 Object.assign() 사용
      const user3 = Object.assign({}, user);
      user3.name = 'assign!';
      console.log(user);
      console.log(user3);

10. JSON

  • HTTP
    • 클라이언트가 어떻게 서버와 통신할 수 있는지에 대한 규약
    • Hypertext: 링크뿐만 아니라 문서, 파일 등을 포함
  • HTTP를 이용해서 데이터를 받아올 수 있는 방법
    • AJAX

      • XHR 오브젝트
    • fetch() API

      → XML을 사용하면 파일의 크기도 커질뿐 아니라 가독성도 좋지 않음

      → 요즘은 JSON을 많이 사용

JSON

  • 오브젝트 → JSON: stringify()
    const rabbit = {
      name: 'tori',
      color: 'white',
      birthDate: new Date(),
      // 함수나 Symbol처럼 JS에만 있는 데이터는 변환되지 않음
      jump: () => {
        console.log(`${name} can jump!`);
      }
    }
    
    // jump를 제외하고 문자열로 변환됨
    const json = JSON.stringify(rabbit);
    console.log(json);
  • JSON → 오브젝트: parse()
  • stringify() , parse() 메서드의 reviver 함수 사용법
    // 좀 더 세밀하게 통제하고 싶을 때 사용
    let json = JSON.stringify(rabbit, ['name', 'color']);
    console.log(json);
    
    json = JSON.stringify(rabbit, (key, value) => {
      return key === 'name' ? 'yj' : value;
    });
    console.log(json);

11. Callback

  • 콜백 함수
    • callback 되는 함수
    • 즉, 함수에 인자로 넘겨져서 해당 함수 내부에서 실행되는 함수

12. Promise

  • Promise
    • JavaScript에서 제공하는, 비동기를 간편하게 처리할 수 있도록 도와주는 오브젝트
    • 정상적으로 기능이 수행되었을 경우 처리된 결과값을, 그렇지 않을 경우 에러를 전달
  • Producer
    • 새 프로미스가 만들어질 때, executor 함수가 자동으로 바로 실행됨

      const promise = new Promise((resolve, reject) => {
        // doing some heavy work (네트워크 통신, 파일 읽기 등)
        console.log('doing something...');
        setTimeout(() => {
          resolve('yj');
          // reject(new Error('no network'));
        }, 2000);
      });
  • Consumers
    • then, catch, finally를 이용해서 값을 받아올 수 있음

      promise
        .then(value => {
          // resolve 콜백함수를 통해 전달한 값이 value에 전달됨
          console.log(value);
        })
        .catch(error => {
          console.log(error);
        })
        .finally(() => {
          console.log('finally');
        })

13. async / await

  • async / await
    • Promise를 이용해서 .then , .catch 를 chaining하면 코드가 복잡해질 수 있음 이 때 async&await를 사용하면 보통 코드를 작성하듯이 작성하여 동기적으로 동작하게 할 수 있음
  • async
    • async 를 함수 앞에 쓰면 자동으로 리턴되는 값을 Promise로 만들어줌
      async function fetchUser() {
        return 'yj';
      }
      
      const user = fetchUser();
      user.then(console.log); 
  • await
    • Promise도 여러 번 중첩하게 되면 콜백 지옥과 비슷한 문제 발생 (chaining으로 인한 가독성 저하 및 유지보수 어려움)
      → async await를 사용하면 기존에 동기적으로 코드를 작성하듯이 할 수 있고 가독성도 좋아짐

      function delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }
      
      async function getApple() {
        await delay(1000);
        return 'apple!';
      }
      
      async function getBanana() {
        await delay(1000);
        return 'banana!';
      }
      
      // Promise로 작성할 경우
      // function pickFruits() {
      //   return getApple()
      //   .then(apple => {
      //     return getBanana()
      //     .then(banana => `${apple} + ${banana}`);
      //   });
      // }
      
      // async await
      async function pickFruits() {
        try {
          // 단, 순차적으로 실행되어 1초 + 1초 -> 총 2초 소요
          // 서로 연관된 것이 없는데도 순차적으로 실행되므로 비효율적
          // const apple = await getApple();
          // const banana = await getBanana();
      
          // 병렬적으로 수행하는 법 (코드 더러운 ver)
          const applePromise = getApple();
          const bananaPromise = getBanana();
          const apple = await applePromise;
          const banana = await bananaPromise;
          return `${apple} + ${banana}`;
        } catch(err) {
          console.log(err);
        }
      }
      
      pickFruits().then(console.log);
  • Promise.all()
    async function pickAllFruits() {
      // all 메서드 사용 시 비동기적으로 실행 후 응답 array 반환
      return Promise.all([getApple(), getBanana()])
      .then(fruits => fruits.join(' + '));
    }
    
    pickAllFruits().then(console.log);

0개의 댓글