TIL. Day7 (원시자료형,참조자료형,Scope,Closure)

jeyoon·2021년 1월 26일
0

원시 자료형과 참조 자료형

원시 자료형 (primitive data type)

  • 객체가 아니면서 method를 가지지 않는 6가지의 타입
    • string, number, bigint, boolean, undefined, symbol, (null)
  • 데이터가 "하나"의 정보를 담고 있음
  • 변수에는 크기와 관계 없이 하나의 데이터만 담음
 const num1 = 123;
 const num2 = 123456789;
  • 값 자체에 대한 변경이 불가능(immutable)하지만, 변수에 다른 데이터를 할당할 수는 있음
"hello world!"
"hello codestates!"
// "hello world!" 와 "hello codestates!"는 변경할 수 없는 고정된 값

let word = "hello world!" 
word = "hello codestates!" 
// 하지만 word라는 변수에 재할당을 하여 내용변경은 가능

const num1 = 123;
num1 = 123456789; // 에러 발생
// const 키워드로 선언하면 재할당 불가(let은 가능)
  • null: 어떤 객체 값이 비어있음을 의미하며, typeof null = 'object'이다. 따라서 엄밀히 따지면 원시 타입이라고 볼 수 없으나, 원시 타입과 거의 같게 사용되며 작동 방식 또한 원시 타입이라고 볼 수 있다.

참조 자료형 (reference data type)

  • 자바스크립트에서 원시 자료형이 아닌 모든 것은 참조 자료형

  • 배열([])과 객체({}), 함수(function(){})가 대표적

  • 변수에는 특별한 데이터 보관함(heap)을 찾아갈 수 있는 주소(reference)가 담겨있고, 이 주소를 따라가보면 특별한 데이터 보관함을 찾을 수 있음

  • 이 특별한 데이터 보관함은 자기 마음대로 사이즈를 늘렸다가 줄였다가 함 = 동적(dynamic)으로 변함

  • heap은 왜 동적으로 크기가 변하게 되었을까?

    • 대량의 데이터가 들어오는 경우, 고정된 데이터 공간을 사용하는 것은 비효율적
    • 데이터가 언제 늘어나고 줄어들지 모르기 때문에 별도의 저장공간(heap)을 마련하여 따로 관리하는 것이 효율적
    • 변수에는 원시값 혹은 주소만 지정할 수 있고, 주소는 크기가 변하는 heap을 참조

예제)

코드가 실행된 후, x.foo의 값은 무엇일까요?

let x = { foo: 3 };
let y = x;
y = 2;


x.foo // 3

코드가 실행된 후, score 의 값은 무엇일까요?

let score = 80;
function doStuff(value) {
  value = 90;
}

doStuff(score);

score // 80

스코프(Scope)

  • Scope: 변수 접근 규칙에 따른 변수의 유효 범위
  • 변수는 어떠한 환경 내에서만 사용 가능, 프로그래밍 언어는 각각의 변수 접근 규칙을 갖고 있다
  • 스코프는 변수와 그 값이 어디서부터 어디까지 유효한지를 판단하는 범위
  • JavaScript는 기본적으로 함수가 선언되는(lexical) 동시에 자신만의 Scope를 가짐

Global Scope vs. Local Scope

  • 안쪽 Scope에서는 바깥 변수/함수 접근 가능
  • 바깥쪽 Scope에서는 안쪽 변수/함수 접근 불가능
  • Scope는 중첩이 가능 - 함수 안에 함수를 넣을 수 있음
  • Global Scope는 최상단(가장 바깥쪽)의 Scope로,
    전역 변수에는 어디서든 접근이 가능
  • 지역 변수는 함수 내에서 전역 변수보다 더 높은 우선순위를 가짐

스코프의 종류와 let, const, var

스코프의 종류

  • function scope: 함수로 둘러싸인 범위
  • block scope: 중괄호로 둘러싸인 범위 (if문, for문에서 봤쥬)
    • 화살표 함수로 둘러싸인 범위는 block scope로 취급됨!

let, const, var

  • 변수를 정의하는 또 다른 키워드, var (old way)

    • block 범위를 벗어나도 같은 function scope에서는 사용가능
      ➡️ 블록 스코프를 무시하고, 함수 스코프만 따름
    • 그러나 Block 단위로 Scope를 구분했을 때에 예측하기 쉬운 코드를 작성할 수 있으므로 let 키워드의 사용이 권장됨
    • var 키워드로 변수 선언 시 재선언이 가능 (TypeError ㄴㄴ)
  • const 키워드

    • 값이 변하지 않는 변수, 즉 상수를 정의할 때 사용
    • let 키워드와 동일하게 Block Scope를 따름
    • 값을 재할당하려고 하면 TypeError를 냄

변수 선언에서 주의할 점

  • 전역변수와 window 객체
    • 전역 범위를 대표하는 window 객체
    • Global Scope에서 선언된 함수, 그리고 var 키워드를 이용해 선언된 변수는 window 객체와 연결됨
var myName = "Paul";
   console.log(window.myName); //Paul

function foo(){
   console.log('bar');
}
console.log(foo===window.foo); //true

전역 변수는 최소화할 것 ❗️

  • 전역 범위에 너무 많은 전역 변수를 선언하지 않도록 주의 !
  • local scope에서 변수 사용하는 것이 안전 (전역 변수에는 어디서든 접근 가능하므로, side effect 발생할 수 있음)

선언 없는 변수 할당 금지 ❗️

🚫 선언 키워드(var, let, const)없이 변수를 할당하지 말 것!

function showAge(){
    age = 90; // var로 선언한 전역변수처럼 취급됨
    console.log(age); // 90
}

showAge(); 
console.log(age); // 90 
console.log(window.age); // 90

이런 실수를 방지하기 위해 Strict Mode를 사용할 수 있다.
Strict Mode는 브라우저가 보다 엄격하게 작동하도록 만들어준다. 앞서 언급한 것처럼 "선언 없는 변수 할당"의 경우도 Strict Mode는 에러로 판단한다.

Strict Mode를 적용하려면, js 파일 상단에 'use strict' 라고 입력하면 된다. (따옴표 포함)

클로저 (Closure)

  • 클로저는 외부 함수의 변수에 접근할 수 있는 내부 함수, 또는 이러한 작동 원리를 일컫는 용어이다.
  • 클로저 함수 안에서는 지역 변수, 외부 함수의 변수, 전역 변수로의 접근이 전부 가능

클로저의 활용

데이터를 보존하는 함수

외부 함수의 실행이 끝나더라도, 외부 함수 내 변수를 사용할 수 있다.

유용한 클로저 예제 ⬇️

function htmlMaker(tag){
    let startTag ='<'+tag+'>';
    let endTag='</'+tag+'>';
    return function(content){
        return startTag + content + endTag;
      }
  }
 
    let divMaker = htmlMaker('div');
    divMaker('code'); // '<div>code</div>'
    divMaker('states'); // '<div>states</div>'
   
// htmlMaker함수의 실행이 끝났지만 변수 tag에 문자열 'div'를 담아 사용 가능

    let h1Maker = htmlMaker('h1');
    h1Maker('Headline'); // '<h1>Headline</h1>'

// htmlMaker함수의 실행이 끝났지만 변수 tag에 문자열 'h1'을 담아 사용 가능

정보의 접근 제한 (캡슐화)

  • 클로저 모듈 패턴 : 변수를 스코프 안쪽에 가두어 함수 밖으로 노출시키지 않는 방법
function makeCounter(){
    let privateCounter = 0;

    return {
        increment: function(){
            privateCounter++;
        },
        decrement: function(){
            privateCounter--;
        },
        getValue: function(){
            return privateCounter;
        }
      }
}
let counter1 = makeCounter();
counter1.increment();
counter2.increment();
counter2.getValue(); // 2

let counter2 = makeCounter();
counter2.increment();
counter2.decrement();
counter2.increment();
counter2.getValue(); // 1

privateCounter라는 변수는 어떤 경우에도 직접 수정이 불가능하지만, 리턴하는 객체가 제공하는 메소드를 통해 privateCounter의 값을 간접적으로 조작할 수 있다.
이것이 바로 정보의 접근 제한(캡슐화)이다.

이렇게 하지 않고 privateCounter를 전역 변수로 선언할 경우, 다른 함수 혹은 로직 등에 의해 의도치 않게 값이 변경될 수 있다. 이를 side effect라고 한다. 클로저를 사용해 불필요한 전역 변수 사용을 줄여, 값을 보다 안전하게 다룰 수 있다.

모듈화

위의 예제 코드에서 makeCounter 함수를 이용해 여러 개의 함수를 만든 것을 확인했다. counter1과 counter2의 privateCounter는 서로에게 영향을 미치지 않고, 각각의 값을 보존할 수 있다. 이와 같이 함수 재사용성을 극대화하여 함수 하나를 독립적인 부품 형태로 분리하는 것을 모듈화라고 하며, 클로저는 모듈화에 유리하다.

0개의 댓글