코드스테이츠(SEB_FE_42)

[JS] 원시 자료형과 참조 자료형, 스코프, 클로저

codeyun2 2022. 11. 7. 16:28

원시 자료형과 참조 자료형

 

원시 자료형(Primitive data type)

: number, string, boolean, null, undefined, bigint, symbol (객체가 아님)

고정된 저장공간을 가짐. 하나의 데이터만 담음

값 자체에 대한 변경 불가능 immutable

함수의 전달인자로 전달할 경우, 값 자체를 복사

- 전달된 전달인자가 호출된 함수의 지역변수로 (매 호출 시마다) 새롭게 선언됨.

 

참조 자료형(Reference data type)

: array, object, function(Object)

크기가 동적으로 변하는 저장공간(heap)을 가짐. 다량의 데이터를 담음

데이터가 동적으로 변함

함수의 전달인자로 전달할 경우, reference가 전달

 

변수 선언: 메모리에 자리 잡기

변수 할당: 메모리에 값 할당

 

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

: 원시 자료형이 할당될 때에는 변수에 값(value)자체가 담기고, 참조 자료형이 할당될 때에는 보관함의 주소(reference)가 담김

- 변수에 주소가 저장되기 때문에 동적으로 크기가 변하는 특별한 보관함(heap)을 사용할 수 있음

(> 값 또는 주소가 저장되는 공간: stack)

  원시 자료형 참조 자료형
stack(할당 시 저장되는 값) value reference
함수 전달인자(arguments) value 복사 reference

 

 

스코프(scope)

: 변수에 접근할 수 있는 범위

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

  • 안쪽 스코프에서 바깥쪽 스코프로 접근할 수 있지만 반대(바깥에서 안)는 불가능
  • 스코프 중첩이 가능함
  • 가장 바깥의 스코프는 전역 스코프(Global scope)라 부름 -> 외에는 모두 지역 스코프(Local scope)라 부름.
  • 지역 변수는 전역 변수보다 더 높은 우선 순위를 가짐
    (지역 변수: 지역 스코프에서 선언한 변수, 전역 변수: 전역 스코프에서 선언한 변수)
    • variable shadowing
      동일한 변수 이름으로 인해 바깥쪽 변수가 안쪽 변수로 가려지는(shadow) 현상

 

안쪽 스코프에서 변수 선언+ 할당하는 경우 내부에서만 사용.

바깥 스코프에서 선언한 변수를 안쪽 스코프에서 사용한다면 바깥에도 영향을 끼침.

 

block scope

: 중괄호로 둘러싼 범위 + 화살표 함수

function scope(함수 스코프)

: function 키워드가 등장하는 함수 선언식, 함수 표현식의 범위

 

let, const, var

  let const var
유효 범위 블록 스코프, 함수 스코프 블록 스코프, 함수 스코프 함수 스코프+ 화살표 함수 블록
재선언 불가능(SyntaxError) 불가능 가능
값 재할당 가능 불가능(TypeError) 가능

var보다 let을 사용한 변수 선언이 권장되는 이유

- 변수 재선언 가능

- window 객체 기능을 덮어씌워서 내장 기능을 사용할 수 없게 만들 수 있음

> 선언 키워드 없는 선언은 var로 선언한 전역 변수로 취급됨

>> 선언 키워드 없는 선언을 방지하기 위해 Strict Mode를 사용할 수 있음

: 문법 실수를 에러로 판단해주는 기능

// js 파일 최상단에 입력
// 위에는 주석만 사용 가능
'use strict'

// 클래스와 모듈을 사용하면 use strict가 자동 적용됨

모든 선언(var, let, const, function, class)는 호이스팅hoisting 특징을 갖고 있음

- 선언 단계, 초기화 단계, 할당 단계 구분이 중요

- var 키워드는 스코프 선두의 선언 단계와 초기화 단계가 한번에 진행됨(> undefined), 할당은 실제 위치에서 실행

- let, const 키워드는 스코프 선두에서 선언 단계만 진행, 변수를 선언하는 코드가 실행될 때 초기화 단계가 진행

(ReferenceError > 스코프의 시작 지점부터 초기화 시작 지점까지를 일시적 사각지대TDZ: Temporal Dead Zone라고 함)

 

 

IIFE(Immediately-invoked function expressions 즉시 실행 함수 표현식)

: var도 블록 레벨 스코프를 가질 수 있게 하는 방법(var와 마찬가지로 ES6 이후로 자주 사용하지 않음. let const 사용)

// IIFE를 만드는 방법

(function() {
  alert("함수를 괄호로 둘러싸기");
})();

(function() {
  alert("전체를 괄호로 둘러싸기");
}());

!function() {
  alert("표현식 앞에 비트 NOT 연산자 붙이기");
}();

+function() {
  alert("표현식 앞에 단항 덧셈 연산자 붙이기");
}();

함수 선언문을 만들 땐 반드시 '이름'이 있어야 하며, 정의와 동시에 호출을 허용하지 않음.

-> 자바스크립트가 함수 선언문을 함수 표현식(즉시 호출 가능)으로 인식하도록 속여야 함

 

 

window 객체

- 브라우저 창을 대표하는 객체

- 브라우저 창과 관계없이 전역 항목도 담고 있음

- var로 선언된 전역 변수와 전역 함수가 window객체에 속함

 

전역 변수

: 어디서든 접근 가능한 변수

편리하지만, 다른 함수 혹은 로직에 의해 의도되지 않은 변경이 발생할 수 있음 => 부수 효과 side effect

 

개발자도구의 breaktime을 이용한 scope 확인

개발자도구 > Sources > js파일 선택 > break할 라인 선택 > 새로고침 > scope 확인

 

 

클로저(closure)

: 함수와, 함수가 선언된 어휘적(lexical) 환경의 조합

내부 함수(=클로저 함수)는 외부 함수에 선언된 변수(지역 변수=> 자유 변수)를 참조하며

외부 함수가 내부 함수를 리턴하는 중첩 함수 형태

 

클로저 함수: 외부함수의 변수를 기억한 상태로 변수에 저장하여 사용

클로저 함수 선언, 할당 때 외부 함수 실행됨

// outerFn 함수 '실행'하여 리턴 값을 innerFn 변수에 넣음

const innerFn = outerFn();

* 스코프를 이용하여 변수의 접근 범위를 닫는다(closure)는 점이 중요

* 함수의 리턴만큼 함수 선언 위치가 중요

* 상태(state)를 은닉

 

어휘적 범위 lexical scope

함수의 상위스코프는 함수가 정의되는 시점에 결정되는데(고정) 그 범위를 어휘적 범위라고 한다.

최초 선언 위치가 중요, 호출과 상관 없음

스코프 체인 scope chain

: 해당 코드(실행중인 컨텍스트) 안의 변수의 유효 범위 체인(리스트)

함수가 정의될 때 저장됨

스코프가 담긴 순서대로 탐색(지역 변수> 전역변수, 안> 밖)

실행 컨텍스트 Execution context

: 코드가 실행되는 환경.

- 글로벌 실행 컨텍스트: 코드 실행 전 생성, 함수에 없는 모든 코드는 전역 실행 컨텍스트 안에 존재

- 함수 실행 컨텍스트: 함수 호출마다 새로운 실행 컨텍스트 작성

 

 

클로저 모듈 패턴

const makeCounter = () {
    let value = 0;
    
    return {
        // increase, decrease, getValue는 같은 스코프 체인을 공유
        increase: () => {
            value = value + 1
        },
        decrease: () => {
            value = value - 1
        },
        getValue: () => value
    }
}

const counter1 = makeCounter();

 

커링(Currying)

n개의 인자를 받는 과정을 n개의 단일 인자 함수열로 만드는 것

f(a,b,c)를 f(a)(b)(c)로

const curry = f => a => b => c=> f(a, b, c)
// 위 함수를 풀어서 작성하면 아래와 같음
function curry(f) {
    return function(a) {
        return function(b) {
            return function(c) {
                return f(a, b, c);
            }
        }
    }
}

function mul(a, b, c) {
    return a * b * c;
}

const curriedmul = curry(mul)
const curriedmulais3 = curriedmul(3)
const curriedmulais3bis5 = curriedmulais3(5)
console.log(curriedmulais3bis5(2)) // 30

* 커링 참고

 

 

클로저의 장점

1 데이터를 보존하는 함수

- 외부 함수의 실행이 끝나더라도 외부 함수 내의 변수가 메모리에 저장되어 있음

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

- 변수를 직접 수정할 수 없고 리턴 객체가 제공하는 메서드를 통해 간접적으로 조작

3 모듈화에 유리

- 함수의 재사용성을 극대화하여 함수 하나를 독립적인 부품 형태로 분리하는 것

- 데이터와 메서드를 묶어서 다룰 수 있음

 

클로저의 단점 "메모리 차지"

객체가 참조 대상이 아닐 때 JS 가비지 컬렉션(Garbage Collection)에 의해 자동으로 메모리 할당이 해제되는데

외부 함수 스코프가 내부 함수에 의해 언제든지 참조될 수 있기 때문에 클로저 패턴에서는 메모리가 남아있음.

 

 

 

 

2022.11.07

코스 S1U10 중