Node.js 과제

최서호·2022년 5월 19일
0

Javascript의 언어적 특성

Javascript의 자료형과 Javascript만의 특성은 무엇일까?

1. Loosely typed, dynamic(느슨한 타입의 동적) 언어

  • JavaScript의 변수는 어떤 특정 타입과 연결되지 않으며, 모든 타입의 값으로 할당 (및 재할당) 가능합니다.
  • 이러한 특성으로 인해 변수 생성 시 원시 변수의 타입을 미리 선언하지 않아도 된다는 장점이 있습니다.
  • 하지만 많은 API가 오고 가는 대형 프로젝트(혹은 협업 시)에서 타입이 올바른지 체크하는것이 굉장히 까다롭기 때문에 배포 시 예상치 못한 문제와 직면할 수 있습니다.
  • 보완 방법 : Javascript의 이러한 단점을 보완하여 정적 타입체크와 강력한 문법을 추가한 Typescript를 사용하여 보완 가능합니다.

2. JavaScript 형변환

  • 암시적 변환 : 자바스크립트 엔진이 필요에 따라 자동으로 데이터타입을 변환시키는 것이다.
  • 더하기 + 연산자는 숫자보다 문자열이 우선시 되기때문에, 숫자형이 문자형을 만나면 문자형으로 변환하여 연산된다 (문자 > 숫자). 다른 연산자는 (1,*,/,%) 숫자형이 문자형보다 우선시되기 때문에 더하기와 같은 문자형으로의 변환이 일어나지 않는다. (문자 < 숫자)
  • 명시적 변환 : 명시적변환이란 개발자가 의도를 가지고 데이터타입을 변환시키는 것이다. 타입을 변경하는 기본적인 방법은 Object(), Number(), String(), Boolean() 와 같은 함수를 이용하는데 new 연산자가 없다면 사용한 함수는 타입을 변환하는 함수로써 사용된다.

3. ==, ===(동치 비교)

  • == 동치비교는 값이 같은지만 비교한다. 타입이 같은지는 구분하지않는다.
  • === 동치비교는 좀더 엄격하게 비교한다. 타입까지 같아야 한다.
2 == '2' -> true
2 === '2' -> false

4. undefined와 null의 미세한 차이들

  • undefined는 값이 할당되지 않은 변수이다. 즉 정의되지 않은것, 초기화되어 있지 않거나 존재하지 않는 개체의 프로퍼티 및 존재하지 않는 배열의 원소값에 접근하려고 할때 얻어지는 변수의 값입니다.
  • null은 값이 비어있음을 나타내는데 사용합니다. 다시 말해서, 아무것도 참조하고 있지 않다라는 의미가 담겨 있으며 주로 객체를 담을 변수를 초기화할 때 많이 사용합니다.
let a;
let b = null;

console.log(a); -> undefined
console.log(b); -> null

JavaScript 객체와 불변성이란 ?

1. 기본형 데이터와 참조형 데이터

  • 기본형 타입 : 숫자, 문자열, 불리언, null, undefined, symbol 이 있습니다. 일반적으로 기본형은 할당이나 연산시 데이터가 복제 된다고 알려져있습니다. 기본형타입은 값을 복사해옵니다.
  • 참조형 타입 : 객체, 배열, 함수, 정규표현식등이 있으며 참조형 타입에는 값이 저장된 주소값을 참조해 옵니다.
  • 스택: 기본형 타입 변수들과 그 값들이 함께 저장된다.- 힙 영역에 저장되어 있는 참조형 타입 변수들의 reference(주소)가 저장된다.- 정적인 것들이 저장되는 곳
  • 힙: 참조형 타입의 변수들이 저장된다. (new연산자를 통해 생성된 인스턴스 변수가 저장된다.) - (cf. 인스턴스 변수의 reference는 스택에 저장)- 동적인 것들이 저장되는 곳
// 원시타입은 값이 복사되어 전달됨
let a = 1;
let b = a; //1
b = 2;
console.log(a);
console.log(b);

// 객체타입은 참조값(메모리주소, 레퍼런스)가 복사되어 전달됨
let apple = {
  // 0x1234(메모리주소)
  name: '사과',
};
let orange = apple; // 0x1234(메모리주소)
orange.name = '오렌지';
console.log(apple);
console.log(orange);

2. 불변 객체를 만드는 방법

  • 불변객체란 어떤 객체내부의 프로퍼티들을 변경할 수 없도록 되어있는 객체를 일컫습니다.
  • 불변객체는 객체 내부 프로퍼티를 변경할 때 마다 새로운 객체를 만들어 재할당 하기로 정하거나, 자동으로 새로운 객체를 만드는 도구를 활용하여 (immutable,js, immer,js 등의 라이브러리) 불변성을 확보 할 수 있다.
  • 불변 객체가 필요한 경우는 객체에 변화를 가해도 원본이 그대로 남아있어야 하는 경우이다

3. 얕은 복사와 깊은 복사

  • 얕은복사 : 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주솟값만 복사하는 방법입니다.
const obj = {value: 1}
const newObj = obj

newObj.value = 2

console.log(obj.value) // 2
console.log(obj === newObj // true
  • obj 변수에 object를 할당하고, newObj 변수에 obj 변수의 값을 할당했습니다. 그리고 newObj 프로퍼티인 value 값을 2로 설정하고, obj.value를 콘솔에 출력하면, 2로 변경된 것을 볼 수 있습니다. 왜냐면, 얕은 복사 때문에, 사본의 데이터를 변경하더라도, 동일한 참조형 데이터 주소를 가리키고 있기에, 원본의 데이터도 변경되는 것입니다.

  • 깊은복사 : 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법입니다. 기본적형 타입의 데이터는 원래 깊은 복사가 진행됩니다. 객체에서 깊은 복사는 다양한 방법으로 진행됩니다. (재귀함수를 이용한 복사, JSON.stringify(), lodash 라이브러리 사용)


호이스팅과 TDZ는 무엇일까?

1. 스코프, 호이스팅, TDZ

  • 스코프 : 변수에 접근할 수 있는 범위 입니다. 자바스크립트에선 2종류의 스코프가 있는데 전역지역 스코프가 있습니다.

--전역 스코프(Global Scope)는 말 그대로 전역에 선언되어있어 어느 곳에서든지 해당 변수에 접근할 수 있다는 의미이고 지역 스코프(Local Scope)는 해당 지역에서만 접근할 수 있어 지역을 벗어난 곳에선 접근할 수 없다는 의미입니다.

--자바스크립트에서 함수를 선언하면 함수를 선언할 때마다 새로운 스코프를 생성하게 됩니다. 그러므로 함수 몸체에 선언한 변수는 해당 함수 몸체 안에서만 접근할 수 있는데요. 이걸 함수 스코프(function-scoped)라고 합니다. 함수 스코프가 바로 지역 스코프의 예라고 할 수 있습니다.

  • 호이스팅 : 함수 안에 있는 선언들을 모두 끌어 올려서 해당 함수 유효 스코프의 최상단에 선언 하는 것을 말합니다.

-- 함수 선언문은 코드를 구현한 위치와 관계없이 자바스크립트의 특징인 호이스팅에 따라 브라우저가 자바스크립트를 해석 할 때 맨위로 끌어 올려집니다.

-- 함수 표현식은 함수 선언문과 달리 선언과 호출 순서에 따라서 정상적으로 함수가 실행되지 않을 수 있습니다. 함수 표현식에서는 선언과 할당의 분리가 생깁니다.

  • Temporal Dead Zone(TDZ) : 선언 전에 변수를 사용하는 것을 비 허용하는 개념상의 공간입니다.

-- TDZ에 있는 변수에 접근하게 되면 ReferenceError: Cannot access before initialization 에러가 발생합니다.

-- TDZ는 const, let, class는 TDZ에 영향을 받습니다. 즉 const, let, class는 선언 전에 변수를 사용하는것을 허용하지 않습니다.

-- 반대로 var, function, import의 선언은 TDZ의 영향을 받지 않습니다. 특히 var 변수는 선언 전에도 사용할 수 있는 점에서 var 변수 사용을 피해 예기치 못한 오류를 방지하는 것이 좋습니다.

-- 변수가 먼저 선언이 된 경우, 초기화에 따라서 TDZ가 생깁니다. 특히 let,const와 var는 초기화 시점이 다릅니다. var는 암묵적으로 undefined로 초기화 된 상태에서 자바스크립트 코드를 읽기 때문에, TDZ에서 에러가 나지 않습니다.

2. 실행 컨텍스트와 콜 스택

  • 실행 컨텍스트 : 자바스크립트의 핵심 개념으로, 코드를 실행하기 위해 필요한 환경이며, 더 자세히 말하자면 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.

-- 자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념이며 모든 코드는 특정한 실행 컨텍스트 안에서 실행된다.
-- 자바스크립트는 어떤 실행컨텍스트가 활성화되는 시점에 선언된 변수들을 위로 끌어올리고 (호이스팅) 외부 환경 정보를 구성하고 this값을 설정하는 등의 동작을 수행하는데, 이로 인해 다른 언어에서는 발생할 수 없는 특이한 현상들이 발생한다.

  • 글로벌 실행컨텍스트

-- 디폴트 실행 컨텍스트로ㅡ 자바스크립트 파일이 엔진에 의해 처음 로드되었을 때 실행되기 시작하는 환경이다.

  • 함수 실행컨텍스트

-- 우리가 실행컨텍스트를 따로 구성하는 방법은 함수를 실행하는 것 뿐이다. 함수가 호출되고 실행됨에 따라서 해당 함수 안에서 생성되는 컨텍스트. 각각의 함수는 고유의 실행 컨텍스트를 가진다. 그리고 전역 실행 컨텍스트에 언제나 접근할 수 있다.

  • 콜 스택

  • 콜 은 호출을 뜻하고 스택은 출입구가 하나뿐인 깊은 우물 같은 데이터 구조다. 따라서 콜스택은 자바스크립트가 함수 호출을 기록하기 위해 사용하는 우물 형태의 데이터 구조이다

-- 항상 맨 위에 놓인 함수를 우선으로 실행된다. 이런 식으로 자바스크립트 엔진은 가장 위에 쌓여있는 context와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장한다.

-- 자바스크립트 엔진은 함수를 호출할 때 Execution context 를 생성하고 Call Stack에 추가합니다. 함수가 종료된 후 Creation phase, Execution phase 두 단계가 끝나면 자바스크립트 엔진이 Call Stack에서 해당 함수를 제거(pop) 하게 됩니다.

3. 스코프 체인, 변수 은닉화

  • 스코프 체인 : 일종의 리스트로서 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장하고, 의미 그대로 각각의 스코프가 어떻게 연결(chain)되고 있는지 보여주는 것을 말한다.

-- 스코프 체인(scope chain)은 함수의 감춰진 프로퍼티인 [[Scope]]로 참조할 수 있다.

console.dir()을 사용하면, 개발자 도구로 쉽게 확인이 가능하다.

-- b() 함수에 [[Scopes]] 속성이 존재한다. 이것이 바로 스코프 체인(scope chain)이다.
자기 자신의 스코프(scope)를 제외한 자신과 가장 가까운 변수 객체의 스코프 순으로 접근하는 것을 눈으로 확인할 수 있다.

-- 정리하자면 자기 자신의 스코프(scope)를 제외한 자신과 가장 가까운 변수 객체의 모든 스코프들을 스코프 체인이라 할 수 있다.

  • 변수 은닉화 : 직접적으로 변경되면 안 되는 변수에 대한 접근을 막는 것을 은닉화 라고 합니다.

-- 객체의 private 한 속성을 만들 수 없었던 자바스크립트에서는 몇 가지 대안을 사용해왔습니다. 다른 클래스 기반의 언어처럼 근본적으로 private 하게 만들 수 없었기 때문에 컨벤션으로 약속하거나 비은닉을 향한 자바스크립트의 여정 비슷한 효과를 주는 꼼수를 사용했었다.

-- 컨벤션을 이용한 방법으로는 관용적으로 가장 많이 사용되는 것이 _ 즉 언더스코어 프리픽스를 속성 명에 사용하는 것이다.

function SomeConstructor() {
  this._privateProp = 'dont touch this';
  this.publicProp = 'you can touch this';
}

-- 이 방법은 private로 취급할 뿐이지 실제로는 public으로 동작하기 때문에 외부에서 얼마든지 접근할 수 있었다.

-- 근본적으로 접근이 불가능한 private 속성을 만드는 방법으로는 클로저를 이용한 방법이 있다.

function SomeConstructor() {
  const privateProp = 'dont touch this';
  this.publicProp = 'you can touch this';

  this.doSomethingWithPrivateProp = () => { ... }
}

-- 하지만 이제는 자바스크립트에서도 진정한 의미의 private 속성을 만들 수 있게 되었다. 그 특징은 간단하게 요약하면 아래와 같다.

  • Stage-3 단계에 있는 스펙으로 특별한 결격 사유가 없는 한 표준 스펙이 될 것이다. 물론 변경되거나 개선될 여지는 있다.

  • private과 같은 키워드를 사용하지 않는다. 대신 # 즉 샵 프리픽스를 사용한다. 키워드가 아니라 프리픽스다. 속성 명 앞에 #이 붙으면 Private 필드로 동작한다. - Class Field Decalarations 스펙의 일부다. public과 다른 점은 클래스의 필드 선언을 통해서만 만들 수 있다. 즉 동적으로 객체에 private 필드를 추가할 수 없다.

  • 메서드에는 제한적이다. 메서드 선언으로 사용할 수 없다. private 메서드를 만들려면 함수 표현식으로 정의해야 한다.
    어디까지나 현재로서는 그렇다는 말이다. 스펙이 업데이트될 수도 있다. (Class fields and private methods: Stage 3 update)

  • Computed Property Name을 사용할 수 없다. #foo 자체만 식별자로 허용되고 #[fooName] 이건 문법 오류다.

  • 모든 Private 필드는 소속된 클래스에 고유한 스코프를 갖는다. 그렇기 때문에 독특한 특징이 있다.

실습 과제

  • 콘솔에 찍힐 b 값을 예상해보고, 어디에서 선언된 “b”가 몇번째 라인에서 호출한 console.log에 찍혔는지, 왜 그런지 설명해보세요. 주석을 풀어보고 오류가 난다면 왜 오류가 나는 지 설명하고 오류를 수정해보세요.
let b = 1;

function hi () {
const a = 1;
let b = 100;
b++;
console.log(a,b);
}

//console.log(a);

console.log(b);
hi();
console.log(b);
let b = 1; // 전역 스코프 👍

function hi() { // 함수 내부에서 선언된 변수들은 함수 내부에서만 유효한 지역 스코프
  const a = 1;
  let b = 100;
  b++;
  console.log(a, b);
}

// console.log(a); // a는 hi()함수 내부에서 선언된 지역 스코프이기때문에 함수 밖에서 존재하지 않음.
// 위를 동작하게 하려면 const a = 1을 함수 밖으로 꺼내야 함.

console.log(b); // 가장 첫줄의 전역스코프 b가 호출됨 👍

hi(); // 부른 함수가 실행되고 지역스코프 a = 1 과 b++로 1증가한 b = 100이 출력된다.

console.log(b); // let b = 100은 지역스코프기 때문에 여전히 전역스코프값 let b = 1을 따르는 모습 👍
profile
개발자가되고싶은사람

0개의 댓글