자바스크립트 중급 (feat. 코딩앙마)

roadzmoon76·2022년 1월 29일
2

JavaScript

목록 보기
5/6

https://youtube.com/playlist?list=PLZKTXPmaJk8JZ2NAC538UzhY_UNqMdZB4

📖 변수(var, let, const)

  • var는 한번 선언된 변수를 다시 선언 가능

  • 동일한 상황에서 let은 에러나옴

  • var는 선언 전에 사용 가능. 호이스팅 덕임. 선언은 호이스팅되나 할당은 호이스팅 안되기 때문에 undefined 찍힘

var name;
console.log(name); // undefined
name = 'JiMoon';
  • 호이스팅은 스코프 내부 어디서든 변수 선언은 최상위에 선언된 것 처럼 행동한다는 개념

  • letconst호이스팅이 되지만 TDZ(Temporal Dead Zone이라는 개념을 도입해 다른 언어들과 같은 논리적 순서에 맞게 동작하도록 설정했기 때문임. 이는 불확실성을 떨어뜨려줌.

    위 사진을 보면. 4번줄이 생기면서 3번줄은 TDZ에 들어갔기 때문에 오류 발생.

  • 변수는 크게 3단계 생성과정을 거침

    1. 선언 단계
    2. 초기화 단계 : undefined를 할당 해주는 단계
    3. 할당 단계
  • var는 아래와 같이 2단계로 작동 함

    1. 선언 및 초기화 단계
    2. 할당 단계
  • let은 3단계 생성과정 거침

    1. 선언 단계 : 호이스팅 되며 이루어짐
    2. 초기화 단계 : 실제 코드에 도달했을때 발생
    3. 할당 단계
  • const는 1단계 생성과정 거침. 그래서 나중에 할당만으로 값을 바꾸는게 불가능

    1. 선언 + 초기화 + 할당

      7번줄에서 할당을 동시에 안해줬기 때문에 에러발생
  • 스코프

    • var 는 함수 스코프(function-scoped)
      함수 스코프는 함수 내에서 선언된 변수만 지역변수.

      위 사진처럼 if문 안에서 var로 선언된 변수는 같은 함수 내라면 if문 밖에서도 사용가능

      위 사진처럼 var도 함수 스코프 밖에선 사용 불가

    • let, const 는 블록 스코프(block-scoped)
      블록 스코프는 모든 코드블록에서 선언된 변수는 코드블록 내에서만 유효하고, 외부에선 접근할 수 없다는 의미. 즉 코드블록 내부에서 선언한 변수는 지역변수임. 여기서 말하는 코드블록은 함수, if 문, for 문, while 문, try/catch 문 등을 의미

      위 사진처럼 코드블럭 밖에서 사용하자 에러

  • var는 이제 사용하지 않고, 예측 가능한 결과를 내고 버그를 줄일 수 있는 letconst 사용 권장

📖 생성자 함수

  • 비슷한 객체를 여러개 만들어야 하는 경우에 사용

  • 생성자 함수의 첫글자는 대문자로 이름짓고, new 연산자를 사용해서 함수를 호출

    어떻게 동작하나 순서를 살펴보면

    1. new 함수명(); 을 실행하면 일단 빈객체를 만들고 this에 할당함
    2. 함수 본문을 실행하며 this에 프로퍼티들을 추가함
    3. this를 반환함
      function User(name, age) {
      // this = {}
      this.name = name;
      this.age = age;
      this.sayName = function () {
        console.log(`내 이름은 ${name}이야`)
      // return this;
      }
      실제로 // 문장들은 저 코드에 없지만 new를 붙여 실행하는 순간 저 방식으로 알고리즘이 돌아감.
      또한 메서드를 추가하는것도 가능

📖 객체 메소드, 계산된 프로퍼티

  • 아래 첫번째 사진에서 5번줄 age 대신, 두번째 사진[a] 처럼 대괄호로 묶어주면 a라는 문자열이 아니라 변수 a에 할당된 값이 들어감. 이를 computed property(계산된 프로퍼티) 라고 함

  • 식 자체를 넣는것도 가능

    위의 정보들을 응용하여 객체를 만드는 함수를 만들면

    ⭐️ 위에서 2번째 줄에 key 의 위치에 [key] 이렇게 써줘야 스트링 매개변수를 받아 우리가 알고있는 key 의 문법 Object.abc 또는 Object['abc'] 의 형태가 되어서, key 값으로 인식할 수 있다

  • Object Methods

    • Object.assign() : 객체 복제
      const user = {
      name : 'JiMoon'
      age : 29,
      }
      const cloneUser = user; // 이러면 객체가 복사되는 것이 아니라 주소만 복사됨
      // 하나의 객체를 두 변수가 접근하게됨
      또 다른 객체로써 동일하게 복제하려면 Object.assign() 메소드를 써야함
      const newUser = Object.assign({}, user);
      // 위와같이 하면 첫번째 매개변수는 초기값 빈객체를 만들어주는것이며
      // 두번째 매개변에서 받은 객체의 프로퍼티들을 빈객체에 넣어주는 메소드임
      {} + { name : 'JiMoon', age : 29 }
      = { name : 'JiMoon', age : 29 }
      // 위와 같이 서로 다른 주소값에 담긴 동일한 정보를 담고있는 객체로 복제됨
      Object.assign({ gender:'male' }, user);
      // 위와 같이 초기 객체에 프로퍼티를 담아주게되면 그 이후에 들어감
      // 키가 같다면 덮어씌움
      이런 assign의 알고리즘을 응용해서 객체를 합치는데도 이용가능
      const user = {
       name : 'JiMoon',
      }
      const info1 = {
       age : 29,
      }
      const info2 = {
       gender = 'male',
      }
      Object.assign(user, info1, info2);
    • Object.keys() : 키 배열 반환
    • Object.values() : 값 배열 반환
    • Object.entries() : 키/값 배열 반환
    • Object.fromEntries() : 키/값 배열을 개체로

📖 Symbol

  • 심볼은 유일한 식별자를 만들 때 사용
    const a = Symbol(); // new를 붙이지 않음
    const b = Symbol();
    console.log(a) // Symbol()
    console.log(b) // Symbol()
    console.log(a === b) // false
    console.log(a == b) // false
  • 객체의 키에는 숫자나 블린을 넣어도 반환하면 문자열로 반환됨
  • 이렇게 객체 프로퍼티 키는 문자열로 가능하며, 참조시에도 문자열로 입력
  • 심볼도 오브젝트의 키로 활용 가능
  • 다만 위에서 언급한 일부 오브젝트 메소드들은 키가 심볼형 인건 건너 뜀. for in 도 건너뜀
  • 그럼 이렇게 꽁꽁 숨겨져있는걸 어디에 사용할까? 특정 객체에 원본 데이터는 건들이지 않고 속성을 추가할 때 사용

    ⭐️ 20번째 줄 for ... in ... 에서 이미 객체의 키값을 받기때문에 key 자체로 받으면 되고, value 값은 key 자체가 스트링 값이므로 객체의 문법중 하나인 Object[''] 을 사용해서 value 값에 접근하는것.

Symbol.for() : 전역 심볼

  • 하나의 심볼만 보장받을 수 있음. 없으면 만들고, 있으면 가져오기 때문

  • Symbol 함수는 매번 다른 Symbol 값을 생성하지만, Symbol.for 메소드는 하나를 생성한 뒤 키를 통해 같은 Symbol을 공유

    const id1 = Symbol.for('id');
    const id2 = Symbol.for('id');
    id1 === id2; /// true
    // 이름을 얻고싶다면 Symbol.keyFor 이용
    Symbol.keyFor(id1) // 'id'
    // 전역심볼이 아닌 심볼은 description 사용
    const id = Symbol('id 입니다');
    id.description; // 'id 입니다'
  • 숨겨진 Symbol key 보는 법

    • Object.getOwnPropertySymbols() : 심볼 프로퍼티만 보여줌
    • Reflect.ownKeys() : 심볼 키를 포함한 객체의 모든 키를 보여줌

📖 숫자, 수학 method

  • toString() : 기본적으로 스트링으로 전환할때 사용하는 메소드인데 괄호안에 넣어준 숫자의 진법으로 변경해주는 기능도 있다

    let num = 10;
    
    num.toString(); // '10'
    num.toString(2); // '1010'
    
    let num2 = 255;
    
    num2.toString(16); // 'ff'
  • Math : 자바스크립트에는 수학과 관련된 프로퍼티와 메소드들을 가지고있는 Math 라는 내장 객체가 있다

    • Math.PI; : 원주율을 구해줌
    • Math.ceil() : 올림
    • Math.floor() : 내림
    • Math.round() : 반올림
      🙋 만약 특정 소수점 자리 까지만 사용해야 한다면?
      let userRate = 30.1234;
      // 요구사항 : 소수점 둘째자리 까지 표현 (셋째 자리에서 반올림)
      Math.round(userRate * 100)/100 // 30.12
      👉 해당 메소드들은 항상 소수점 첫째자리를 기준으로 계산하므로 내가 10의 n제곱을 곱하거나 나눠서 나리를 맞춰주면 됨
      👉 혹은 반올림 같은 경우에는 toFixed() 를 사용하면 됨. ()안에 내가 나타내고자하는 소수점 자리수를 넣어주면 알아서 반올림 해서 나타내줌. 주의할점은 결과는 문자열로 반환해 줌. 그래서 반환 후 Number() 를 이용해 숫자로 변환 후 작업하는 경우가 많음.
    • isNaN() : NaN 인지 아닌지 판단해줌. 오직 inNaN() 만이 판단 가능. NaN 은 자기 자신마저도 같지 않다고 나오기 때문
      let x = Number('x'); // NaN
      x == NaN // false
      x === NaN // false
      NaN == NaN // false
      isNaN(x) // true
      isNaN(3) // false
    • parseInt() : 문자열을 숫자로 바꿔줌. Number() 와 다른점은 문자가 혼용되어 있어도 동작함.
      다만 parseInt 도 읽을 수 있는 부분 까지만 읽고 문자를 만나면 숫자를 반환하므로, 시작이 문자면 바로 반환됨.
      let margin = '10px';
      parseInt(margin); // 10
      Number(margin); // NaN
      let redColor = 'f3';
      parseInt(redColor); // NaN
      그러나 parseInt 는 두번째 인수를 받아 전달받는 숫자의 진수를 지정할 수 있다
      let redColor = 'f3';
      parseInt(redColor); // NaN
      let rdeColor = 'f3';
      parseInt(redColor, 16); // 243
      parseInt('11', 2) // 3
    • parseFloat() : parseInt 와 동일하게 동작하나 parseInt 는 정수만 반환하지만 parseFloat 는 부동 소수점을 반환함
      let padding = '18.5%';
      parseInt(padding); // 18
      parseFloat(padding); // 18.5
    • Math.random() : 0이상 1미만의 무작위 숫자 생성
      🙋 만약 1~100 사이 임의의 숫자를 뽑고 싶다면?
      👉 Math.floor(Math.random()*100)+1
    • Math.max() / Math.min() : 괄호안의 인수 값 중 최대값, 최소값 구함
      Math.max(1,4,-1,5,10,9,5.54); // 10
      Math.min(1,4,-1,5,10,9,5.51); // -1
    • Math.abs() : 절대값
    • Math.pow(n, m) : n의 m승 값
    • Math.sqrt() : 제곱근

📖 문자열 메소드(String methods)

  • '', "" : 작은 따옴표과 큰 따옴표는 큰 차이가 없다 상황에 맞게 쓰면됨

    • html 코드 같은 경우는 안에 "" 로 된 내용들이 있으므로 '' 가 편하다
    • 영어로 된 문장에는 안에 '' 가 들어가므로 "" 가 편하다
  • `` : 백틱은 ${} 를 이용해 변수를 표현하거나 표현식을 쓸 수 있다

    • 백틱은 여러줄을 포함할 수 있다
      let desc = `오늘은 맑고 화창한
      날씨가 계속 되겠습니다.
      내일은 비소식이 있습니다.`;
      let desc = '오늘은 맑고 화창한\n날씨가 계속 되겠습니다.'; // 따옴표로 줄바꿈을 표현하려면 /를 써야함
      let desc = '오늘은 맑고 화창한 // error! 따옴표로 작성시에는 꼭 한줄로 써야지 줄바꿈시 에러남
      날씨가 계속 되겠습니다.
      내일은 비소식이 있습니다.'
  • 배열처럼 문자열도 length 로 문자열 길이를 구할 수 있다

  • 배열과 동일하게 문자열도 대괄호와 숫자로 특정 위치에 접근 가능. 하지만 배열과 달리 한글자만 바꾸는건 허용 x

  • toUpperCase() / toLowerCase() : 영어의 경우 대문자, 소문자로 변환

  • indexOf(text) : 문자를 인수로 받아 몇번 째에 위치하는지 알려줌. 만약 찾는 문자가 없으면 -1 을 반환

    • 포함된 문자가 여러개라도 첫번째 위치만 반환
    • 만약 if 문 사용시 조건에 주의하며 써야함
      if(desc.indexOf('Hi')) {
      console.log('Hi가 포함된 문장입니다.'); // Hi가 첫번째에 있어서 0이 나와 false가 됨
      }
      if(desc.indexOf('Hi') > -1) {
      console.log('Hi가 포함된 문장입니다.'); // 이래야 true가 되서 if문이 발동됨
      }
  • str.slice(n, m) : 시작점 n 부터 m 이 없으면 문자열 끝까지, 양수면 그 숫자까지(포함x), 음수면 끝에서부터

  • str.substring(n, m) : nm 사이 문자열 반환. nm 을 바꿔도 동작. 음수는 0으로 인식

  • str.substr(n, m) : n 부터 시작해서 m 개를 가져옴

  • str.trim() : 앞 뒤 공백 제거

  • str.repeat(n) : 문자열을 n번 반복

  • 문자열도 비교가 가능함

    • 1 보다 3 이 크듯이, 'a' 보다 'c' 가 큼
    • 아스키 코드 표를 보면 됨
    • 아스키 코드 표를 다 외울 필요는 없고 알파벳 뒤로갈수록 커지며, 소문자가 대문자보다 크다는 것만 알고있으면 됨
    • 아스키 번호는 codePointAt(0); 을 이용해 얻을 수 있다. 예를들어 소문자 'a' 의 번호가 궁금하다면 'a'.codePointAt(0); // 97 이렇게 나온다.
    • 반대로 숫자코드를 알고있어도 문자코드를 얻을 수 있다
      String.fromCodePoint(97); // 'a'
  • 실전 예제

    • 숫자를 제외한 문자로만 구성된 새로운 배열 얻기

    • 금칙어가 들어가있다면 알림

      • indexOf 활용

        위 사진 처럼 하면 원하는 결과가 제대로 안나옴

        조건문에 조건을 이렇게 해줘야 정상적으로 출력

      • includes 활용

    📖 배열 메소드

  • arr.splice(n, m) : n 에서 시작해서 m 개 지움.

    • 특정 요소까지 추가하려면 arr.splice(n, 2, x, y) 이런식으로 써주면 됨.
    • 사이에 요소를 넣고 싶다면 m0 으로 하면 됨
    • arr.splice() 는 삭제된 요소를 반환함
  • arr.slice(n, m) : n 부터 m 까지 반환. m 은 포함 X

  • arr.concat(arr2, arr3 ..) :합쳐서 새배열 반환

  • arr.forEach(fn) : 배열 반복. 함수를 인수로 받음

    let users = ['Mike', 'Tom', 'Jane'];
    users.forEach((item, index, arr) => {});
    // item 은 Mike, Tom, Jane
    // index 는 0, 1, 2
    // arr 는 users
  • arr.indexOf / arr.lastIndexOf : 발견하면 해당요소의 인덱스 반환. 없으면 -1 을 반환

    • 인수가 두개면 두번째 인수는 시작 위치를 의미. 그 이후부터 탐색
    • 끝에서부터 탐색하고 싶다면 arr.lastIndexOf 사용
  • arr.includes() : 포함 하는지 확인. 불리언으로 반환

  • arr.find(fn) / arr.findIndex(fn) : indexOf 처럼 찾는다는 의미는 동일하지만, 보다 복잡한 연산이 가능하도록 함수를 전달 가능

    • 짝수를 찾아낸다든지, 성인을 찾아낸다든지 할 수 있다
    • arr.find(fn) 는 첫번째 true 값만 반환하고 끝. 만약 없으면 undefined 를 반환
    • arr.findIndex(fn) 은 해당 index 를 반환. 없으면 -1 을 반환

  • arr.filter(fn) : 조건을 만족하는 모든 요소를 찾고싶을 때

  • arr.reverse() : 역순으로 재정렬

    • 최근 가입한 유저부터 보여주거나
    • 게시판에서 가장 최근부터 작성된 글 순서를 보여줄 때 자주 사용
  • arr.map(fn) : 함수를 받아 특정 기능을 시행하고, 새로운 배열을 반환

  • arr.join() : 배열을 합쳐서 문장을 만들 때 사용

    • 인수로 구분자를 전달함. 아무것도 전달하지 않으면 쉼표
  • arr.split() : 문자열을 나눠서 배열로 만들어줌

    • 인수에는 어떤 문자로 나눌것인지 정해줌
  • arr.isArray() : 배열인지 아닌지 확인

    • 자바스크립트에서 배열은 객체에 속하기때문에 타입오브는 객체라고 알려줌
  • arr.sort(fn) : 배열 재정렬. 배열 자체가 변경되니 주의.

    • arr3가 뒤죽박죽이 된건 정렬할 때 요소를 문자열로 취급하기 때문임. 1과 2로 시작하는 13과 27이 제일 앞으로 오게된 것
      제대로 된 결과를 도출하고 싶다면 값을 비교해줄수있는 함수를 전달해주면 됨

      두 요소를 비교해서 작은걸 앞에 놓는 로직임. 두수를 계속 비교해서 양수라면(a > b) 자리가 안바뀌고 음수라면 자리가 바뀌는 로직.
    • 위의 함수가 돌아가는 로직을 이해하기 어려운 편이라 함수를 만들어서 사용하기 보단, 유용한 기능을 모아놓은 Lodash 같은 라이브러리를 사용함
      실무에서 정말 많이 사용하니 꼭 공식 사이트가서 학습!
      https://lodash.com/
  • arr.reduce

    • 배열의 각 요소를 모두 더하고 싶다면 보통 for , for of , forEach 를 사용함
    • 위를 한번에 쓸 수 있는게 reduce

      reduce 는 함수를 갖는데 현재까지 누적된 계산값과 현재값을 가짐. 초기값은 옵션이고 안쓰면 첫번째 요소부터 시작.
    • 좀더 실용적인 예제 : 성인만 골라 담기
    • arr.reduceRight()reduce 와 기능은 동일하나 배열 오른쪽부터 실행하는 차이점만 있다

      지금까지 배운 메소드를 상황에 맞게 잘 사용하는 것도 실력임
      연습을 많이 해보자!

📖 구조 분해 할당(Destructuring assignment)

  • 구조 분해 할당 구문은 배열이나 객체의 속성을 분해해서 그 값을 변수에 담을 수 있게 하는 표현식

    ✏️ 배열 구조 분해

    let [x, y] = [1, 2];
    console.log(x); // 1
    console.log(y); // 2
    • split 을 응용 할 수 있음
    • 만약 해당하는 값이 없다면 undefined 들어감
      미리 기본값을 주면 미연에 방지 가능
      let [a, b, c] = [1, 2]; // c = undefined
      let [a=3, b=4, c=5] = [1, 2];
      console.log(a); // 1
      console.log(b); // 2
      console.log(c); // 5
    • 공백과 쉼표를 통해 일부 반환값 무시 가능
      let [user1, , user2] = ['Mike', 'Tom', 'Jane', 'Tony'];
      console.log(user1); // 'Mike'
      console.log(user2); // 'Jane'
    • 바꿔치기
      • 일반적으로 바꿔치기시 새로운 변수 필요
      • 구조 분해 할당 사용시 매우 편해짐

✏️ 객체 구조분해

  • 배열 구조분해와 거의 비슷하나 순서를 바꿔도 동일하게 동작하는 차이점이 있음
  • 프로퍼티의 키값을 무조건 사용해야 하는게 아님. 다른 변수 이름도 바꿀수 있음
  • 배열 구조분해와 마찬가지로 기본값 설정 가능
    할당 될 값이 없으면 undefined

📖 나머지 배개변수, 전개 구문 (Rest parameters, Spread syntax)

  • 인수 전달
    • 자바스크립트에서 함수에 넘겨지는 인수의 개수는 제약이 없어서 6번줄이 에러가 뜨는 것은 아님
    • 인수의 개수를 정해두고 함수를 만들어도 실제 호출시에 그 개수를 정확히 맞추어 함수를 호출할 필요는 없음
    • 아무것도 전달하지 않아도 에러는 안생기고 undefined 가 찍힐 뿐임
    • 함수의 인수를 얻는 방법은 두가지임
      1. arguments : 화살표 함수에는 존재하지 않음. 과거에 많이 씀
      2. 나머지 매개 변수 : 현재 많이 씀

✏️ arguments 란?

  • 함수로 넘어온 모든 인수에 접근 가능
  • 함수 내에서 이용 가능한 지역 변수임
  • lengthindex 가 있어 배열이라고 착각하기 쉽지만 Array 형태의 객체임
    • Array 형태의 객체들은 lengthindex 등의 속성은 갖고있지만, 배열의 내장 메소드는 가지고 있지 않음!(forEachmap 등 사용 불가)

✏️ 나머지 매개변수(Rest parameters)란?

  • ES6을 사용할 수 있는 환경이면 가급적 나머지 매개변수 사용을 권장함
  • 나머지 매개변수는 정해지지 않은 개수의 인수를 배열로 나타낼수있게함
    1. 점 세개 ... 를 찍고 뒤에 배열 이름을 나타내줌(위에선 names)
    2. 그러면 names 배열 안에 전달된 인수들이 들어감
    3. 아무것도 전달하지 않으면 undefined 가 아니라 빈배열이 나타남
  • 조금 더 실용적인 예제 : 전달 받은 수를 모두 더해야하는 함수(매번 전달받는 수가 다름)
  • 나머지 매개변수는 arguments 랑 다르게 배열이므로 메소드도 사용 가능
  • 조금 더 실용적인 예제 : user 객체를 만들어 주는 생성자 함수
    • 스킬은 사람마다 개수가 다르니 나머지 매개변수로 받음
    • 주의할 점은 나머지 매개변수는 항상 마지막에 위치해야함!

✏️ 전개 구문(Spread syntax) : 배열

let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];

let result = [...arr1, ...arr2];

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

let result2 = [0, ...arr1, ...arr2 7, 8, 9];
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 원래 배열에 넣고( arr.push() ), 중간에 빼고( arr.splice() ), 병합( arr.concat() ) 하는 작업들은 굉장히 번거로움. 그러나 전개 구문을 사용하면 쉬워짐
  • 객체에도 사용 가능
let user = {name: 'Mike'};
let mike = {...user, age:30};

console.log(mike) // {name: 'Mike', age: 30}
  • 또한 assign 을 쓰지 않아도 복사가 잘됨
let arr = [1, 2, 3];
let arr2 = [...arr]; // [1, 2, 3]

let user = {name;'Mike', age:30};
let user2 = {...user};

user2.name = 'Tom';

console.log(user.name); // 'Mike'
console.log(user2.name); // 'Tom'
  • 배열 예제
    • forEach 사용
      • 이때 주의할 점은 9번에서 리버스를 해줘야함. 안그러면 [6, 5, 4, 1, 2, 3] 이 나옴
    • 전개구문 사용
  • 객체 예제
    • assign , forEach 사용
    • 전개구문 사용

📖 클로저(Closure)

https://youtu.be/tpl2oXQkGZs

  • 함수의 Lexical 환경에는 넘겨받은 매개변수와 지역변수들이 저장됨

✏️ 클로저란?

  • 함수와 렉시컬 환경의 조합. 함수가 생성될 당시의 외부 변수를 기억하고 생성 이후에도 계속 접근이 가능한 기능을 말함. 외부함수의 실행이 끝나서 외부함수가 소멸 된 이후에도 내부함수가 외부함수의 변수에 접근할 수 있음.
    • 예제
      • 8번 줄에서 add3 함수가 생성된 이후에도 상위함수인 makeAdder 의 x에 접근이 가능함
      • 10번줄에서 makeAdder(10)이 호출 됐지만 12번줄의 add(3)에는 영향이 없음. 서로 다른 환경이기 때문
    • 예제 2
      • 11, 12, 13 번 줄의 결과를 원하는 대로 바꿀수 있을까? 불가능하다. 오직 카운터를 증가시키고 반환받기만 함. num 의 은닉화에 성공한 것. 갑자기 99로 바꾼다거나 100씩 증가하거나 하는건 불가능함

📖 setTimeout / setInterval

✏️ setTimeout

  • 일정 시간이 지난 후 함수를 실행
function fn() {
  console.log(3);
}
setTimeout(fn, 3000);

// 아래와 동일

setTimeout(function () {
  console.log(3)
}, 3000);
  • 위 코드는 3초뒤에 로그를 찍어줌
  • setTimeout 은 두개의 매개변수를 가짐. 첫번째는 일정시간이 지난뒤 실행되는 함수이고, 두번째는 시간임. 시간은 ms 단위이다. 인수가 필요하다면 시간 뒤에 적어주면 된다.
function showName(name) {
  console.log(name);
}
setTimeout(showName, 3000, 'Mike');
  • clearTimeout(tId) 는 예정된 작업을 없앰. setTimeout 은 타임id를 반환하는데 이것을 이용해 취소할 수 있다.
const tId = function showName(name) {
  console.log(name);
}
setTimeout(showName, 3000, 'Mike');

clearTimeout(tId);
  • 3초가 지나기 전에 클리어타임아웃이 실행되므로 아무일도 일어나지 않음

✏️ setInterval

  • 일정 시간 간격으로 함수를 반복
  • setTimeout 와 사용법 동일
  • 중간에 중단하려면 clearInterval(tId); 실행

🚨 주의사항

  • 딜레이 타임을 0으로 줘도 바로 실행되진 않는다. 아래 코드는 1이 먼저 찍히고 2가 나중에 찍힘. 이유는 현재 실행중인 스크립트가 종료된 이후 스케쥴링 함수를 실행하기 때문임. 그리고 브라우저는 기본적으로 4ms 정도의 대기시간이 있다
setTimeout(function(){
  console.log(2)
}, 0);
console.log(1);

📝 예제

  • 유저가 접속하면 접속한지 얼마나 지났는지 보여줌
let num = 0;

function showTime() {
  console.log(`안녕하세요. 접속하신지 ${num++}초가 지났습니다.`);
}

setInterval(showTime, 1000);

만약 계속 보여주기 싫고 5초까지만 보여주고싶다면?

let num = 0;

function showTime() {
  console.log(`안녕하세요. 접속하신지 ${num++}초가 지났습니다.`);
  if (num > 5) {
    clearInterval(tId);
  }
}

const tId = setInterval(showTime, 1000);

📖 call, apply, bind

  • 자바스크립트에선 함수 호출 방식과 관계없이 this 를 지정할 수 있음

✏️ call

  • call 메서드는 모든 함수에서 사용할 수 있으며, this를 특정값으로 지정할 수 있다.
  • call을 사용후 this로 사용할 객체를 넘기면, 해당 함수가 주어진 객체의 메소드인 것처럼 사용할 수 있다.
  • call의 첫 매개변수는 this로 사용할 값이고, 매개변수가 더있으면 그 매개변수를 호출하는 함수로 전달됨.

📝 call 예제

  • 예제 1
  • 예제 2

✏️ apply

  • apply 는 함수 매개변수를 처리하는 방법을 제외하면 call 과 완전히 같음. call 은 일반적인 함수와 마찬가지로 매개변수를 직접 받지만, apply 는 매개변수를 배열로 받음.
  • apply 는 배열 요소를 함수 매개변수로 사용할 때 유용
  • apply 는 두번째 매개변수로 배열을 전달하면 그 요소들을 차례대로 인수로 사용함
  • callapply 가 헷갈린다면 applyarray 로 받는다고 기억

📝 apply 예제

  • 예제 1
  • 예제 2
    • Math 안에는 숫자만 넣어야하는데 배열을 넣게 되면 NaN 이 나옴 제일 빠른 방법은 전개구문을 사용하는 것이고, call 도 사용 가능
    • Maththis 가 필요하지 않아 아무 값이나 넣은것
    • 만약 call 을 쓴다면 차례대로 매개변수가 들어가야하니 스프레드 연산자를 쓰면 됨

✏️ bind

  • 함수의 this 값을 영구히 바꿀 수 있다

📝 bind 예제

  • 예제 1

    • 10번줄의 upadateMike 함수는 this를 mike로 고정해 놓은 함수
  • 실전 사용 예제

📖 상속, prototype

📝 예제 1

  • 객체에는 자신이 프로퍼티를 가지고 있는지 확인하는 hasOwnProperty 라는 메소드가 있음. 그런데 이 hasOwnProperty 는 만든적이 없는데 도대체 어디있는 걸까?
    • 바로 프로토타입이라는 곳 안에 들어있다 __proto__ 라고 표현되어있기도 함. 일단 객체가 프로퍼티를 읽는데 없다면 이곳에서 찾음
    • 만약 객체 안에 hasOwnProperty 라는 프로퍼티가 있다면 어떻게 될까?
      • 일단은 객체 안에서 먼저 탐색을 하므로 그 프로퍼티가 먼저 나오게 된다. 없을때만 프로토타입에서 찾는 것!

📝 예제 2

  • 프로토타입이 어떻게 동작하는지 보기 위해 상속이라는 개념을 이용하려 함
    • 차들이 계속 늘어나면 변수를 그때그때 하나씩 만들어야하는데, 공통된 부분은 어떻게 처리할 수 있을까? 이때 프로토타입과 상속을 이용하면 됨
      • 21번줄에서 car가 bmw의 프로토타입이 되게 해줌. 즉 bmw가 car의 상속을 받는 것
      • 그리고 bmw.wheels 를 치면 우선 bmw내부에서 찾아본후 프로토 타입에 들어가서 상속받은 wheels를 찾아옴.
        • 또한 상속은 위처럼 계속 이어질 수 있다. 이런 개념을 프로토 타입 체인(Prototype Chain)이라고 함!

          for in 을 이용해 객체의 프로퍼티들을 순회하면 프로토타입에 있는것들까지 다 순회를 함

          이렇게 key나 값과 관련된 객체 내장 메소드는 상속된 프로퍼티는 안나옴
          만약에 for in 문에서 구분하고 싶다면 hasOwnProperty 를 사용하면 됨

          hasOwnProperty 는 객체가 직접 가지고있는 프로퍼티만 트루를 반환해줌

📝 예제 3

  • 생성자 함수 이용하기. 생성자 함수를 이용하면 비슷한 객체들을 간단하게 만들 수 있음

    이중 중복되는 부분은 분리

    1~6번줄 + 21~22번줄의 내용을 11~12번 줄처럼 한번에 처리할 수도 있음
    생성자 함수가 생성하는 객체에 프로토타입을 바로 준다는 뜻임
    이렇게 해놓으면 생성자로 만들어진 모든 객체에 __proto__ 를 일일히 작업해줄 필요가 없어짐

✏️ instance

  • 생성자함수가 새로운 객체를 만들어 낼때 그 객체는 생성자의 instance 라고 불려짐. 자바스크립트에서는 이를 편리하게 확인할 수 있는 instanceof 연산자가 있다
  • instanceof 를 이용해서 객체와 생성자를 비교할 수 있고, 이는 해당 객체가 그 생성자로부터 생성된것 인지를 판단해서 true 혹은 false 를 반환함
  • 생성자로 만들어진 instance 객체는 constructor 라는 프로퍼티가 존재함. constructor 는 생성자를 가르킴

📝 예제 1


위 12~20번째 줄을 더욱 깔끔하게 해보면

그런데 이렇게하면 constructor 이 사라짐

이러한 이유로 22~31 처럼 프로토타입을 덮어 씌우지 말고, 원래 12~20번 줄처럼 하나씩 프로토타입을 추가하는게 좋은 것
아니면

이런식으로 constructor 를 수동으로 명시해주면 좋다

위와 같이 자바스크립트는 언제나 정확한 constructor를 제시해 주지 않으므로 언제든지 수정될 수 있다는 점을 염두 해 두자

📝 예제 2


위와같이 자동차의 색상을 맘대로 변경할 수 있게되면 문제가 생길 수 있다. 방지하려면 클로저를 이용하면 됨

이렇게 하면 초기에 셋팅했던 칼라값을 얻을수만 있고 바꿀 수는 없다
getColor 함수는 생성될 당시의 칼라값을 기억하는 것

📖 클래스(Class)

  • ES6 에 추가된 스펙
  • 비슷한 형태의 객체를 생성하기 위해 생성자 함수를 사용했는데 클래스로도 구현 가능

    constructor 은 객체를 만들어 주는 생성자 메소드이다
    new 를 통해 호출하면 자동으로 실행됨
    그럼 constructor을 통해 객체에 name 과 age 가 만들어 짐
    showName 처럼 class 내에 정의한 메소드는 User2 의 프로토타입에 저장됨
    마이크는 객체 내부에 showName이 있고, 톰은 프로토타입에 showName 이 있다

    둘다 사용법은 동일

    생성자 함수에서 클래스를 쓴것처럼 동일하게 작동하게 하려면

    그럼 둘의 차이 단순히 문법의 편의성일까??
    생성자 함수는 new 없이 호출하면 undefined 만 뜰뿐 에러가 뜨진 않음
    class는 new 없이 호출하면 에러가 뜸

    prototype 내부에 있는 constructor 을 보면 user2 의 constructor 은 class 라고 명시가 되어있음. 이렇게 constructor 가 클래스라는걸 알수있고 이경우 new 없이 호출하면 에러가 발생되게 설계되어있음

    for in 문은 프로토타입에 포함된 프로퍼티들을 다보여주고 객체가 가지고있는 프로퍼티만 판별하기 위해선 hasOwnProperty 를 사용했어야 함
    class 의 메소드는 for in 문에서 제외됨

✏️ 상속

  • class 에서 상속은 extends 키워드 사용

✏️ 메소드 오버라이딩(method overriding)

  • 만약 위의 예시에서 car로 가기전에 bmw 에 동일한 메소드명이 있다면 어떻게 될까?

    덮의 씌우게 됨
  • 그렇다면 부모의 메소드를 그대로 쓰면서 확장하고 싶다면 어떻게 하면 될까? 그럴땐 super 라는 키워드를 사용하면 된다

    이렇게 super.메소드명 으로 부모클래스의 메소드를 사용 가능. 이런 방식을 오버라이딩 이라고 함

✏️ 생성자 오버라이딩


constructor 에서 this 를 사용하기전에 super constructor 즉, 부모 생성자를 먼저 호출해야 한다고 나옴
위에서 공부한 것처럼 클래스의 constructor 은 빈 객체를 만들어 주고, this 로 그 객체를 가르키게 됨
반면 extends 를 이용해서 만든 자식 클래스는 빈 객체를 만들어 주고 this 에 할당해 주는 작업을 건너 뜀. 그래서 항상 super() 키워드로 부모 클래스의 constructor 을 실행시켜줘야함.

그러나 navigation 은 잘 들어갔는데, color 값이 제대로 안들어감.
이를 위해선 자식클래스의 컨스트럭터에 동일한 인수를 받는 작업을 해줘야함
15번줄에서 color 를 받고 16번줄에서 부모에게 넘겨줌

  • 근데 위에서 상속에서의 사진을 보면 이렇게 안해줬어도 잘 나왔는데 왜그랬던걸까?

    위 사진은 맨 처음 코드임. 자식 class인 bmw 에는 constructor 가 없음
    이럴때 자바스크립트는

    위와 같이 동작함. constructor가 없으면 그부분이 있는것처럼 행동해서 자식생성자는 무조건 부모 생성자를 호출함

📖 프로미스(Promise)

  • 음식을 주문한다 생각해보자, 주문하면 언제 나올지 모르고 만들다 실패할수도 있다. 이럴때 계속 앞에 서있으면 다됐냐고 물어보는건 매우 비효율적이다. 그냥 전화번호를 주고 다른 볼일을 보고있다가 연락을 받는게 좋다. 이럴때 사용할 수 있는게 바로 프로미스다.

✏️ 프로미스 기본 문법

const pr = new Promise((resolve, reject) => {
  // code
});
  • 위와 같이 사용함
  • new Promise 로 생성함
  • 함수를 전달받는데 인수는 resolve 와 reject 다. resolve 는 성공한 경우 실행되는 함수, reject 는 실패했을때 실행되는 함수다. 이런식으로 어떤 일이 완료 된후 실행되는 함수를 callback 함수라 함
  • new Promise 객체가 생성하는 Promise 객체는 state 와 result 를 프로퍼티로 갖음.
    • state 는 초기에 pending(대기) 였다가, resolve(value) 가 호출되면, 즉 성공하면 fulfilled(이행됨) 이 됨. 이때 result 는 value 로 resolve 함수를 통해 전달 된 값임
    • reject(error) 가 호출되면, 즉 실패하면 state 는 rejected(거부됨) 이 됨. 이때 result 는 reject 함수로 전달된 error 이다

📝 프로미스 예시

  • 판매자의 코드 : 주문을 받으면 3초동안 뭔가를 한후 성공인지 실패인지 알려줌

    • 이 코드는 pending 였다가 3초후에 fulfilled가 됨. result 는 undefined 였다가 'OK' 가 됨

      실패를 가정한 코드는 위의 경우. peding 이였다가 3초후에 rejected 로 바뀜. result 는 undefined 였다가 3초후에 error
  • 소비자의 코드
    then 을 이용해서 리젝트와 리졸브를 처리할 수 있다

  • then 이외에 사용할 수 있는것이 catchfinally 이다

    • catch 는 에러가 발생한 경우. 즉 리젝트인 경우에만 실행

      위와 같이 then으로만 표현한걸 then과 catch 로 표현할 수 있다. 동일하게 동작하나 catch로 명확하게 구분해주는게 더 가독성에도 좋고, 첫번째 함수를 실행했다가 나는 에러도 잡아줄 수 있기때문에 catch문을 사용하는 것이 훨씬 좋다.
    • finally 는 이행이든 거부든 처리가 완료되면 항상 실행

      로딩 화면 같은걸 없앨 때 유용

📝 프로미스 실전 예제 1


📝 프로미스 실전 예제 2


위처럼 뎁스가 깊어지며 계속 콜백을 호출하는 것을 콜백헬, 콜백지옥이라함
콜백함수를 사용하지 않고 프로미스로도 가능

위와같이 프로미스가 계속 연결되는것을 프로미스 체이닝(Promises chaining)

2번을 실패로 돌리니 3번은 실행조차 안됨

시간을 측정해보니 6초정도 걸림. 만약 세 사람이 각 상점으로 가서 동시에 주문하면 제일 오래걸리는 3초면 모든 제품을 받을 수 있을 것임. 이때 쓰이는게 Promise.all

📝 Promise.all 예제


위 처럼 Promise.all 은 한꺼번에 실행하고 모두 이행되면 값을 사용할 수 있다. 시간도 절약 가능

1번을 리젝트로 바꿔주니 어떠한 데이터도 얻지 못하고 에러가 남. 이런건 하나의 정보라도 누락되면 페이지를 보여주면 안되는 경우에 활용 가능. 다 보여주거나 아예 안보여주거나 할때

📝 Promise.race 예제

  • all은 모든 작업이 완료될 때 까지 기다리지만, race는 말그대로 경주라 하나라도 일등으로 완료되면 끝남

    2번이 리젝트를 예정하고 있었는데, 1번 주문이 완료가되어 무시됨
    용량이 큰 이미지들을 로딩하는데 그 중에 하나라도 로딩이 되면 그 이미지들을 보여줄 때 이런 방식을 사용
profile
크론병걸린 자퇴생, 개발자되기

0개의 댓글