경일게임아카데미 멀티 디바이스 메타버스 플랫폼 개발자 양성과정 20220908 개인 공부 2022/04/04~2022/12/14

Jinho Lee·2022년 9월 9일
0

2022.09.08 개인 공부 - JavaScript

  • 자료 : 코딩앙마 자바스크립트 중급 강좌 : 140분 완성

  • 기존에 배운 C 계열 언어 (C, C++, C#) 및 Node.js 수업에서 배운 내용과 다르거나 다뤄지지 않은 부분만, 본인의 공부를 위하여 기재한다.

  • Node.js나 서버에 활용하리라 생각되는 부분을 우선하여 공부한다.

변수, 호이스팅 (Hoisting)

let, const와 var의 차이

  • var는 한번 선언된 변수를 다시 선언할 수 있다.

  • var는 선언하기 전에 사용할 수 있다 - 호이스팅 (Hoisting)

    • 호이스팅 :
      스코프 내부 어디서든 변수 선언이 최상위에 선언된 것처럼 행동

    • 선언은 호이스팅 되지만, 할당은 호이스팅 되지 않는다.

Temporal Dead Zone (TDZ)

  • let과 const에서 호이스팅 문제가 발생하지 않는 이유

  • Temporal Dead Zone (TDZ) :
    let과 const 선언의 스코프 내부 상위 영역

  • TDZ 영역에 있는 변수는 사용할 수 없다

    • 코드를 예측 가능하게 하고 잠재적인 버그를 줄일 수 있다
      호이스팅으로 인한 버그를 줄인다

    • 호이스팅은 스코프 단위로 일어난다.

  • 변수의 생성 과정

    1. 선언 단계

    2. 초기화 단계

    3. 할당 단계

    • var선언과 초기화가 동시에 일어난다
      → 초기화에서 undefined를 할당

    • let은 선언과 초기화가 분리되어 진행된다

    • const선언, 초기화, 할당이 모두 동시에 일어난다

  • var :
    함수 스코프 (function-scoped)

  • let, const :
    블록 스코프 (block-scoped)

  • 변수 비교 표

    varletconst
    생성선언, 초기화선언선언, 초기화, 할당
    값 변경 가능OOX
    스코프 범위함수 스코프블록 스코프블록 스코프
    TDZ 영향OXX
    호이스팅 발생OXX

생성자 함수

  • 객체 리터럴과 같이 객체를 만드는 또다른 방법

  • 생성할 때 함수명 앞에 new를 붙여 사용

  • 첫 글자를 대문자로 하는 것이 관례

  • 매개변수를 받아 객체를 생성

function User(name, age){
  this.name = name;
	this.age = age;
}
let user = new User("Mike", 33);
  • 메소드 추가 가능
function User(name, age){
	this.name = name;
	this.age = age;
	this.sayName = function(){
		console.log(this.name);
	}
}
let user2 = new User("Han", 40);
user2.sayname(); // Han

객체 메소드 (Object Methods), 계산된 프로퍼티 (Computed Property)

계산된 프로퍼티 (Computed Property)

  • 아래 예시에서와 같이 프로퍼티 이름 부분변수를 대괄호로 묶어 넣어주면 변수에 할당된 값을 이름으로 사용할 수 있다.

  • 예시

// 일반적인 객체의 선언
let a = 'age';

const user = {
	name : 'Mike',
	age : 30
}

// 계산된 프로퍼티 사용
let a = 'age';

const user = {
	name : 'Mike',
	[a] : 30
}
  • 식을 넣는 것도 가능하다.
const user = {
	[1 + 4] : 5,
	["안녕" + "하세요"] : "Hello"
}

// 콘솔
user
> {5: 5, 안녕하세요: "Hello"}

객체에서 사용할 수 있는 메소드

1. Object.assign()

  • 객체 복제

    • 클론_객체 = 객체 는 객체의 참조 값(주소 값)만 복제된다.
      ⇒ 하나의 객체를 두 변수가 접근

    • 동일하게 복제하려면 assign 사용

  • 매개변수로 들어가는 첫 번째 인수의 객체에 두 번째 인수병합된다.

const user = {
	name : 'Mike',
	age : 30
}

const newUser = Object.assign({}, user);
// 왼쪽의 빈 객체에 오른쪽 객체의 내부 요소가 병합되어 복제
// {} + { name : 'Mike', age : 30 }

Object.assign({ gender:'male' }, user);
// gender 프로퍼티에 user 내부 요소가 복제되어 병합(추가)
// { gender : 'male', name : 'Mike', age : 30 }

Object.assign({ name : 'Tom' }, user);
// 같은 프로퍼티에 값이 있으면 덮어쓴다.
// { name : 'Mike', age : 30 }
  1. 첫 번째 빈 객체에 두 번째 객체의 내부 요소를 병합한다. (복제한다)

  2. 다른 프로퍼티가 있다면 두 번째 객체의 내부 요소를
    추가하여 병합한다.

  3. 같은 프로퍼티에 다른 값이 있다면 두 번째 객체의 것으로
    덮어쓰고 병합한다.

  • 2개 이상의 객체를 한 번에 병합할 수 있다.
const user = {
	name : 'Mike'
}
const info1 = {
	age : 30,
}
const info2 = {
	gender : 'male',
}

Object.assign(user, info1, info2);
/*
user = {
	name : 'Mike',
	age : 30,
	gender : 'male',
}
*/

2. Object.keys()

  • 객체 프로퍼티 키를 배열로 반환
const user = {
	name : 'Mike',
	age : 30,
	gender : 'male',
}
Object.keys(user);
// ["name", "age", "gender"]

3. Object.values()

  • 프로퍼티 값을 배열로 반환
const user = {
	name : 'Mike',
	age : 30,
	gender : 'male',
}
Object.values(user);
// ["Mike", 30, "male"]

4. Object.entries()

  • 프로퍼티 키와 값을 쌍으로 묶어 배열로 반환

  • 키/값 쌍 배열을 요소로 갖는 배열 반환

const user = {
	name : 'Mike',
	age : 30,
	gender : 'male',
}
Object.entries(user);
/*
[
	["name", "Mike"],
	["age", 30],
	["gender", "male"]
] 
*/

5. Object.fromEntries()

  • 키와 값으로 묶인 배열을 요소로 갖는 배열을 객체로 변환

  • 배열 쌍에서 앞을 키로 본다.

const arr = 
[
	["name", "Mike"],
	["age", 30],
	["gender", "male"]
];
Object.fromEntries(arr);
/*
{
	name : 'Mike',
	age : 30,
	gender : 'male',
}
*/

심볼 (Symbol)

  • 일반적인 프로퍼티 키 : 문자형

    • 심볼형은 프로퍼티 키로 사용할 수 있다.
  • 심볼의 생성
    const a = Symbol();
    new를 붙이지 않는다.

    • 매개변수로 문자형을 넘겨 설명도 넣을 수 있다.

      const id = Symbol('id');

    • 설명은 심볼의 description 프로퍼티로 접근 가능

      const id = Symbol('id 입니다.');
      
      id.description; // "id 입니다."
  • 심볼의 특성 - 유일성 보장

    • 유일한 식별자를 만들 때 사용한다.

    • 각 심볼은 전체 프로그램에서 딱 하나

    • 심볼형 변수끼리는 일치 연산자 (===), 동등 연산자 (==)로 비교하면 false가 나온다.

  • 프로퍼티 키 : 심볼형

const id = Symbol('id');
const user = {
	name : 'Mike',
	age : 30,
	[id] : 'myid'
}
// 콘솔
> user
>> {name: "Mike", age: 30, Symbol(id): "myid"}
> user[id]
>> "myid"
  • 이 때, 심볼형 프로퍼티숨겨져 있다.

    • 객체 메소드를 사용해도 반환 되지 않는다.
      ( Object.keys(), values(), entries() 등 )

      Object.keys(user);    // ["name", "age"]
      Object.values(user);  // ["Mike", 30]
      Object.entries(user); // [Array(2), Array(2)]
    • for … in문으로 순회해도 나오지 않는다.
      for(let a in user){ }

    • 이로써 특정 데이터의 원본은 수정하지 않으면서 프로퍼티(속성)을 추가할 수 있다.

  • 전역 심볼 : Symbol.for()

    • 하나의 심볼만 보장받을 수 있다.

      • 없으면 만들고, 있으면 가져온다.
    • Symbol 함수와 달리 Symbol.for 메소드는 하나를 생성하고
      키를 통해 같은 Symbol을 공유한다.

      const id1 = Symbol.for('id');
      const id2 = Symbol.for('id');
      
      id1 === id2; // true
      
      Symbol.keyFor(id1) // "id"
    • 전역 심볼의 키를 얻는 메소드 → Symbol.keyFor()

  • 숨겨진 Symbol key 보는 법

    • Object.getOwnPropertySymbols(객체명)
      심볼 키 배열 반환

    • Reflect.ownKeys(객체명)
      심볼 키를 포함한 객체의 모든 키 배열 반환

  • 사용 양상 예시 코드

// 다른 개발자가 만들어 놓은 객체
const user = {
	name: "Mike",
	age: 30,
};

// 내 작업
// user.showName = function(){};
const showName = Symbol("show name");
user[showName] = function(){
	console.log(this.name);
};

user[showName]();

// 사용자가 접속하면 보는 메세지
for (let key in user) {
	console.log(`His ${key} is ${user[key]}.`);

> Mike
> His name is Mike.
> His age is 30.
> // His showName is function(){}.

Number, Math

  • toString()

    • 숫자를 문자로 바꾼다.

    • 매개변수로 받은 수의 진법으로 변환한다. 기본값 10진법

      let num = 10;
      num.toString(); // "10"
      num.toString(2); // "1010"
      let num2 = 255;
      num2.toString(16); // "ff"
  • toFixed()

    • 소수점 자릿수 표현을 지정

    • 매개변수로 받은 자릿수까지 표현 (그 아래에서 반올림)

    • 문자열로 반환한다.

      let userRate = 30.1234;
      
      userRate.toFixed(2); // "30.12"
      userRate.toFixed(0); // "30"
      userRate.toFixed(6); // "30.123400"
  • isNaN()

    • NaN인지 여부를 검사

    • 유일하게 NaN인지 검사할 수 있는 방법이다.
      - NaN은 자기 자신과도 똑같지 않다고 판단된다.

      let x = Number('x'); // NaN
      
      x == NaN // false
      x === NaN // false
      NaN == NaN // false
      
      isNaN(x) // true
      isNaN(3) // false
  • parseInt()

    • 문자열을 숫자로 바꾼다

    • Number()와 달리 문자가 혼용되어 있어도 동작한다.

      • 문자를 만날 때까지 읽고, 숫자를 반환한다.
      let margin = '10px';
      
      parseInt(margin); // 10
      Number(margin); // NaN
      
      let redColor = 'f3';
      parseInt(redColor); // NaN
    • 두 번째 인수를 받아 진법을 바꿀 수 있다.

      let redColor = 'f3';
      
      parseInt(redColor); // NaN
      parseInt(redColor, 16); // 243
      
      parseInt('11', 2); // 3
  • parseFloat()

    • 문자열을 부동소수점 수로 바꾼다.

    • parseInt()와 같은 방식으로 동작한다.

      let padding = '18.5%';
      parseInt(padding); // 18
      parseFloat(padding); // 18.5

Math

  • Math.PI

    • 3.141592653589793
  • Math.ceil()

    • 올림
  • Math.floor()

    • 내림
  • Math.round()

    • 반올림
  • Math.random()

    • 0 ~ 1 사이 무작위 숫자 생성
  • Math.max()

    • 인수들 중 최댓값 반환
  • Math.min()

    • 인수들 중 최솟값 반환
  • Math.abs()

    • 절대값
  • Math.pow(n, m)

    • 거듭 제곱

    • n은 밑, m은 지수

  • Math.sqrt()

    • 제곱근

Array

  • arr.splice

    • 요소를 삭제하고 반환

    • arr.splice(n, m)

      • 특정 요소 삭제

      • n : 시작 인덱스
        m : 지우는 개수

        let arr = [1, 2, 3, 4, 5];
         let result = arr.splice(1, 2);
        
         console.log(arr); // [1, 4, 5];
         console.log(result); // [2, 3];
    • arr.splice(n, m, x, ...)

      • 특정 요소 지우고 추가

      • x 이후는 추가하는 요소

        let arr = [1, 2, 3, 4, 5];
        arr.splice(1, 3, 100, 200);
        
        console.log(arr); // [1, 100, 200, 5];
        console.log(result); // [2, 3, 4];
  • arr.slice(n, m)

    • 인덱스 n부터 m까지 반환 (m은 제외)
  • arr.concat(arr2, arr3, …)

    • 인수의 배열을 합쳐서 새 배열 반환
  • arr.forEach(fn)

    • 배열의 각 요소에 대하여 함수 반복

    • 함수를 인수로 받는데, 그 함수의 매개변수는 각 요소, 인덱스, 배열이다.

  • arr.indexOf(n)

    • 처음부터 검사하여 인수의 요소를 발견하면 인덱스 반환

    • arr.indexOf(n, m)

      • 인덱스 n부터 검사하여 m을 찾으면 인덱스 반환
  • arr.lastIndexOf(n)

    • 뒤에서부터 검사하여 인수의 요소를 발견하면 인덱스 반환
  • arr.includes(n)

    • 인수의 요소 포함 여부 확인
  • arr.find(fn)

    • 함수를 인수로 받아, 함수의 조건을 만족하는 요소 포함 여부 확인

    • 첫 번째 요소를 찾으면 true, 못 찾으면 undefined 반환

  • arr.findIndex(fn)

    • 함수를 인수로 받아, 함수의 조건을 만족하는 첫 번째 요소의 인덱스 반환

    • 첫 번째 요소의 해당 인덱스, 못 찾으면 -1 반환

  • arr.filter(fn)

    • 함수를 인수로 받아, 함수의 조건을 만족하는 모든 요소를 배열로 반환
  • arr.reverse()

    • 배열을 역순으로 정렬
  • arr.map(fn)

    • 함수를 인수로 받아 함수 기능을 시행하고 새로운 배열을 반환
  • arr.sort()

    • 배열 재정렬

    • 배열 자체가 변경되므로 주의해야 한다.

    • 함수를 인수로 받아 정렬 로직을 정할 수 있다.
      기본값 오름차순

      let arr = [27, 8, 5, 13];
      
      arr.sort(); // arr = [5, 8, 13, 27];
      
      arr.sort((a, b) => {
      	return b - a; // 양수면 a, b를 뒤바꾼다
      }); // arr = [27, 13, 8, 5];
    • Lodash라는 라이브러리는 sort에 유용한 로직을 정리해놓았다.

구조 분해 할당

(Destructuring Assignment)

  • 배열이나 객체의 속성을 분해하여 그 값을 변수에 할당할 수 있게하는 표현식

나머지 매개변수, 전개 구문

(Rest Parameters, Spread Syntax)

  • 로 사용

  • 함수에 매개변수를 전달하는 방법

    1. arguments

    2. 나머지 매개변수

arguments

  • 함수로 넘어 온 모든 인수에 접근

    • 인수의 배열처럼 사용 가능
  • 함수 내에서 이용 가능한 지역 변수

  • length / index

  • Array(배열) 형태의 객체

  • 배열 내장 메소드 없음

나머지 매개변수 (Rest Parameters)

  • 뒤에 배열 이름

  • 전달 받은 모든 인수를 요소로 배열로 나타남

function add(...numbers) {
	let result = 0;
	numbers.forEach((num) => (result += num));
	console.log(result);
}

add(1, 2, 3); // 6
add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // 55
function User(name, age, ...skills) {
	this.name = name;
	this.age = age;
	this.skills = skills;
}

const user1 = new User("Mike", 30, "html", "css");
const user2 = new User("Tom", 20, "JS", "React");
const user3 = new User("Jane", 10, "English");

console.log(user1.skills); // ["html", "css"]
console.log(user2.skills); // ["JS", "React"]
console.log(user3.skills); // ["English"]
  • 주의 : 나머지 매개변수는 항상 제일 마지막에 위치

전개 구문 (Spread Syntax)

  • 배열을 요소로 넣어 풀어 쓸 수 있다.

  • …배열명

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

let result = [0, ...arr1, ...arr2, 7, 8, 9];
	// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 배열의 병합을 간편히 구현

  • 객체도 가능

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

console.log(mike); // {name: "Mike", age: 30}
  • 이를 이용해 복제도 가능하다.

클로저 (Closure)

  • 자바스크립트는 어휘적 환경 (Lexical Environment)을 갖는다.

  • 클로저 (Closure)

    • 함수와 어휘적 환경(렉시컬 환경)의 조합

    • 함수가 생성될 당시의 외부 변수를 기억

    • 외부 변수를 참조

    • 생성 이후에도 계속 접근 가능

  • 어휘적 환경 (렉시컬 환경, Lexical Environment) (220913 추가)

    • 지역변수를 저장하는 환경
    • 스크립트 전체, 함수, 코드 블럭 등이 렉시컬 환경을 갖는다.
  • 앞으로 필요하면 더 공부

call / apply / bind

  • 함수 호출 방식과 관계없이 this를 지정할 수 있는 함수

  • call

    • 모든 함수에서 사용 가능

    • this를 특정 값으로 지정 가능

      const mike = {
      	name: "Mike",
      };
      
      const tom = {
      	name: "Tom",
      };
      
      function showThisName() {
      	console.log(this.name);
      }
      
      showThisName(); // 아무것도 안 나온다.
      showThisName.call(mike); // Mike
      // this 객체를 mike로 넘긴다.
      showThisName.call(tom); // Tom
      // this 객체를 tom으로 넘긴다.
  • apply

    • 매개변수를 제외하고 call과 같다.

    • 매개변수를 배열로 받는다.

      const mike = {
      	name: "Mike",
      };
      
      function update(birthYear, occupation) {
      	this.birthYear = birthYear;
      	this.occupation = occupation;
      }
      
      update.call(mike, 1999, "singer");
      console.log(mike); // {name: "Mike", birthYear: 1999, occupation: "singer"}
      
      update.apply(mike, [1999, "singer"]);
      console.log(mike); // {name: "Mike", birthYear: 1999, occupation: "singer"}
    • apply는 배열 요소를 함수 매개변수로 사용할 때 유용

  • bind

    • 함수의 this 값을 영구히 바꿀 수 있다.
      const user = {
      	name: "Mike",
      	showName: function () {
      		console.log(`hello, ${this.name}`);
      	},
      };
      
      user.showName(); // hello, Mike
      
      let fn = user.showName;
      
      fn(); // hello, 이후로 아무것도 표시 안 됨
      
      fn.call(user); // hello, Mike
      fn.apply(user); // hello, Mike
      
      let boundFn = fn.bind(user);
      
      boundFn(); // hello, Mike

상속, prototype

  • **__proto__**

    • 객체가 어떤 객체를 상속 받고 있는지를 나타내는 프로퍼티

    • 자식_객체.__proto__ = 부모_객체
      형식으로 사용한다.

    • 이를 이용한 상속 관계의 연속
      프로토타입 체인(Prototype Chain)이라고 한다.

    • 다만 이는 레거시 기능으로, JavaScript 엔진에서 매우 느린 작업이다. 사용이 추천되진 않는다.

프로미스 (Promise)

  • Promise객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타낸다.

  • 프로미스 사용
    const pr = new Promise((resolve, reject) ⇒ { /*code*/ });

    • **resolve**성공했을 때, **reject**실패했을 때 호출되는 콜백 함수

    • Promise 객체의 동작 방식

      1. new Promise로 생성된 프로미스 객체는
        stateresult를 프로퍼티(속성)로 갖는다.

      2. 처음에 state는 작업이 이루어지는 동안
        pending(대기) 값을 가진다.
        resultundefined

      3. 작업이 완료되고,

        3-1. 성공하면

        • **resolve(value)**가 호출되고,

        • state 값은 **fulfilled(이행됨),**

        • result 값은 value를 할당 받는다.

          3-2. 실패하면

        • **reject(error)**가 호출되고,

        • state 값은 **rejected(거부됨),**

        • result 값은 error를 할당 받는다.

      4. Promise객체가 result를 반환하면 메소드 then을 사용해 결과에 따른 동작을 정할 수 있다.

        4-1. Promise객체의 catch메소드는 error가 반환되었을 때만 실행된다.

        4-2. Promise객체의 finally메소드는 결과에 상관없이 처리가 완료되면 항상 실행된다.

async, await

  • async 키워드를 사용하면 함수가 항상 프로미스를 반환한다,

  • await 키워드는 async 함수 내부에서만 사용 가능하다.

  • await 키워드는 프로미스수식한다.

    • 프로미스가 처리될 때까지 다른 작업은 대기한다.

Generator

  • 함수의 실행을 도중에 멈췄다가 재개할 수 있는 기능

  • 생성

    • function 옆에 ⇒ `function`

    • 내부에 yield 키워드 사용

    • 예시

      function* fn() {
      	yield 1;
      	yield 2;
      	yield 3;
      	return "finish";
      }
      
      const a = fn();
  • 사용

    • next() 메소드

      • 실행하면 가장 가까운 yield 문을 만날 때까지 실행하고 데이터 객체를 반환한다.

      • 객체는 valuedone을 프로퍼티로 갖는다.

        • value: yield 수식을 받은 . default는 undefined.

        • done: Generator 함수 코드끝났는지 여부

      • 인수를 전달하면 yield 키워드로 할당할 수 있다.

    • return() 메소드

      • 호출하는 즉시 인수를 value로 할당하고 **done 속성값을 true로 바꿔 Generator 함수를 종료**한다.

      • 매개변수로 주어진 값을 반환하고 Generator 종료

        • 마지막으로 중단된 위치에서 Generator 본체return문이 삽입된 것처럼 작동한다.
    • throw() 메소드

      • 호출하는 즉시 catch를 실행하고 **done 속성값을 true로 바꿔 Generator 함수를 종료**한다.

      • Generator에 오류를 발생시키고 Generator 종료

        • 마지막으로 중단 된 위치에서 Generator 본체에 throw문이 삽입된 것처럼 작동한다.
    • 예시 코드

      function* fn() {
      	try {
      	console.log(1);
      	yield 1;
      	console.log(2);
      	yield 2;
      	console.log(3);
      	yield 3;
      	console.log(4);
      	console.log(5);
      	yield 4;
      	const num = yield "인수 전달";
      	console.log(num);
      	yield 5;
      	return "finish";
      	} catch (e) {
      		console.log(e);
      	}
      }
      
      const a = fn();
      a.next(); > 1 // {value: 1, done: false}
      a.next(); > 2 // {value: 2, done: false}
      a.next(); > 3 // {value: 3, done: false}
      a.next(); > 4 > 5 // {value: 4, done: false}
      
      a.next(); > // {value: "인수 전달", done: false}
      a.next(5); > 5 // {value: 5, done: false}
      a.next(); > // {value: "finish", done: true}
      
      a.next(); > // {value: undefined, done: true}
      
      const b = fn();
      b.next(); > 1 // {value: 1, done: false}
      b.next(); > 2 // {value: 2, done: false}
      b.next(); > 3 // {value: 3, done: false}
      b.return('END'); // // {value: "END", done: true}
      
      const c = fn();
      c.next(); > 1 // {value: 1, done: false}
      c.next(); > 2 // {value: 2, done: false}
      c.next(); > 3 // {value: 3, done: false}
      c.throw(new Error('err')); // Error: err// {value: undefined, done: true}
  • Generator는 iterable

    • 반복이 가능

      • 조건
      1. Symbol.iterator 메소드가 구현되어 있어야 한다.

      2. Symbol.iteratoriterator반환해야 한다.

    • iterator

      • valuedone 속성을 가진 객체를 반환하는
        **next 메소드**를 가진다.
    • 재밌게도, 배열 Array 또한 iterable하고 iterator이다.

  • yield*

    • 다른 Generator 함수를 또 호출할 수 있다.

      function* gen1() {
      	yield "W";
      	yield "o";
      	yield "r";
      	yield "l";
      	yield "d";
      }
      
      function* gen2() {
      	yield "Hello,";
      	yield* gen1();
      	yield "!";
      }
      
      console.log(...gen2());
      > Hello, W o r l d !
    • 반복 가능한 모든 객체를 수식하여 사용할 수 있다.

0개의 댓글