-
4장) 변수와 스코프, 메모리Java Script/프론트앤드 개발자를 위한 자바스크립트 2020. 6. 26. 21:51반응형
변수와 스코프, 메모리
JS는 느슨한 변수 타입 ~> 변수는 특정 시간 특정 값을 가리키는 문자 그대로 이름일 뿐
원시 값과 참조 값
- 원시 값 : 단순 데이터 (스택 메모리에 저장)
- 참조 값 : 여러 값으로 구성되는 객체를 가리킴(힙 메모리에 저장)
- 변수에 값을 할당 ~> JS 엔진이 원시 데이턴지 참조 데이턴지 판단
- JS는 메모리 위치에 직접 접근하는 것을 허용 X ~> 객체를 조작할 때는 객체 자체가 아닌 객체에 대한 참조를 조작하는 것
동적 프로퍼티
참조 값을 다룰 때는 언제든 프로퍼티와 메서드를 추가/삭제 가능
var person = new Object(); person.name = "Zin0"; alert(person.name); // Zin0 var name = "Zin0"; name.age = 28; alert(name.age); // undefined
원시 값에는 프로퍼티가 없지만 추가하려해도 에러 X.
다만, 동적으로 프로퍼티를 추가할 수 있는 값은 참조 값 뿐이다.
값 복사
- 원시 값을 다른 변수로 복사할 때는 저장된 값을 새로 생성해서 새로운 변수에 복사
위의 두 변수는 5라는 값을 각각 갖는다. (다른 메모리 위치)var num1 = 5; var num2 = num1;
- 참조 값을 복사할 때는, Heap 공간에 저장된 객체를 가리키는 포인터 값을 복사해줌.
같은 객체를 가리키기 때문에, obj2에서 해당 프로퍼티에 접근하면, Zin0 값을 가져온다.var obj1 = new Object(); var obj2 = obj1; obj1.name = "Zin0"; alert(obj2.name); // Zin0
- 원시 값을 다른 변수로 복사할 때는 저장된 값을 새로 생성해서 새로운 변수에 복사
매개변수 전달
ECMAScript의 함수 매개변수는 모두 값으로 전달
매개변수의 값은 지역 변수에 복사된다. ~> 이름 붙은 매개변수로 복사되면서, ECMAScript에서는 arguments 객체의 한 자리를 차지한다. 원시 값과 참조 값에 따라 값을 복사함.
function addTen(num) { num +=10; return num; } var count = 20; var result = addTen(count); alert(count); // 20 alert(result); // 30 function setName(obj) { obj.name = "Zin0" } var person = new Object(); setName(person); alert(person.name); // Zin0 function setName(obj) { obj.name = "Zin0"; obj = new Object(); obj.name = "JY"; } var person = new Object(); setname(person); alert(person.name); // Zin0
매개 변수가 원시 값일 경우에는 값을 복사하여 지역변수에 저장하고 return함으로 변수값에 변화가 없음.
참조 값의 경우 매개변수가 참조 형태로 전달된 것이 아니라, Heap 공간에 저장된 객체를 가리키는 포인터 값을 복사해 준 것이다. ECMAScript의 함수 매개변수는 지역 변수와 다를 것 없다고 생각하면 편하다.
타입 판별
- typeof 연산자는 변수가 원시 타입인지 파악하기 최상.
- 값이 객체거나 null이면 "object"를 반환
- 함수에 사용하면, "function"을 반환
- instanceof 연산자는 주어진 참조 타입의 인스턴스인지 파악
alert(person instanceof Object); // person 변수가 Object의 인스턴스인가? alert(colors instanceof Array); // colors 변수가 Array의 인스턴스인가? alert(pattern instanceof RegExp); // pattern 변수가 RegExp의 인스턴스인가?
- 원시 값은 Object의 인스턴스가 아니기 때문에 항상 false 값을 리턴
- typeof 연산자는 변수가 원시 타입인지 파악하기 최상.
실행 컨텍스트와 스코프
실행 컨텍스트(Execution Context) - '컨텍스트'라고 부름.
실행 컨텍스트는 다른 데이터에 접근할 수 있는지, 어떻게 행동하는지를 규정한다.
변수 객체가 연결되어 있으며, 해당 컨텍스트에서 정의된 모든 변수와 함수는 이 객체에 존재.
코드에서 직접 접근할 수는 없지만, 이면에서 데이터를 다룰 때 이 객체를 이용
전역 컨텍스트 - 가장 바깥쪽에 존재하는 실행 컨텍스트
- ECMAScript 구현 황경에 따라 이름이 달라진다.
- 웹 브라우저에서는 window라 부르고, 이 window 객체에 전역 변수와 함수가 모두 window 객체의 프로퍼티 및 메서드로 생성됨.
- 실행 컨텍스트는 포함된 코드가 모두 실행될 때 파괴 (Life Cycle)
- 내부에서 정의된 변수와 함수도 함께 파괴됨
- 전역 컨텍스트는 애플리케이션이 종료될 때 파괴된다.
함수 호출 ~> 독자적인 실행 컨텍스트 생성 ~> 스택에 쌓임.
- 함수 실행이 끝나면 컨텍스트를 스택에서 꺼내고 컨트롤을 이전 컨텍스트에 반환
컨텍스트에서 코드를 실행하면, 변수 객체에 스코프 체인이 만들어 진다.
실행 컨텍스트가 접근할 수 있는 모든 변수와 함수에 순서를 정의
스코프 체인의 앞쪽은 항상 코드가 실행되는 컨텍스트의 변수 객체
컨텍스트가 함수라면 활성화 객체를 변수 객체로 사용
활성화 객체는 변수 하나로 시작(전역 컨텍스트에 존재X)
- 전역 컨텍스트에 도달할 때 까지, 하위 컨텍스트부터 부모 컨텍스트로 계속 이동
식별자를 찾을 때는 스코프 체인을 따라가면서 식별자 이름 검색
~> 찾을 수 없을 때는 에러 발생
var color = "blue";function changeColor() {
var anotherColor = "red"; function swapColors() { var tempColor = anotherColor; anotherColor = color; color = tempColor; // color, anotherColor, tempColor 모두 접근 가능 } // color, anotherColor 접근 가능, tempColor는 불가능 swapColors();
}
// color만 접근 가능
changeColor();
내부 컨텍스트는 스코프 체인을 통해 외부 컨텍스트 전체에 접근 가능하지만, 외부 컨텍스트는 내부 컨텍스트에 대해 전혀 알 수 없다.
스코프 체인 확장
실행 컨텍스트에는 전역 컨텍스트와 함수 컨텍스트 두 타입이 존재(eval() 호출 시, 생성되는 세 번째 타입이 있기는 하다.)
스코프 체인을 확장하는 방법
스코프 체인 앞 부분에 임시로 변수 객체를 만들며, 코드 실행 후에 사라진다.
try-catch 문의 catch 블록 - 에러 객체를 선언하는 변수 객체가 생성
with 문 - 객체가 스코프 체인에 추가
function buildUrl() {var qs = "?debug=true"; with(location) { var url = href + qs; } return url;
}
▲ with 문이 location 객체에 적용됨 ~> location 객체가 스코프 체인에 추가됨.
href 변수는 location.href를 참조, qs는 buildUrl()함수 컨텍스트의 변수 객체에 들어있다. 따라서 with문 내부에서 선언한 변수 url은 함수 컨텍스트로 편입 ~> 함수 값으로 반환 가능
JS에는 블록 레벨 스코프가 없다
if(true) { var color = "blue"; } alert(color); // blue for(var i=0; i<10; i++) { doSth(1); } alert(i); //10
JS에서는 변수를 선언할 때 현재 실행 컨텍스트에 추가한다. 따라서 블록 레벨 안의 변수를 이용 가능
변수 선언
- var를 사용해 선언한 변수는 자동으로 가장 가까운 컨텍스트에 추가된다.
- 함수 내부 -> 함수 로컬 컨텍스트
- with문 내부 -> 함수 컨텍스트
function add(num1, num2) {
}var sum = num1 + num2; return sum;
var result = add(10,20); //30
alert(sum); // error
- var를 사용해 선언한 변수는 자동으로 가장 가까운 컨텍스트에 추가된다.
식별자 검색
컨텍스트 안에서 식별자 참조 -> 스코프 체인 앞에서부터 식별자를 탐색
식별자 이름을 찾으면 검색 멈추고 변수를 설정
전역 컨텍스트의 변수 객체에서도 못찾으면, 정의되지 않을 것으로 판단.
var color = "blue";
function getColor() {return color;
}
alert(getColor()); // bluefunction getColor2() {
val color = "red"; return color;
}
alert(getColor()); // red
- 가비지 콜렉션
- 코드 실행 중에 메모리를 관리함 ~> 필요한 메모리 자동 할당, 더 이상 사용 X 메모리는 자동 회수.
- 코드 실행 중에 특정 시점에서 메모리를 회수하도록 지정할 수 있음
- 더이상 사용하지 않는 변수를 식별하는 기준은 두 가지 방법을 흔히 사용한다.
- 1 - 표시하고 지우기
- JS에서 가장 널리 쓰이는 가비지 컬렉션 방법
- 변수가 특정 컨텍스트 안에서 사용할 것으로 정의 ~> 그 컨텍스트 안에 있는 것으로 표시
- 컨텍스트 안에 존재하는 변수의 메모리는 해제하면 안된다. 실행 중에 사용될 가능성이 있기 때문에. 변수가 컨텍스트 밖으로 나가면 컨텍스트 밖에 있는 것으로 표시
- 가비지 컬렉터가 작동하면 메모리에 저장된 변수 전체에 표시를 남긴다.
- 그 다음 컨텍스트에 있는 변수와, 컨텍스트에 있는 변수가 참조하는 변수에서 표시를 지운다.
- 위의 두 과정을 거친 다음에도 표시가 남아있는 변수는 삭제해도 안전함.
- 2 - 참조 카운팅
- 널리 쓰이지는 않는다.
- 각 값이 얼마나 많이 참조됐는지 추적
- 변수 선언하고 참조 값이 할당되면 참조 카운트는 1
- 다른 변수가 같은 값을 참조하면 카운트 증가
- 해당 값을 참조하는 변수에 다른 값 할당 ~> 원래 값의 참조 카운트가 줄어든다.
- 참조 카운트가 0이 되면 해당 값에 접근할 방법 X, 메모리 회수해도 안전
- 순환 참조
- 객체 A와 B가 서로를 참조하는 경우
- 표시하고 지우는 방식은 함수 실행이 끝날 때, 두 변수가 모두 스코프를 벗어남으로 문제X
- 참조 카운팅은 서로 참조 카운트 2를 가지기 때문에, 함수 실행 이후에도 두 변수 존재.
- IE 8을 포함한 이전 버전은 BOM과 DOM 객체들이 c++의 COM(구성 요소 객체 모델)로 구현됨. COM 객체는 가비지 콜렉션에 참조 카운팅 방식 이용. ~> 순환 참조 문제
- IE 9는 BOM과 DOM 객체를 JS 객체로 만들어서 해결
- 성능
- 가비지 컬렉션을 실행하는 타이밍이 중요함 (IE가 가비지 컬렉터를 너무 자주 실행해서 성능 문제를 일으킨다..)
- 메모리 관리
- 가비지 컬렉션을 지원하는 프로그래밍 환경에서는 메모리 관리에 신경쓰지 않아도 된다.
- 하지만, JS라는 환경에서 메모리 관리와 가비지 콜렉션은 다름.
- 웹브라우저에서 사용할 수 있는 메모리는 일반적인 데스크톱 애플리케이션 가용 메모리에 비해 매우 적기 때문. ~> JS가 시스템 메모리를 전부 사용해서 OS 다운되는 현상 방지 위해
- 따라서 필요없어진 데이터는 null을 할당하여 참조를 제거하는 편이 좋다.
- 주로 전역 변수 및 전역 객체의 프로퍼티에 적용
- 지역 변수는 컨텍스트를 빠져나가는 순간 자동으로 참조 제거
반응형'Java Script > 프론트앤드 개발자를 위한 자바스크립트' 카테고리의 다른 글
프론트앤드 개발자를 위한 JS - 3장 (0) 2020.05.03 프론트엔드 개발자를 위한 JS - 2장 (2) 2020.04.24 프론트엔드 개발자를 위한 JS - 1장 (1) 2020.04.22