TIL 11일차

ᄋᄋ·2021년 11월 24일
0

원시 자료형과 참조 자료형

변수의 선언은 메모리에 자리를 잡는 것이고, 값의 할당은 메모리에 값을 채우는 것.

원시 자료형이 할당될 때에는 변수에 값(value) 자체가 담김.
객체가 아니면서 method를 가지지 않는 6 가지의 타입(원시자료형):
string, number, bigint, boolean, undefined, symbol, (null)

참조 자료형이 할당될 때는 보관함의 주소(reference)가 담김.
참조 자료형은 기존에 고정된 크기의 보관함이 아니라, 동적으로 크기가 변하는 특별한 보관함을 사용한다 --> heap

참조 자료형의 ===(strict equality)는 주소값이 같은지를 확인함.
그래서 [] === []는 false임.

원시 자료형과 참조 자료형의 차이

참조 자료형(배열, 객체...)는 어떻게 코드를 작성하느냐에 따라 보관되는 데이터의 양이 천차만별임.

원시 자료형은 "하나"의 의미를 가지는 데이터라 보관함의 크기는 고정됨.

원시형 데이터는 값 자체를 수정할 수 없지만(immutable), 참조형은 가능함. 프로퍼티나 값을 수정, 삭제, 추가가 가능한 것은 heap이라는 유동적인 장소에서 데이터를 보관하기 때문임. 각 데이터들은 특정 주소가 있음.

bigint

BigInt 타입은 임의 정밀도로 정수를 나타낼 수 있는 자바스크립트의 숫자 원시 형식임. BigInt를 사용하면 Number의 정수 한계를 넘어서는 큰 정수도 안전하게 저장 및 연산할 수 있음.

BigInt는 정수 끝에 n을 추가하거나 생성자를 호출하여 생성됨.

const x = 2n ** 53n;
9007199254740992n
const y = x + 1n;
9007199254740993n

BigInt() 생성자는 BigInt 객체를 생성함.
BigInt(value);

여기 value는 생성하는 객체에 할당할 숫자 값, 문자열 또는 정수임.
BigInt(123); //123n

산술 연산자 +, *, -, ** 및 %를 BigInt에서도 사용할 수 있음. BigInt는 정확히 Number와 같지는 않으나 대략적으로는 같다고 할 수 있음.

BigInt는 if, ||, &&, Boolean, !와 같이 Boolean 타입으로 변환되는 경우 Number처럼 작동함.

BigInt는 Number로 교체할 수 없으며 TypeError가 발생함.

Symbol

Symbol에 대한 설명 바로가기

null

null은 원시 타입과 거의 같게 사용된다고 볼 수 있음. 작동 방식 또한 다른 원시 타입과 같음. 다만 엄밀하게 따지자면 원시 타입이라고 볼 수 없음.

typeof null === "object";       // true

null은 undefined과 같이 글로벌(전역) 객체의 속성에 대한 식별자가 아님. 대신 null은 식별되지 않은 것을 표현함. 즉, 변수가 아무런 객체를 가리키지 않음을 표현함. (null은 empty reference임.) API에서는 종종 관련된 객체가 존재하지 않을 때 그 객체 대신 null을 사용함.

NaN이 '숫자가 아님'을 표현하기에 typeof NaN이 'number'인 것처럼 null도 비슷한 논리로 typeof null === "object" 인 거임.

하지만 null은 원시자료형이기 때문에 객체처럼 null.foo = 42와 같은 속성 추가가 불가함. 근데 NaN은 실제로 숫자객체처럼 사용 가능해서 NaN.foo = 42 를 해도 잘 동작함.

// 정의되지 않고 초기화된 적도 없는 foo
foo; //ReferenceError: foo is not defined

// 존재하지만 값이나 자료형이 존재하지 않는 foo
var foo = null;
foo; //null

null과 undefined의 차이
typeof null          // "object" (하위호환 유지를 위해 "null"이 아님)
typeof undefined     // "undefined"
null === undefined   // false
null  == undefined   // true
null === null        // true
null == null         // true
!null                // true
isNaN(1 + null)      // false
isNaN(1 + undefined) // true

스코프

: 변수 접근 규칙에 따른 유효 범위

1. block scope와 function scope

  • block scope : { } 로 둘러싸인 스코프
    *arrow 함수도 여기에 포함됨.
  • function scope : function 키워드를 쓴 스코프.
    (arrow 함수는 함수 스코프가 아님.)
  • block scope > function scope (함수 스코프도 블록 스코프에 속함.)

2. 전역 변수와 지역 변수간의 우선 순위

: 지역변수 > 전역변수
외부 스코프 --접근X--> 내부 스코프 변수
내부 스코프 --접근O--> 외부 스코프 변수
=> 안쪽 스코프에서 바깥쪽 스코프로는 접근할 수 있지만 반대는 불가능.

3. 중첩 규칙

스코프는 중첩이 가능함.

scope1은 전역(global) 스코프, 2,3,4는 지역(local) 스코프.

4. let, const, var 의 차이

var : 유효범위는 함수 스코프(+ arrow func), 재할당 가능, 재선언도 가능.
let : 유효범위는 블록 스코프, 재할당 가능.
const : 유효범위는 블록 스코프, 재할당 불가능.

5. debugger & breakpoint

debugger
debugger문을 활용한다면 자바스크립트 코드도 실행 흐름을 따라가며 효과적으로 검증할 수 있음.
검증하고 싶은 코드의 중간에 debugger문을 끼워넣으면 됨.
이는 코드 실행시에 breakpoint가 됨.
debugger문을 끼워넣은 위치에서 실행이 중단되고, 디버깅을 할 수 있게 된다.

*참고 링크

breakpoint : 중단점 설정은 하나만 가능한게 아니라 동시에 여러 개를 설정 하는 것이 가능.

6. 전역 객체(window)의 이해

브라우저에는 window 객체가 존재함. 브라우저 창을 대표하는 객체임. 전역 항목도 담고 있음. var로 선언된 전역 변수와 전역함수가 이 객체에 속함.

주의할 점

  • 전역 변수를 최소화하자. (side effect를 줄이기 위함.)
  • let, const를 주로 사용하자.
    : var 키워드는 블록 스코프를 무시하고, 재선언을 해도 에러를 내지 않음.
    전역변수를 var로 선언하는 경우, window 기능을 덮어씌워서 내장 기능을 사용할 수 없게 만들 수 있음.

  • 선언없이 변수를 할당하면 안 됨.
    : 선언 없이 변수를 할당하면, 해당 변수는 var로 선언한 전역 변수처럼 취급됨.
    실수를 방지하기 위해 strict mode를 사용할 수 있음. Strict Mode는 브라우저가 보다 엄격하게 작동하도록 만들어줌. Strict Mode를 적용하려면, js 파일 상단에 'use strict'라고 입력하면 됨. (따옴표 포함!)

+헷갈릴 수 있는 부분)

위의 get(x) 부분은 사실 let x; 와 같다고 볼 수 있음.
즉, 지역 스코프에서 새로운 변수 x를 선언한 거임.
여기서 get(20);을 했으니 let x = 20;이라는 선언과 할당이 지역스코프 내에서 일어난 거라고 볼 수 있음. 따라서 위의 전역변수 x와는 전혀 다른 x임.


클로저

정의

"함수와 함수가 선언된 어휘적(lexical) 환경의 조합"
이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성됨.

특이하게도 자바스크립트는 함수가 호출되는 환경과 별개로, 기존에 선언되어 있던 환경 - 어휘적 환경 - 을 기준으로 변수를 조회하려고 함. "외부함수의 변수에 접근할 수 있는 내부함수"를 클로저 함수로 부르는 이유도 그러함.

클로저 함수

클로저는 외부함수의 컨텍스트에 접근할 수 있는 내부함수를 뜻함. 외부함수의 실행이 종료된 후에도, 클로저 함수는 외부함수의 스코프, 즉, 함수가 선언된 어휘적 환경에 접근할 수 있음.

func A( ) { func B( ) { } }

외부함수 func A, 내부함수 func B.
여기서 func B가 클로저 함수.

클로저 사용 예시
클로저를 통해 커링(currying, 함수 하나가 n개의 인자를 받는 대신 n개의 함수를 만들어 각각 인자를 받게 하는 방법), 클로저 모듈(변수를 외부 함수 스코프 안쪽에 감추어, 변수가 함수 밖에서 노출되는 것을 막는 방법) 등의 패턴을 구현할 수 있음.

클로저의 단점
일반 함수였다면 함수 실행 종료 후 garbage 컬렉션 대상이 되었을 객체가, 클로저 패턴에서는 메모리 상에 남아 있게 됨. 외부함수 스코프가 내부함수에 의해 언제든지 참조될 수 있기 때문임. 따라서 클로저를 남발할 경우 퍼포먼스 저하가 발생할 수도 있음.

*자바스크립트는 가비지 컬렉션을 통해 메모리 관리를 함. 객체가 참조 대상이 아닐 때, 가비지 컬렉션에 의해 자동으로 메모리 할당이 해제됨.

클로저 함수의 기본 형태

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


클로저는 리턴하는 함수에 의해 스코프(변수의 접근 범위)가 구분됨. 클로저의 핵심은 스코프를 이용해서, 변수의 접근 범위를 닫는(closure; 폐쇄) 데에 있음. 따라서, 함수를 리턴하는 것만큼이나, 변수가 선언된 곳이 중요함.

클로저의 활용

  • 데이터를 보존함.
    일반적인 함수는, 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수 없음. 이와 다르게, 클로저는 외부 함수의 실행이 끝나더라도, 외부 함수 내 변수가 메모리 상에 저장됨. (어휘적 환경을 메모리에 저장하기 때문에 가능한 일임.)

  • 정보의 접근 제한
    클로저 모듈 패턴이라고 불리는 아주 유용한 패턴임.
    클로저를 이용해 내부 함수를 단 하나만 리턴하는 것에 그치지 않고, 객체에 담아 여러 개의 내부 함수를 리턴하도록 만듬.

    참고로, '외부 스코프에서는 내부 스코프의 변수에 접근할 수 없다'는 규칙에 의해, 어떤 경우에도 value는 직접 수정이 불가능함. 대신, 리턴하는 객체가 제공하는 메소드를 통해 value 값을 간접적으로 조작할 수 있음.
    이것이 바로 정보의 접근 제한(캡슐화)임.

    클로저를 통해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있음.

  • 모듈화

    makeCounter에 의해 리턴된 객체는, makeCounter를 실행할 때에 선언되는 value 값을 각자 독립적으로 가지게 됨. 따라서 counter1에서의 value와 counter2에서의 value는 서로에게 영향을 끼치지 않고, 각각의 값을 보존할 수 있음.

    이와 같이 함수 재사용성을 극대화하여, 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것을 모듈화라고 함.

    클로저를 통해 데이터와 메소드를 같이 묶어서 다룰 수 있음.
    즉, 클로저는 모듈화에 유리함.


몰랐던 것

console.log에 동작 코드를 집어넣으면 그 결과가 반영됨.

증감연산자( ++, -- )
변수에 1을 더하거나 1을 빼줌.
++나 --를 붙이는 위치에 따라서 결과가 달라질 수 있음.

앞에 붙으면 전위증감연산자, 뒤에 붙으면 후위증감연산자라고 부름.
++ii = i + 1;과 같음.
하지만 i++i = i + 1; 과는 결과가 다를 수도 있음.

++가 앞에 붙으면 먼저 1을 더하고 다음 동작을 하고, ++가 뒤에 붙으면 동작을 먼저 한 후 1을 더함.


+헷갈렸던 클로저 문제

var a = 0;
function foo() {
    var b = 0;
    return function() {
        console.log(++a, ++b);
    };
}

var f1 = foo();
var f1 = foo();

profile
개발자A

0개의 댓글