reference type, Scope, Closer
Reference type
변수에는 원시 자료형(primitive type)과 참조 자료형(reference type)이 있다.
원시 자료형은 말 그대로 원시적, 단순하게 한 메모리에 변수라는 이름을 가진 값을 할당하는 것이므로 쉽게 이해할 수 있다.
참조 자료형은 왜 그런 이름이 붙었을까? 바로 reference, 주소가 변수에 값으로 들어가기 때문이다. 각 reference는 동적(dynamic)으로 크기가 변하는 특별한 보관함(heap)으로 이어진다.
내가 기억해야할 점은 아래 예시와 같다.
// 원시 자료형의 경우
let a = 0;
let b = a;
b = 1;
console.log(a); // 0
// 참조 자료형의 경우
let c = [1, 2, 3];
let d = a;
d[0] = 100;
console.log(c[0]); // [100, 2, 3]
reference data를 복사한다는 것은 주소를 복사하는 것이다.
원시 자료형은 단순히 값을 복사하는 것이므로 b가 다른 값으로 변해도 a 값은 변화가 없는 반면,
참조 자료형은 주소값을 복사하므로 d를 이용해 배열의 요소를 바꾸면 같은 주소를 가진 c의 요소도 바뀐다.
주소 참조라는 점을 이해하니 앞선 Array를 공부할 때 [] === []
가 false인 이유를 알 수 있었다. 동일한 이유로 비어있지 않은 object, array 모두 같게 생기더라도 서로 다른 주소를 가지므로 엄격한 비교에서는 false를 반환한다. 해보니까 그냥 비교 (==)에도 false를 반환하기는 한다..
'참조하다' 라는 말에 익숙해지자. 바로 변수의 값을 읽는 것이 아니라, 주소 참조를 선행한 다음 그 주소에 도착해 데이터를 읽는 것!
// 객체의 주소를 복사하고, 주소 도착지의 객체에 영향을 미치지 않는 값을 재할당 했을 때 let x = {foo: 3}; let y = x; y = 2; console.log(x) // {foo: 3}
// 객체의 주소를 복사하고, 주소 도착지의 객체의 값을 바꾸도록 재할당 했을 때 let a = {foo: 3}; let b = a; b.foo = 4; console.log(x) // {foo: 4}
Scope
우물 같은 스코프.. 안쪽 스코프에서는 밝은 바깥을 내다볼 수 있지만 바깥 스코프에서는 안쪽이 어두워 보이지 않는다!
가장 바깥쪽의 scope는 Global scope(전역 스코프), 이외는 Local scope(지역 스코프)라고 하고, 지역스코프가 더 높은 우선순위를 가진다.
1. block scope
- 반복문에 선언하는 i는 딱 그 block에서만 사용된다. block을 넘어서서 i를 묻는다면 Reference Error!
2. fucntion scope
- function은
화살표 함수는 block scope로 취급된다. function 키워드를 쓴 경우에만 function scope!
나의 level에서 선언하지 않은 변수가 바깥 스코프에서도 안보이면 ReferenceError가 발생한다.
❗️var
, scope와 관련해 주의할 점
- var는 block scope를 무시한다!(for문에서 var로 i를 선언하면 바깥에서도 i가 조회된다)
- 재선언도 가능하다. 보통 재선언되면 그건 버근데.. 문제인걸 모르는 것
- 전역변수를 var로 선언하면 브라우저에만 존재하는 window 객체를 덮어씌워서 내장 기능이 먹통이 될지도 모른다.
그러니까 그냥 쓰지말자!!!!!
let x = 10; function outer () { let x = 20; function inner () { return x; } return inner(); } let result = outer(); console.log(result); // 20
이렇게 scope가 위계적으로 겹치면 순차적인 스코프 체이닝이 일어난다.
let x = 10; function outer () { let x = 20; function inner () { x = x + 10; return x; } inner(); } outer(); let result = x; console.log(result); // 10
result
는 outer()
함수 실행과 무관하게 같은 레벨에서 10으로 할당되었으므로 10을 반환한다.
let x = 10; function outer () { x = 20; function inner () { let x; x = x + 20; return x; } inner(); } outer(); let result = x;
위와 마찬가지인 상황이지만, outer()
내에 x
가 선언되지 않아 상위 스코프인 전역 스코프를 변경한다.
❗️참고로, inner()
함수에서 type이 정해지지 않은 채 선언된 x
를 + 20
하면 undefined + 20
으로 NaN
을 반환한다.
Closer
- 외부함수의 변수에 접근할 수 있는 내부함수라고 받아들이자.
- 실제 정의는, 함수와 함수가 선언된 어휘적 환경의 조합이다.
const adder = function(x){
return function(y){
return x + y;
}
}
이때 안쪽의 function(y)
이 closer에 해당하고, 스코프를 이용해 변수의 접근 범위를 닫는 것에 핵심이 있다.
어떻게 closer를 사용할 것인가
1. 데이터 보존
const add5 = adder(5);
위 함수 adder
에 이어서, add5
를 adder(5)
라고 선언하고 실행시키더라도 아직 y 변수가 남아있으므로 함수 자체로 남아있다. 따라서 외부 함수의 실행이 끝나도, 인자로 넘긴 5라는 값을 x에 계속 담은 채로 둘 수 있다.
즉, 특정 데이터를 보존(외부 함수에 해당)한 상태로 여러번 사용할 수 있다.
2. 클로저 모듈 패턴
const makeCounter = () => {
let value = 0;
return {
increase: () => {
value = value + 1
},
decrease: () => {
value = value - 1
},
getValue: () => value
}
}
const counter1 = makeCounter();
함수의 리턴값을 object로하는데, 그 속에 여러개의 내부 함수를 담는다.
value
에 새로운 값을 할당하는 것은 불가능하지만 객체에 담긴 함수를 메소드로 활용하여 간접적으로 바꿀 수 있다.
생각해보면 외부 함수 단계에서는 value
를 선언한 것 밖에 없다. 하지만 이 과정을 통해 value
는 전역 변수가 아닌 스코프 내 접근이 제한된 변수가 되었다. 이를 캡슐화라고 하는데, 전역 변수로 선언하여 발생할 지도 모르는 side effect를 최소화할 수 있다.
3. 함수의 재활용, 모듈화
reference type은 변수에 주소를 저장한다고 공부했다. 두개의 다른 변수에 같은 함수를 할당하는 것은 같은 주소를 할당하는 것이므로, 값도 동일하게 바뀔 것이다.
하지만 위에서 만든 makeCounter()
의 경우로 예를 들면,
const counter2 = makeCounter();
이라고 동일한 함수를 할당하더라도
value
가 서로 영향을 끼치지 않도록 자신만의 함수 스코프 내에 갇혀있기때문에 각자 독립적이면서도 같은 함수를 할당하며 각각의 값을 보존할 수 있다.
이렇게 재사용한 상태로 만들어졌다고 해서 모듈화라고 말한다.
Author And Source
이 문제에 관하여(reference type, Scope, Closer), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@titaniumdiana/reference-type-Scope-Closer저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)