스코프에 대한 지식이 없다면 먼저 간단히 읽고 오세요.

실행컨텍스트에 대해서 알고 진행해야하기에 실행 컨텍스트 글을 꼭 참고해주세요.

 

실행 컨텍스트(Execution context)

실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체입니다. 자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 선언된 변수를 위로 끌어올리고(호이스팅, hoisting) 외부

okayoon.tistory.com

 

 

 

클로저(Closure)

여러 함수형 프로그래밍 언어에서 등장하는 보편적인 특성입니다.

클로저는 객체지향과 함수형 프로그래밍에서 매우 중요한 개념입니다.

 

특성<- 이라는 것 보이시죠?

자바스크립트 고유의 개념이 아니라서 많은 사람들이 헷갈려하고(나 또한..) 정의를 요약하기가 힘듭니다.

 

 

"자신을 내포하는 함수의 컨텍스트에 접근할 수 있는 함수"

- 더글라스 크록포드, 자바스크립트 핵심가이드

 

"함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는것"

- 에단 브라운, 러닝 자바스크립트

 

" 컴퓨터 언어에서 클로저는 일급 객체 함수의 개념을 이용하여 스코프에 묶인 변수를 바인딩하기 위한 일종의 기술이다."  

- 위키백과

 

 

위의 글들만 봤을 때 혼란스러웠습니다...

추가적으로 자바스크립트 코어책을 보고 난 후 관련 지식들을 수정해서 작성해보려고 합니다.

 

 

개념

클로저는 어떤 함수 A가 선언된 당시 이 A 함수가 만들어질때 저장된 환경들과 또 다른 함수 B의 환경 사이에서의 상호관계에 따른 현상입니다.

=> 함수가 선언될 당시의 lexical environment(함수 선언당시 실행 컨텍스트 내의 식별자 정보, 외부환경 정보)의 상호관계에 따른 현상입니다.

 

이해가 어려우니 예시를 보고 다시 이야기해보겠습니다.

 

예시1

외부 함수의 변수를 참조하는 내부함수

(여기서 클로저는 식별자 name을 참조하기 위한 내부함수 inner에서 발생하는 현상입니다.)

var outer = function(){
	var name = 'hans';
    
    var inner = function(){
    	console.log(name);
    };
    
    inner();
};

outer();

1) inner 함수 내부에는 식별자 name이 없습니다.

=> 이것은 즉 inner 함수의 lexical environment의 environmentRecord에 name 식별자가 없다는 것을 의미합니다.

=> environmentRecord는 현재 컨텍스트와 관련된 코드의 식별자 정보(매개변수의 이름, 함수 선언, 변수명등)들이 저장해둡니다.

 

2) inner 함수는 lexical environment의 outer-environmentReference에서 상위 함수의 컨텍스트를 찾는데, 이때 상위 컨텍스트는 outer입니다.

=> outer-envirnmentReference는 현재 호출된 함수가 선언될 당시(할성화될 당시)의 LexicalEnvironment를 참조한 것입니다. 

=> 즉, 선언 당시 outer의 lexical environment를 참조합니다.

 

3) inner의 outer-environment로 인해 outer 함수의 lexical environment에서 name 식별자를 찾을 수 있습니다.

=> 스코프 체이닝!

 

4) outer 함수가 끝나게되면 outer함수의 실행 컨텍스트가 종료되며 lexical environment에 저장된 식별자(name, inner)에 대한 참조를 지웁니다.

=> 이때 만약 참조하는 변수가 있다면 메모리에 남아있고, 참조되지 않는다면 가비지 컬렉터의 수집대상이 됩니다.

 

 

예시2

외부 함수의 변수를 참조하는 내부 함수

var outer = function(){
	var a = 1;
    
    var inner = function(){
    	return ++a;
    };
    
    return inner;
};

var outerFunc = outer();
console.log(outerFunc()); // 2
console.log(outerFunc()); // 3

1) inner 함수를 반환하고 있습니다.

2) outerFunc가 선언될 때 outer의 실행 컨텍스트가 종료됩니다.

=> 이때 우리는 식별자 a와 inner가 가비지컬렉팅될 것이라 생각합니다.만 아닙니다.

 

3) outerFunc는 outer의 실행 결과인 inner 함수를 참조합니다.

4) 이후 outerFunc가 호출될 때 앞에서 반환한 inner 함수를 실행합니다.

5) inner가 실행될 때 inner의 lexical environment의 environmentRecord에서 식별자 a를 찾습니다.

=> 없기때문에 lexical environment의 outer-environment에서 상위 함수의 컨텍스트를 찾습니다.

=> inner 함수가 선언된 위치의 lexical environment가 참조복사되기 때문에 outer 함수의 lexical environment를 찾을 수 있습니다. 

 

6) outer의 lexical environment 의 environmentRecord에서 식별자 a를 찾습니다.

 

 

여기서 

"반환된 inner 함수가 실행될때 outer함수 실행 컨텍스트는 종료되었는데, 어떻게 outer함수의 lexical environment에 접근할 수 있는가?"

라는 의문이 들었지만, 코어 자바스크립트 책에서 친절히 설명해줍니다..

 

!!!

이것은 가비지 컬렉터 동작 방식때문이라고 합니다.

가비지 컬렉터는 값을 참조하는 변수가 하나라도 있다면 수집 대상에 포함하지 않습니다.

즉, outer의 컨텍스트가 종료된 뒤에 반환된 inner 함수에서 식별자 a를 참조하고 있고...

outerFunc로 선언되어있기 때문에 언제라도outerFunc가 실행될 수 있음을 인지합니다..

그렇기 때문에 반환된 inner 함수가 활성화 되었을때, 위의 예시대로(lexical environment^^) 동작해야하기 때문에 가비지 컬렉터의 대상이 되지 않으며.... 이것으로 인해 동작이 가능한 것입니다. 

 

그리고 위의 클로저의 예시로 return을 설명했지만

실제로는 외부로 전달이 return만을 의미하지 않으며... return 없이도 클로저가 발생하는 다양한 경우들이 있습니다. 

뭐... 콜백함수를 고차함수로 바꿔 클로저를 활용하는 방식이나 클로저를 통한 변수 보호, 커링함수 등....

=> 커링함수는 여러개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나누어 순차적으로 호출될 수 있게 체인형태로 구성된 함수를 말합니다. 각 호출될 때마다 인자들을 메모리에 저장하고 마지막 호출 시에 가비지컬렉팅 대상이 되는 방식으로 사용하는 방식입니다.

 

 

 

그래서..?

간단히 말하면 클로저는 어떠한 현상에 의해 메모리에 남겨진 변수들의 집합(?)입니다.

=> 어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상, 외부 함수의 LexicalEnvironment가 가비지 컬렉팅되지 않는 현상을 말합니다.

 

책에서는 더 다양한 예시와 표현들이 있지만,

가장 근접한 표현의 글이라고 작성되어있던것 중 하나의 글을 가져와보았습니다.

 

 

"함수를 선언할 때 만들어지는 유효범위가 사라진 후에도 호출할 수 있는 함수"

- 존레식, 자바스크립트 닌자 비급

 

 

클로저를 이해하고나면 드는 생각은,

메모리를 너무 낭비하는 것 아닌것인가? 라고 생각합니다. 또한 실제로 메모리 누수의 위험을 이유로 클로저를 지양하라고 합니다.

하지만 메모리 소모는 클로저의 본질적 특성입니다.

그리고 이 특성을 잘 활용하는 것이 중요합니다.

 

메모리 관리 중 하나의 예를 들면 null, undefined를 사용하는 것입니다. 

가비지 컬렉터를 이해하여 사용하지 않는 메모리에 대해 회수를 해주는것이 중요합니다.

function init(){
	name = null;
	count = null;
    date = null;
}

(null과 undefined에 대해서도 글한번 읽어보세요.)

 

 

과거에 썼던 글에 오류를 발견하고 수정했는데, 새로쓰게되었습니다..

과거의 글아 안녕.....☆

그때 생각했던 개념과는 많이 달라졌군요...

성장한 것이겠죠.. ? 공부는 이래서 쭉 해야하는 것 같습니다.

 


 

참조

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures

코어자바스크립트 : 핵심개념과 동작원리로 이해하는 자바스크립트 프로그래밍, 정재남

 

실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체입니다.

 

자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 선언된 변수를 위로 끌어올리고(호이스팅, hoisting)

외부환경 정보를 구성하고, this 값을 설정하는 등의 동작을 수행합니다.

 

실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체라고 했는데, 

동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고

이를 콜스택(call stack)에 쌓아두었다가 가장 위에 쌓인 컨텍스트와 관련있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장합니다.

 

'동일한 환경'은 하나의 실행 컨텍스트를 구성할 수 있는 방법으로 전역공간, eval() 함수, 함수 등이 있습니다.

*ES6에서는 블록에서도 새로운 실행 컨텍스트가 생성됩니다.

  • 전역공간
  • eval
  • 함수 실행

 

전역컨텍스트는 자바스크립트 코드가 실행하는 순간 콜 스택에 담깁니다.

브라우저에서 실행 명령없이도 자동으로 실행하는 것외에 일반적인 실행 컨텍스트와 다른점은 없습니다.

즉, 자바스크립트 파일이 열리는 순간 전역 컨텍스트가 활성화된다고 이해하면됩니다.

 

코드에 따라 실행 컨텍스트가 콜스택에 쌓이는 예시를 간단하게 보겠습니다.

function a(){

	function b(){
    	// ....
    };
    
    b();
};

a();

콜스택에서의 실행컨텍스트의 변화

*실행이 종료되면 해당하는 실행컨텍스트가 콜스택에서 제거됩니다.

 

실행 컨텍스트는 활성화되는 시점에 쌓여있는 아래의 컨텍스트들의 환경정보들을 수집해서 실행컨텍스트의 객체에 저장합니다.

즉 b 함수 실행 컨텍스트가 활성화될 시점에 a의 환경정보를 b 실행컨텍스트의 객체에 저장한다는 의미입니다.

 

이때 저장되는 환경정보는 3가지로 나누어볼 수 있습니다.

  • VariableEnvironment

    • 현재 컨텍스트 내의 식별자 정보, 외부 환경 정보.

    • 선언 시점의 LexicalEnvironment의 스냅샷으로 변경 사항은 반영되지 않는다.)

  • LexicalEnvironment

    • 처음에는 VariableEnvironment와 같지만 실시간으로 변경사항이 반영됨.

  • ThisBinding

    • this 식별자가 바라봐야하는 대상 객체

 

 

VariableEnvironment

최초 실행 시(선언 시점)의 스냅샷.

실행 컨텍스트를 생성할 때 VariableEnvironment에 정보를 먼저 담은 후 이를 복사하여 LexicalEnvironment를 만든다.

이후에는 LexicalEnvironment를 주로 사용한다.

 

구성

  • environmentRecord
  • outer-EnvironmentReference

 

 

LexicalEnvironment

실행 컨텍스트를 생성할 때 VariableEnvironment를 복사하여 만든다.

VariableEnvironment보다 주로 사용되며 복사해왔기 때문에 VariableEnvironment와 구성이 동일하다.

 

구성

  • environmentRecord
  • outer-EnvironmentReference

 

 

environmentRecord

현재 컨텍스트와 관련된 코드의 식별자 정보(매개변수의 이름, 함수 선언, 변수명등)들이 저장됩니다.

컨텍스트 내부 전체를 처음부터 끝까지 훑으며 순서대로 수집한다.

변수 정보를 모두 수집 후에도 코드들은 실행되기 전이다.

이때 자바스크립트엔진이 코드 실행전에 변수들을 알게되기 때문에 호이스팅이라는 개념이 나오며

이때 식별자의 값에 대해서는 environmentRecord가 수집하지않기때문에 값은 할당하는 자리 그대로에 위치해 있습니다. 

*실제 자바스크립트 엔진이 변수를 끌어올리는 것이 아니며, 끌어올린다라고 표현한 이유는 변수 정보를 수집하는 과정을 이해하기 쉽기 위해 대체한 가상의 개념이다.

 

 

outer-EnvironmentReference

스코프(Scope)는 식별자에 대한 유효범위입니다.

ES5에서는 전역공간을 제외하면 오직 함수에 의해서만 스코프가 생성됩니다.

'식별자의 유효범위'를 안에서 부터 바깥으로 차례로 검색해 나가는 것을 스코프 체인(Scope Chain)이라고 하며

이를 가능하게하는 것이 LexicalEnvironment의 outer-EnvironmentReference입니다.

outer-EnvironmentReference는 현재 호출된 함수가 선언될 당시(할성화될 당시)의 LexicalEnvironment를 참조합니다.

 

스코프체인

outer-EnvironmentReference는 자신이 선언된 시점의 LexicalEnvironment만 참조하고 있기 때문에, 식별자를 찾기위해서는 계속 찾아 올라가여 식별자를 찾습니다. 그리고 이것을 스코프체인이라고 합니다.

이때 가장 먼저 발견되는 식별자에만 접근이 가능합니다. 

*최상위에 선언된 동일한 이름의 식별자에 접근할 수없게 상단에 동일한 이름으로 식별자를 선언하는 것을 것을 변수 은닉화(variable shadowing)라고 합니다.

 

콜스택에서의 실행컨텍스트의 변화

즉, b라는 함수가 식별자를 찾는다면 b의 outer-EnvironmentReference에서 선언될 당시의 LexicalEnvironment를 참조하고(즉 a의 LexicalEnvironment임) 이때 a에서도 해당하는 식별자를 찾지못한다면

a 함수의 outer-EnvironmentReference에서 LexicalEnvironment를 참조하여(전역컨텍스트의 LexicalEnvironment임) 식별자를 찾습니다.

 

 

ThisBinding

실행컨텍스트의 ThisBinding에는 this로 지정된 객체가 저장됩니다.

만약 실행컨텍스트의 활성화 당시 this가 지정되지 않을 경우 전역객체가 this로 저장됩니다.

 


 

출처

코어 자바스크립트:핵심 개념과 동작 원리로 이해하는 자바스크립트 프로그래밍,(저자 정재남)

 

둘다 '없음'을 나타낼때 사용하고는 합니다.

의미는 같을 수 있으나 사용하는 목적을 정확히해야 혼란을 줄일 수 있습니다.

 

 

undefined

undefined는 사용자가 명시적으로 지정할 수도 있지만, 

값이 존재하지 않을때 자바스크립트 엔진이 자동으로 부여하는 경우도 있습니다.

 

1) 값을 대입하지 않은 변수(데이터 영역의 주솟값이  없는 식별자)에 접근할때나

2) 객체 내부에 존재하지 않는 프로퍼티에 접근하려고할때,

3) return 문이 없거나 호출되지 않는 함수의 실행결과에서 자바스크립트 엔진은 undefine를 반환합니다.

 

 

1) 값을 대입하지 않았을 경우

var name;

console.log(name); // undefined

개략적이미지 - 값을 대입하지 않았을 경우

 

2) 객체 내부에 존재하지 않는 프로퍼티에 접근

var person = {
	name : '홍길동'
};

console.log(person.name); // 홍길동
console.log(person.age); // undefined

 

3) return 문이 없거나 호출되지 않는 함수의 실행결과

var getName = function(){};
var hoon = getName('hoon');

console.log(hoon); // undefined

 

* 만약 return 값이 있다면....

var getName = function(name){
	return name;
};
var hoon = getName('hoon');

console.log(hoon); // hoon

 

자바스크립트 엔진에서 비어있는 경우 undefined를 반환해준다고했는데, 배열일 경우에는 empty를 출력합니다.

이때 사용자가 undefined를 넣었을때와 empty를 반환할때를 구분할 수 있어야합니다.

var arr = new Array(3);

console.log(arr); // [empty x 3]

 

식별자 arr는 배열의 크기를 확보했지만 undefined 조차 할당되지 않은 상태여서 [empty x 3] 이 출력됩니다. 

empty는 순회와 관련해서 배열 메서드에서 제외되는 경우가 있습니다.

var arr = new Array(3);
arr.filter(function(o) { return !o }); // []

var arr2 = [undefined, undefined, undefined];
arr2.filter(function(o) { return !o }); // [undefined, undefined, undefined]

 

사용자가 추가한 undefined는 하나의 데이터이며 '비어있음'을 의미하고 하나의 값으로 동작합니다.

그래서 프로퍼티나 배열의 요소에서 고유의 키값(프로퍼티 이름)이 실존하게 되며 순회의 대상이 될 수 있습니다.

 

하지만

사용자가 추가한 undefined가 아닌 자바스크립트 엔진이 반환한 undefined는 해당 프로퍼티나 배열의 키(index)가 존재하지 않기 때문에 문자 그대로 값이 없음을 나타내는 것입니다.

 

 

null

개발자가 undefined를 직접할당하는 것은 디버깅 시 자바스크립트 엔진이 반환하는 값과 혼란스러울 수 있으므로 명시적으로 '비어있음'을 나타내고 싶을때에는 null을 사용하면됩니다.

* typeof null은 object이므로 주의해야합니다.

*식별자에 undefined가 자동으로 할당된다고 말하는 것은 실제 동작에 비교해서 정확하지 않은것입니다.

식별자에 할당된 값은 없고, 추후 식별자에 접근하려고했을때 undefined가 반환된다고하는것이 정확합니다.

 


출처

코어 자바스크립트:핵심 개념과 동작 원리로 이해하는 자바스크립트 프로그래밍,(저자 정재남)

 

데이터 타입의 종류

기본형 타입(Primitive Type) 참조형 타입(Reference Type)

숫자(Number)
문자열(String)
불리언(Boolean)
null
undefined
심볼(Symbol)

객체(Object)
배열(Array)
함수(Function)
날짜(Date)
정규표현식(RegExp)
Map
WeakMap
Set
WeakSet

 

 

기본형 타입(Primitive Type)

기본형 타입의 종류에는 숫자, 문자열, 불리언, null, undefined, symbol이 있습니다.

일반적으로 기본형은 '할당이나 연산시 데이터가 복제'된다고 알려져있습니다.

 

 

기본형 타입의 메모리 저장 방식

*메모리 할당영역, 주솟값에 대한것은 이해를 돕기위한 개략적인 이미지들이 등장합니다.

 

메모리 할당 시 두 영역을 사용한다고 생각하면 쉽습니다.

  • 식별자가 할당되는 변수 영역

  • 데이터 값이 담기는 데이터 영역

변수 영역에는 식별자와 데이터 영역의 주솟값으로 이루어져있습니다.

데이터 영역에는 데이터가 담겨있습니다.

 

 

예시1) 변수 선언

var name = '홍길동';

개략적이미지 - 기본형타입 예시1. 변수선언

변수 영역(1002)에 식별자(변수명)를 name으로 할당합니다.

데이터 영역(5000)에 문자열 '홍길동' 데이터를 할당합니다.

식별자 name의 변수 영역에 데이터 영역의 주솟값을 값에 연결해줍니다.

 

사용자가 식별자 name을 호출했을때,

해당 변수 영역 값에 연결된 주솟값(5000)에 담긴 데이터가 반환됩니다.

 

 

예시2) 데이터 재할당

var name = '홍길동';
name = '고길동';

개략적이미지 - 기본형타입 예시2. 데이터 재할당

새로운 데이터이므로 비어있는 데이터 영역(5001)에 문자열 '고길동'을 할당합니다.

식별자 name에 변수 영역에 해당하는 데이터 영역의 주솟값(@5000 -> @5001)으로 변경해줍니다.

 

 

예시3) 변수에 변수를 대입

var name = '홍길동';
var name2 = name;

개략적이미지 - 기본형타입 예시3. 변수에 변수를 대입1

비어있는 변수 영역(1003)에 식별자 name2를 할당하고

name2의 데이터 주소에 name의 주솟값(5000)을 가져와 할당합니다.

name2의 데이터가 변경되는 부분을 이어서보겠습니다.

var name = '홍길동';
var name2 = name;
name2 = '고길동';

개략적이미지 - 기본형타입 예시3. 변수에 변수를 대입2

새로운 문자열 '고길동'을 비어있는 데이터 영역(5001)에 할당하고 식별자 name2의 주솟값을 변경해줍니다.

 

 

?????!

여기서 궁금한 점이 등장합니다.

데이터 영역에 문자열 '홍길동'을 넣었다가 문자열 '고길동'으로 변경했을때,

해당 영역의 데이터를 수정하지 않고 오ㅐ? 새로운 메모리 영역을 사용하는가?

이것을 이해하기위해서는 불변성을 이해해야합니다.

 

 

불변성(Immutability)

단순히 정의만을 말하자면 '변하지 않는 성질'이라고 할 수 있습니다.

하지만 불변성이 해당하는 부분이 어디인지를 확실히 이해해야합니다.

불변성은 변수와 상수의 개념으로 말하는 것이 아닙니다.

 

변수와 상수변수 영역 메모리에 데이터 할당 후 재할당이 되는지에 대한 여부로 구분되는 것이며

불변성데이터 영역의 메모리에 대한 것 입니다.

 

예시로 간단히 보겠습니다.

var name = '홍길동';
name = '고길동';

 

변수

개략적이미지 - 불변성 예시1. 변수

식별자 name의 변수 영역에 값이 문자열 '홍길동'에서 '고길동'으로 재할당이 되었으므로

해당 데이터 영역 주솟값으로 변경해줍니다. 

(5000 -> 5001)

변수는 이처럼 데이터 영역의 주솟값 재할당이 가능합니다. (재할당)

 

상수

개략적이미지 - 불변성 예시2. 상수

상수는 데이터 주솟값 재할당이 안되므로

식별자 name의 변수 영역에 데이터 값 변경이 불가능합니다.

 

불변성

개략적이미지 - 불변성 예시3. 불변성1

불변성은 식별자 변수 영역에 해당하는 데이터 값의 재할당이 기준이아니고

데이터 영역의 데이터가 변경이 가능한지에 대한 여부입니다.

즉 문자열 '홍길동'에서 '고길동'으로 변경되었을때, 데이터 영역의 데이터는 변경할 수 없습니다.

 

개략적이미지 - 불변성 예시3. 불변성2

불변성에 의해 비어있는 데이터 영역에 새로 문자열 '고길동'을 할당 한 후

식별자 name의 데이터 주솟값이 재할당됩니다.

 

위의 예시에서 알 수 있듯,

데이터 영역의 데이터는 한번 생성되었을 경우 수정이 안되며(불변성)

새로운 데이터일 경우 비어있는 데이터 영역에 새로 할당됩니다.

그리고 새로 할당된 데이터 영역의 주솟값을 변수 영역의 데이터 주솟값으로 재할당하는 것입니다.

즉, 데이터 영역의 변경이란 새로 만드는 동작에서만 이루어집니다.

 

불변성은 왜 필요한거지?라는 의문이 또 한번 머리에 스쳐지나갈 수 있습니다.

간단하게, 메모리 저장에 대한 이야기를 해보겠습니다.

메모리에 데이터를 저장하기 위해서는 메모리 공간을 선행으로 확보해야합니다.

불변성이 없다고 생각했을때,

처음 저장한 데이터의 크기보다 더 큰 데이터를 '재할당' 해야한다면 어떤일이 생길까요?

데이터 공간을 재확보해야하는 일이 생깁니다.

그리고 이 재확보작업을 하게되면 뒤에 저장된 메모리들의 공간이 뒤로 밀리는 현상이 생기고 

이 현상으로 인해 각각의 주솟값들을 식별자에 다시 연결해야하는 작업이 발생할 수 있습니다.

위와 같은 이유로 불변성은 효율적으로 데이터를 저장하기 위해 생겼습니다.

 

 

참조형 타입(Reference Type)

참조형 타입의 종류는 객체, 배열, 함수, 날짜, 정규표현식, Map, WeakMap, Set, WeakSet이 있다.

일반적으로 참조형은 '참조된다'고 알려져있습니다.

 

 

참조형 타입의 메모리 저장 방식

*메모리 할당영역, 주솟값에 대한것은 이해를 돕기위한 개략적인 이미지들이 등장합니다.

 

 

예시1) 변수 선언

var person = {
	name : '홍길동',
    age : 123
};

 

개략적이미지 - 참조형타입 예시1. 변수선언

기본형타입과 마찬가지로 변수 영역, 데이터 영역이 존재합니다.

식별자 person 변수 영역에 데이터 영역 주솟값을 연결해줍니다.

하지만 참조형타입은 영역이 하나 더 존재합니다.

'객체의 변수(프로퍼티) 영역'입니다.

 

데이터 영역 주솟값에 데이터가 바로 할당되는 것이 아니라 객체의 프로퍼티 영역의 주솟 값(7000~7001)이 연결됩니다.

그리고 해당 객체의 프로퍼티 영역에 데이터 영역의 주솟값을 연결해줍니다.(name-5002 / age-5003)

 

위에서 데이터 영역은 불변하다고 말했던 것처럼 참조형 데이터도 데이터 영역은 불변하다고 할 수 있습니다.

하지만  

기본형 타입은 불변성을 띄고 참조형 타입은 불변하지 않다(가변성)고들 말한다고 하는데,

그 이유를 짚어보겠습니다.

 

 

예시2) 참조형타입은 가변성을 띄는가

var person = {
	name : '홍길동',
    age : 123
};

person.name = '고길동';

개략적이미지 - 참조형타입 예시2. 참조형타입은 가변성을 띄는가

식별자 person의 변수 영역의 값과 데이터 영역(5000)의 값이 변경되지 않았습니다.

하지만 객체의 프로퍼티 영역의 데이터 주솟값이 변경된 것이 확인 되는데, 

이같은 부분을 보고 가변성을 띈다고 하는 것입니다.

 

간단히 말해..

person의 데이터 영역은 불변성을 띄지만,

person의 객체의 프로퍼티 영역은 가변성을 띈다고 할 수 있습니다.

 

 

예시3) 참조형 타입이 참조인이유

var person = {
	name : '홍길동'
};

var person2 = person;

개략적이미지 - 참조형타입 예시3. 참조형 타입이 참조인 이유1

식별자 person의 값을 person2에 대입하게되는 경우에 위와 같이 같은 데이터 영역의 주솟값을 가지게 됩니다.

이 부분은 기본형 타입과 참조형 타입 둘 다 동일합니다.

두 타입의 차이는 데이터 영역, 객체의 프로퍼티 영역에서 확인할 수 있습니다.

아래와 같이 식별자 person2의 name 프로퍼티의 데이터를 수정하게 될 경우 생기는 변화에 대해 확인해보겠습니다. 

var person = {
	name : '홍길동'
};

var person2 = person;
person2.name = '고길동';

개략적이미지 - 참조형타입 예시3. 참조형 타입이 참조인 이유2

변수 영역의 주솟값이나 데이터 영역의 주솟값은 변경되지 않습니다.

name 프로퍼티만 변경된 것이므로 해당 객체의 프로퍼티 영역에서 데이터 주솟값을 변경합니다.

즉, 5002에서 5003으로 변경합니다.

이때 해당 객체의 프로퍼티 영역의 주솟값만 변경하므로 변수 영역, 데이터 영역을 참조하고 똑같이 참조하고 있던

person역시 영향을 받습니다.

var person = {
	name : '홍길동'
};

var person2 = person;
person2.name = '고길동';

console.log(person.name); // '고길동'

 

 

예시4) 객체끼리 참조하지 못하게 하려면

var person = {
	name : '홍길동',
    age : 123
};

var person2 = person;

person2 = {
	name : '홍길동'
    age : 300
};

개략적이미지 - 참조형타입 예시4. 객체끼리 참조하지 못하게 하려면

식별자 person의 객체를 person2에 대입하고나서 새로운 객체로 데이터 자체를 변경해버리면 불변성을 유지할 수 있습니다.

객체 자체를 재 선언해버리면 새로운 객체의 프로퍼티 영역이 생기게 되고 이때 연결된 기존 객체 프로퍼티 영역의 주솟값이 변경되어 버리므로 서로 참조하지 않습니다.

(객체의 프로퍼티영역이 각각 생김)

 

즉, 참조형 데이터가 가변성을 띈다라고 말할수 있는 부분은

참조형데이터 자체를 변경할 때가 아닌 내부 프로퍼티를 변경하는경우에만 성립됩니다.

(참조되는 객체의 불변성을 지키기위해 복사하여 사용하기도합니다.)

 

하지만 어떠한 데이터 타입이든 변수에 할당하기 위해서는 데이터를 직접 할당하는 것이 아닌 데이터 영역의 주솟값을 복사하기 때문에 자바스크립트의 모든 데이터 타입은 참조형 데이터일 수 밖에 없습니다. 

기본형도 결국 주솟값을 참조하는 형태입니다.

다만 기본형은 주솟값을 데이터 영역에서 한번 만 복사해오고 참조형은 객체의 프로퍼티 영역 한단계를 더 거치는 차이가 있습니다.

 


출처

 

'~2022 > FE-개발 개념' 카테고리의 다른 글

클로저(Closure)  (0) 2021.01.01
this  (0) 2020.12.09
실행 컨텍스트(Execution context)  (0) 2020.12.09
undefined와 null  (0) 2020.12.09
변수(Variable)와 식별자(Identifier)  (1) 2020.12.08
Chrome 80 SameSite 속성 동작 변경  (0) 2020.06.26
[jQuery기초] 버전별 특성  (0) 2020.06.18
[jQuery기초] jQuery란?  (0) 2020.06.17

+ Recent posts