[JS] 원시 자료형과 참조 자료형, 스코프, 클로저
원시 자료형과 참조 자료형
원시 자료형(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) 현상
- variable shadowing
안쪽 스코프에서 변수 선언+ 할당하는 경우 내부에서만 사용.
바깥 스코프에서 선언한 변수를 안쪽 스코프에서 사용한다면 바깥에도 영향을 끼침.
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 중