-
- 복제의 방식
- 기본형 : 값이 담긴 주소값을 바로 복제
- 참조형 : 값이 담긴 주소값들로 이루어진 묶음을 가리키는 주소값을 복제
- 불변성의 여부
- 기본형 : 불변성을 띔
- 참조형 : 불변성을 띄지 않음
메모리, 데이터
- 비트
- 컴퓨터가 이해할 수 있는 가장 작은 단위
- 0과 1을 가지고 있는 메모리를 구성하기 위한 작은 조각
- 이 조각들이 모여서 메모리가 만들어진다.
- 바이트
- 0과 1만 표현하는 비트를 모두 찾기는 부담
- 1→2→…→8개(새로운 단위:byte)
- 메모리(memo_ry): byte단위로 구성 모든 데이터는 byte 단위의 식별자인 메모리 주소값을 통해서 서로 구분
식별자, 변수
var testValue = 3
변수 = 데이터
식별자 = 변수명
변수 선언과 데이터 할당
/** 선언과 할당을 풀어 쓴 방식 */ var str; str = 'test!'; /** 선언과 할당을 붙여 쓴 방식 */ var str = 'test!';
- 값을 바로 변수에 대입하지 않는 이유(=무조건 새로 만드는 이유)
- 이미 입력한 문자열이 길어진다면? 숫자는 항상 8byte로 고정이지만, 문자는 고정이 아니다.(영문:1.한글2)
- 이미 1003주소에 할당된 데이터를 변환하려 할 때 훨씬 더 큰 데이터를 저장하려한다면, 1004주소 이후부터 저장되어잇는 모든 데이터를 오른쪽으로 다 미뤄야한다.
- 메모리의 효율적 관리
- 똑같은 데이터를 여러번 저장해야한다면? 1만개의 변수를 생성해서 모든 변수에 숫자 1을 할당하는 상황을 가정했을때 모든 변수를 별개로 인식한다고 하면, 1만개의 변수 공간을 확보해야 한다.
- 바로 대입하는 숫자형은 8바이트 고정으로 1만개 * 8byte = 8만 byte
- 변수 영역에 별도 저장하게되면 변수영역 : 2바이트 1만개 = 2만바이트만 차지하게된다. 💡변수영역은 데이터 영역보다 작기때문에 2로 가정함.
- 데이터 영역 :8바이트 1개 = 8바이트
- 변수영역에 별도로 저장하면 총 2만 8바이트만 든다.
- 똑같은 데이터를 여러번 저장해야한다면? 1만개의 변수를 생성해서 모든 변수에 숫자 1을 할당하는 상황을 가정했을때 모든 변수를 별개로 인식한다고 하면, 1만개의 변수 공간을 확보해야 한다.
기본형 데이터와 참조형 데이터
- 메모리를 기준으로 다시한번 생각해보는 두 가지 주요 개념
- 변수 vs 상수
- 변수 : 변수 영역 메모리를 변경할 수 있음
- 상수 : 변수 영역 메모리를 변경할 수 없음
- 불변하다 vs 불변하지 않다
- 불변하다 : 데이터 영역메모리를 변경할 수 없음
- 불변하지 않다 : 데이터 영역 메모리를 변경할 수 있음
- 변수 vs 상수
- 불변값과 불변성
- // a라는 변수가 abc에서 abcdef가 되는 과정을 통해 불변성을 유추해봅시다! // 'abc'라는 값이 데이터영역의 @5002라는 주소에 들어갔다고 가정할게요. var a = 'abc'; // 'def'라는 값이 @5002라는 주소에 추가되는 것이 아니죠! // @5003에 별도로 'abcdef'라는 값이 생기고 a라는 변수는 @5002 -> @5003 // 즉, "변수 a는 불변하다." 라고 할 수 있습니다. // 이 때, @5002는 더 이상 사용되지 않기 때문에 가비지컬렉터의 수거 대상이 됩니다. a = a + 'def';
- 참조형 데이터의 변수 할당 과정⬇️ 강의를 참고해서 아래 표를 직접 작성해보세요!주소 1001 1002 1003 1004
데이터 objt/@7103~ 주소 5001 5002 5003 5004 데이터 1 ‘bbb’ 데이터 a/@5001 b/@5002 - 데이터 영역에 저장된 값은 여전히 계속 불변값이지만 obj1을 위한 별도 영역은 얼마든지 변경이 가능해서 참조형 데이터를 흔히, 불편하지 않다(=가변하다)라고 한다.
- var obj1 = { a: 1, b: 'bbb', }; // 데이터를 변경해봅시다. obj1.a = 2;
- // 참조형 데이터는 별도 저장공간(obj1을 위한 별도 공간)이 필요합니다! var obj1 = { a: 1, b: 'bbb, };
- 중첩객체의 할당
⬇️ 강의를 참고해서 아래 표를 직접 작성해보세요!주소 1001 1002 1003 1004 1005 …var obj = { x: 3, arr: [3, 4, 5], } // obj.arr[1]의 탐색과정은 어떻게 될까요? 작성하신 표에서 한번 찾아가보세요!
데이터 obj/@7103~ 주소 5001 5002 5003 5004 5005 … 데이터 3 4 5 데이터 x/@5001 arr/@8104~ 데이터 0/@5001 1/@5002 2/@5003 - 참조카운트란 무엇일까요?</aside>
- <aside> 💡 객체를 참조하는 변수나 다른 객체의 수를 나타내는 값입니다. 참조 카운트가 0인 객체는 더 이상 사용되지 않으므로, 가비지 컬렉터에 의해 메모리에서 제거됩니다.
- 가비지컬렉터(GC, Garbage Collector)</aside>
- <aside> 💡 더 이상 사용되지 않는 객체를 자동으로 메모리에서 제거하는 역할을 합니다. 자바스크립트는 가비지 컬렉션을 수행함으로써 개발자가 명시적으로 메모리 관리를 하지 않아도 되도록 지원합니다. 자바스크립트 엔진에서 내부적으로 수행되며, 개발자는 가비지 컬렉션에 대한 직접적인 제어를 할 수 없습니다.
- 변수 복사의 비교
주소 1001 1002 1003 1004 1005 …// STEP01. 쭉 선언을 먼저 해볼께요. var a = 10; //기본형 var obj1 = { c: 10, d: 'ddd' }; //참조형 // STEP02. 복사를 수행해볼께요. var b = a; //기본형 var obj2 = obj1; //참조형
데이터 a/@5001 obj1/@7103~ b/@5001 obj2/@7103~ 주소 5001 5002 5003 5004 5005 데이터 10 ‘ddd’ 데이터 c/@5001 d/@5002 - 복사 이후 값 변경(객체의 프로퍼티 변경
주소 1001 1002 1003 1004 1005 …// STEP01. 쭉 선언을 먼저 해볼께요. var a = 10; //기본형 var obj1 = { c: 10, d: 'ddd' }; //참조형 // STEP02. 복사를 수행해볼께요. var b = a; //기본형 var obj2 = obj1; //참조형 b = 15; obj2.c = 20;
데이터 a/@5001 obj1/@7103~ b/@5003 obj2/@7103~ 주소 5001 5002 5003 5004 5005 데이터 10 ‘ddd’ 15 20 데이터 c/@5004 d/@5002 - 기본형
- 숫자 15라는 값을 데이터 영역에서 검색 후 없다면 생성
- 검색한 결과주소 또는 생성한 주소를 변수 영역 b에 갈아끼움
- a와 b는 서로 다른 데이터 영역의 주소를 바라보고 있기 때문에 영향 없음
- 참조형
- 숫자 20이라는 값을 데이터 영역에서 검색 후 없다면 생성
- 검색한 결과주소 또는 생성한 주소 obj2에게 지정되어 있는 별도 영역(7103~)에 갈아끼움
- obj1도 똑같은 주소를 바라보고 있기 때문에 obj1까지 변경이 됨
데이터 a/@5001 obj1/@7103~ b/@5003 obj2/@78104~ 주소 5001 5002 5003 5004 5005 데이터 10 ‘ddd’ 15 20 데이터 c/@5001 d/@5002 데이터 c/@5004 d/@5002
위 코드를 아래코드와 같이 개선할 수 있다.다만 위와 같은 방법은 속성이 적을땐 간단하게 변경이 가능하지만 속성이 많아질경우 문제가 된다.<패턴><적용>얕은 복사의 경우 중첩된 객체에 대해서는 완벽한 복사를 할 수 없다.// user 객체를 생성 var user = { name: 'wonjang', gender: 'male', }; // 이름을 변경하는 함수, 'changeName'을 정의 // 입력값 : 변경대상 user 객체, 변경하고자 하는 이름 // 출력값 : 새로운 user 객체 // 특징 : 객체의 프로퍼티(속성)에 접근해서 이름을 변경했네요! -> 가변 var changeName = function (user, newName) { var newUser = user; newUser.name = newName; return newUser; }; // 변경한 user정보를 user2 변수에 할당하겠습니다. // 가변이기 때문에 user1도 영향을 받게 될거에요. var user2 = changeName(user, 'twojang'); // 결국 아래 로직은 skip하게 될겁니다. if (user !== user2) { console.log('유저 정보가 변경되었습니다.'); } console.log(user.name, user2.name); // twojang twojang console.log(user === user2); // true
- 얕은복사 : 바로 아래 단계의 값만 복사 문제점 : 중첩된 객체의 경우 참조형 데이터가 저장된 프로퍼티를 복사할 떄 주소값만 복사한다.
위와 같은 코드처럼 객체안에 객체는 가져올 수 가없다.var user = { name: 'wonjang', urls: { portfolio: '<http://github.com/abc>', blog: '<http://blog.com>', facebook: '<http://facebook.com/abc>', } }; var user2 = copyObject(user); user2.name = 'twojang'; // 바로 아래 단계에 대해서는 불변성을 유지하기 때문에 값이 달라지죠. console.log(user.name === user2.name); // false // 더 깊은 단계에 대해서는 불변성을 유지하지 못하기 때문에 값이 같아요. // 더 혼란스러워 지는거죠 ㅠㅠ user.urls.portfolio = '<http://portfolio.com>'; console.log(user.urls.portfolio === user2.urls.portfolio); // true // 아래 예도 똑같아요. user2.urls.blog = ''; console.log(user.urls.blog === user2.urls.blog); // true
- 깊은복사 : 내부의 모든 값들을 하나하나 다 찾아서 모두 복사하는 방법
객체의 프로퍼티 중, 기본형 데이터는 그대로 복사 + 참조형 데이터는 다시 그 내부의 프로퍼티를 복사한다. 이걸 재귀적 수행 이라고한다.var user = { name: 'wonjang', urls: { portfolio: '<http://github.com/abc>', blog: '<http://blog.com>', facebook: '<http://facebook.com/abc>', } }; // 1차 copy var user2 = copyObject(user); // 2차 copy -> 이렇게까지 해줘야만 해요..!! user2.urls = copyObject(user.urls); user.urls.portfolio = '<http://portfolio.com>'; console.log(user.urls.portfolio === user2.urls.portfolio); user2.urls.blog = ''; console.log(user.urls.blog === user2.urls.blog);
var copyObjectDeep = function(target) { var result = {}; if (typeof target === 'object' && target !== null) { for (var prop in target) { result[pop] = copyObjectDeep(target[prop]); } } else { result = target; } return result; }
이렇게 하면 전체 다 복사가 가능하다//결과 확인 var obj = { a: 1, b: { c: null, d: [1, 2], } }; var obj2 = copyObjectDeep(obj); obj2.a = 3; obj2.b.c = 4; obj2.b.d[1] = 3; console.log(obj); console.log(obj2);
- 불변 객체
- 💡**재귀적으로 수행한다?** ⇒함수나 알고리즘이 자기 자신을 호출하여 반복적으로 실행되는 것을 말합니다 😎
- 깊은복사
- //위 패턴을 우리 예제에 적용해봅시다. var user = { name: 'wonjang', gender: 'male', }; var user2 = copyObject(user); user2.name = 'twojang'; if (user !== user2) { console.log('유저 정보가 변경되었습니다.'); } console.log(user.name, user2.name); console.log(user === user2);
- //이런 패턴은 어떨까요? var copyObject = function (target) { var result = {}; // for ~ in 구문을 이용하여, 객체의 모든 프로퍼티에 접근할 수 있습니다. // 하드코딩을 하지 않아도 괜찮아요. // 이 copyObject로 복사를 한 다음, 복사를 완료한 객체의 프로퍼티를 변경하면 // 되겠죠!? for (var prop in target) { result[prop] = target[prop]; } return result; }
- 얕은복사
- // user 객체를 생성 var user = { name: 'wonjang', gender: 'male', }; // 이름을 변경하는 함수 정의 // 입력값 : 변경대상 user 객체, 변경하고자 하는 이름 // 출력값 : 새로운 user 객체 // 특징 : 객체의 프로퍼티에 접근하는 것이 아니라, 아에 새로운 객체를 반환 -> 불변 var changeName = function (user, newName) { return { name: newName, gender: user.gender, }; }; // 변경한 user정보를 user2 변수에 할당하겠습니다. // 불변이기 때문에 user1은 영향이 없어요! var user2 = changeName(user, 'twojang'); // 결국 아래 로직이 수행되겠네요. if (user !== user2) { console.log('유저 정보가 변경되었습니다.'); } console.log(user.name, user2.name); // wonjang twojang console.log(user === user2); // false 👍
- 불변 객체의 필요성
- 객체의 속성에 접근해서 값을 변경하면 가변이 성립한다.
- 참조형 데이터가 ‘가변값’이라고 할 때의 ‘가변’은 참조형 데이터 자체를 변경할 경우가 아니라, 그 내부의 프로퍼티를 변경할 때 성립한다고 할 수 있다.
- //기본형 데이터 var a = 10; var b = a; //참조형 데이터 var obj1 = { c: 10, d: 'ddd' }; var obj2 = obj1; b = 15; obj2 = { c: 20, d: 'ddd'};
- 자바스크립트에서 중첩객체란, 객체 안에 또 다른 객체가 들어가는 것을 말해요. 이번 주차 초반에 살펴보았듯이 객체는 배열, 함수 등을 모두 포함하는 상위개념이기 때문에 배열을 포함하는 객체도 중첩객체라고 할 수 있답니다.
undefined와 null
둘 다 없음을 나타내는 값이다. 다만 목적이 다른다.
undefined
- 변수에 값이 지되지 않는 경우, 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근했을때
- .이나 []로 접근하려 할 때, 해당 데이터가 존재하지 않는 경우
- return문이 없거나 호출되지 않는 함수의 실행 결과
var a; console.log(a); // (1) 값을 대입하지 않은 변수에 접근 var obj = { a: 1 }; console.log(obj.a); // 1 console.log(obj.b); // (2) 존재하지 않는 property에 접근 // console.log(b); // 오류 발생 var func = function() { }; var c = func(); // (3) 반환 값이 없는 function console.log(c); // undefined
null
용도 : ‘없다’를 명시적으로 표현할 때
주의 : typeof null
typeof null이 object인 것은 유명한 javascript 자체 버그이다.
var n = null; console.log(typeof n); // object //동등연산자(equality operator) console.log(n == undefined); // true console.log(n == null); // true //일치연산자(identity operator) console.log(n === undefined); //false console.log(n === null); // true
실행컨텍스트(스코프,변수,객체,호이스팅)
자바스크립트의 실행 컨텍스트는 실행할 코드에 제공할 환경정보를 모아놓은 객체이다.
자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 다음과 같은 일을 한다.
- 선언된 변수를 위로 끌어올린다. = 호이스팅(hoisting)
- 외부 환경 정보를 구성한다.
- this값을 설정한다.
실행 컨텍스트란?
스택 vs 큐
stack은 후입선출 느낌
queue은 선입선출 느낌
콜 스택 (call stack)
동일 환경에 있는 코드를 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이것을 위에있는 스택의 한 종류인 콜스택에 쌓아올린다.
가장 위에 쌓여있는 컨텍스트와 관련된 코드를 실행하는 방법으로 코드의 환경 및 순서를 보장할 수 있다.
- 컨텍스트 구성
- 구성방법
- 전역공간
- eval()함수
- 함수(우리가 흔히 실행컨텍스트를 구성하는 방법)
- 구성방법
// ---- 1번 var a = 1; function outer() { function inner() { console.log(a); //undefined var a = 3; } inner(); // ---- 2번 console.log(a); } outer(); // ---- 3번 console.log(a);
이미지를 봤을때 코드실행 → 전역(in) → 전역(중단) + outer(in) → outer(중단) + inner(in) → inner(out) + outer(재개) → outer(out) + 전역(재개) → 전역(out) → 코드종료
순서로 진행이 된다.
결국 특정 실행 컨텍스트가 생성되는 시점이 콜 스택의 맨 위에 쌓이는 (노출되는) 순간을 의미한다.
실행 컨텍스트 객체의 실체 (= 담기는 정보)
- VariableEnvironment 현재 컨텍스트 내의 식별자 정보(=record)를 갖고 있다.
- 식별자 정보 var a = 3 위의 경우 var a를 의미한다.
- ⭐️LexicalEnvironment⭐️ 현재 컨텍스트 내의 식별자 정보(=record)를 갖고 있다.
- 식별자 정보 var a = 3 위의 경우 var a를 의미한다.
- ThisBinding this 식별자가 바라봐야할 객체
VariableEnvironment, LexicalEnvironment의 개요
이 두가지는 담기는 항목은 완벽하게 동일하다. 그러나, 스냅샷 유지여부는 다르다.
VE : 스냅샷을 유지한다.
LE : 스냅샷을 유지하지 않는다. 즉, 실시간으로 변경사항을 계속해서 반영한다.
결국, 실행 컨텍스트를 생성할 때, VE에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LE를 만들고 이후에는 주로 LE를 활용한다.
구성요소
- VE, LE모두 동일하며, ‘environmentRecord’와 ‘outerEnvironmentReference’로 구성
- environmentRecord(=record)
- 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.
- 함수에 지정된 매개변수 식별자, 함수자체, var로 선언된 변수 식별자 등
- outerEnvironmentReference(=outer)
LexicalEnvironment(1) - environmentRocord(=record)와 호이스팅
레코드는 식별자 정보
- let a ;
- 복제의 방식
- 기본형 : 값이 담긴 주소값을 바로 복제
- 참조형 : 값이 담긴 주소값들로 이루어진 묶음을 가리키는 주소값을 복제
- 불변성의 여부
- 기본형 : 불변성을 띔
- 참조형 : 불변성을 띄지 않음
- 비트
- 컴퓨터가 이해할 수 있는 가장 작은 단위
- 0과 1을 가지고 있는 메모리를 구성하기 위한 작은 조각
- 이 조각들이 모여서 메모리가 만들어진다.
- 바이트
- 0과 1만 표현하는 비트를 모두 찾기는 부담
- 1→2→…→8개(새로운 단위:byte)
- 메모리(memo_ry): byte단위로 구성 모든 데이터는 byte 단위의 식별자인 메모리 주소값을 통해서 서로 구분
- 값을 바로 변수에 대입하지 않는 이유(=무조건 새로 만드는 이유)
- 이미 입력한 문자열이 길어진다면? 숫자는 항상 8byte로 고정이지만, 문자는 고정이 아니다.(영문:1.한글2)
- 이미 1003주소에 할당된 데이터를 변환하려 할 때 훨씬 더 큰 데이터를 저장하려한다면, 1004주소 이후부터 저장되어잇는 모든 데이터를 오른쪽으로 다 미뤄야한다.
- 메모리의 효율적 관리
- 똑같은 데이터를 여러번 저장해야한다면? 1만개의 변수를 생성해서 모든 변수에 숫자 1을 할당하는 상황을 가정했을때 모든 변수를 별개로 인식한다고 하면, 1만개의 변수 공간을 확보해야 한다.
- 바로 대입하는 숫자형은 8바이트 고정으로 1만개 * 8byte = 8만 byte
- 변수 영역에 별도 저장하게되면 변수영역 : 2바이트 1만개 = 2만바이트만 차지하게된다. 💡변수영역은 데이터 영역보다 작기때문에 2로 가정함.
- 데이터 영역 :8바이트 1개 = 8바이트
- 변수영역에 별도로 저장하면 총 2만 8바이트만 든다.
- 똑같은 데이터를 여러번 저장해야한다면? 1만개의 변수를 생성해서 모든 변수에 숫자 1을 할당하는 상황을 가정했을때 모든 변수를 별개로 인식한다고 하면, 1만개의 변수 공간을 확보해야 한다.
- 메모리를 기준으로 다시한번 생각해보는 두 가지 주요 개념
- 변수 vs 상수
- 변수 : 변수 영역 메모리를 변경할 수 있음
- 상수 : 변수 영역 메모리를 변경할 수 없음
- 불변하다 vs 불변하지 않다
- 불변하다 : 데이터 영역메모리를 변경할 수 없음
- 불변하지 않다 : 데이터 영역 메모리를 변경할 수 있음
- 변수 vs 상수
- 불변값과 불변성
- // a라는 변수가 abc에서 abcdef가 되는 과정을 통해 불변성을 유추해봅시다! // 'abc'라는 값이 데이터영역의 @5002라는 주소에 들어갔다고 가정할게요. var a = 'abc'; // 'def'라는 값이 @5002라는 주소에 추가되는 것이 아니죠! // @5003에 별도로 'abcdef'라는 값이 생기고 a라는 변수는 @5002 -> @5003 // 즉, "변수 a는 불변하다." 라고 할 수 있습니다. // 이 때, @5002는 더 이상 사용되지 않기 때문에 가비지컬렉터의 수거 대상이 됩니다. a = a + 'def';
- 참조형 데이터의 변수 할당 과정⬇️ 강의를 참고해서 아래 표를 직접 작성해보세요!주소 1001 1002 1003 1004
데이터 objt/@7103~ 주소 5001 5002 5003 5004 데이터 1 ‘bbb’ 데이터 a/@5001 b/@5002 - 데이터 영역에 저장된 값은 여전히 계속 불변값이지만 obj1을 위한 별도 영역은 얼마든지 변경이 가능해서 참조형 데이터를 흔히, 불편하지 않다(=가변하다)라고 한다.
- var obj1 = { a: 1, b: 'bbb', }; // 데이터를 변경해봅시다. obj1.a = 2;
- // 참조형 데이터는 별도 저장공간(obj1을 위한 별도 공간)이 필요합니다! var obj1 = { a: 1, b: 'bbb, };
- 중첩객체의 할당
⬇️ 강의를 참고해서 아래 표를 직접 작성해보세요!주소 1001 1002 1003 1004 1005 …var obj = { x: 3, arr: [3, 4, 5], } // obj.arr[1]의 탐색과정은 어떻게 될까요? 작성하신 표에서 한번 찾아가보세요!
데이터 obj/@7103~ 주소 5001 5002 5003 5004 5005 … 데이터 3 4 5 데이터 x/@5001 arr/@8104~ 데이터 0/@5001 1/@5002 2/@5003 - 참조카운트란 무엇일까요?</aside>
- <aside> 💡 객체를 참조하는 변수나 다른 객체의 수를 나타내는 값입니다. 참조 카운트가 0인 객체는 더 이상 사용되지 않으므로, 가비지 컬렉터에 의해 메모리에서 제거됩니다.
- 가비지컬렉터(GC, Garbage Collector)</aside>
- <aside> 💡 더 이상 사용되지 않는 객체를 자동으로 메모리에서 제거하는 역할을 합니다. 자바스크립트는 가비지 컬렉션을 수행함으로써 개발자가 명시적으로 메모리 관리를 하지 않아도 되도록 지원합니다. 자바스크립트 엔진에서 내부적으로 수행되며, 개발자는 가비지 컬렉션에 대한 직접적인 제어를 할 수 없습니다.
- 변수 복사의 비교
주소 1001 1002 1003 1004 1005 …// STEP01. 쭉 선언을 먼저 해볼께요. var a = 10; //기본형 var obj1 = { c: 10, d: 'ddd' }; //참조형 // STEP02. 복사를 수행해볼께요. var b = a; //기본형 var obj2 = obj1; //참조형
데이터 a/@5001 obj1/@7103~ b/@5001 obj2/@7103~ 주소 5001 5002 5003 5004 5005 데이터 10 ‘ddd’ 데이터 c/@5001 d/@5002 - 복사 이후 값 변경(객체의 프로퍼티 변경
주소 1001 1002 1003 1004 1005 …// STEP01. 쭉 선언을 먼저 해볼께요. var a = 10; //기본형 var obj1 = { c: 10, d: 'ddd' }; //참조형 // STEP02. 복사를 수행해볼께요. var b = a; //기본형 var obj2 = obj1; //참조형 b = 15; obj2.c = 20;
데이터 a/@5001 obj1/@7103~ b/@5003 obj2/@7103~ 주소 5001 5002 5003 5004 5005 데이터 10 ‘ddd’ 15 20 데이터 c/@5004 d/@5002 - 기본형
- 숫자 15라는 값을 데이터 영역에서 검색 후 없다면 생성
- 검색한 결과주소 또는 생성한 주소를 변수 영역 b에 갈아끼움
- a와 b는 서로 다른 데이터 영역의 주소를 바라보고 있기 때문에 영향 없음
- 참조형
- 숫자 20이라는 값을 데이터 영역에서 검색 후 없다면 생성
- 검색한 결과주소 또는 생성한 주소 obj2에게 지정되어 있는 별도 영역(7103~)에 갈아끼움
- obj1도 똑같은 주소를 바라보고 있기 때문에 obj1까지 변경이 됨
데이터 a/@5001 obj1/@7103~ b/@5003 obj2/@78104~ 주소 5001 5002 5003 5004 5005 데이터 10 ‘ddd’ 15 20 데이터 c/@5001 d/@5002 데이터 c/@5004 d/@5002
위 코드를 아래코드와 같이 개선할 수 있다.다만 위와 같은 방법은 속성이 적을땐 간단하게 변경이 가능하지만 속성이 많아질경우 문제가 된다.<패턴><적용>얕은 복사의 경우 중첩된 객체에 대해서는 완벽한 복사를 할 수 없다.// user 객체를 생성 var user = { name: 'wonjang', gender: 'male', }; // 이름을 변경하는 함수, 'changeName'을 정의 // 입력값 : 변경대상 user 객체, 변경하고자 하는 이름 // 출력값 : 새로운 user 객체 // 특징 : 객체의 프로퍼티(속성)에 접근해서 이름을 변경했네요! -> 가변 var changeName = function (user, newName) { var newUser = user; newUser.name = newName; return newUser; }; // 변경한 user정보를 user2 변수에 할당하겠습니다. // 가변이기 때문에 user1도 영향을 받게 될거에요. var user2 = changeName(user, 'twojang'); // 결국 아래 로직은 skip하게 될겁니다. if (user !== user2) { console.log('유저 정보가 변경되었습니다.'); } console.log(user.name, user2.name); // twojang twojang console.log(user === user2); // true
- 얕은복사 : 바로 아래 단계의 값만 복사 문제점 : 중첩된 객체의 경우 참조형 데이터가 저장된 프로퍼티를 복사할 떄 주소값만 복사한다.
위와 같은 코드처럼 객체안에 객체는 가져올 수 가없다.var user = { name: 'wonjang', urls: { portfolio: '<http://github.com/abc>', blog: '<http://blog.com>', facebook: '<http://facebook.com/abc>', } }; var user2 = copyObject(user); user2.name = 'twojang'; // 바로 아래 단계에 대해서는 불변성을 유지하기 때문에 값이 달라지죠. console.log(user.name === user2.name); // false // 더 깊은 단계에 대해서는 불변성을 유지하지 못하기 때문에 값이 같아요. // 더 혼란스러워 지는거죠 ㅠㅠ user.urls.portfolio = '<http://portfolio.com>'; console.log(user.urls.portfolio === user2.urls.portfolio); // true // 아래 예도 똑같아요. user2.urls.blog = ''; console.log(user.urls.blog === user2.urls.blog); // true
- 깊은복사 : 내부의 모든 값들을 하나하나 다 찾아서 모두 복사하는 방법
객체의 프로퍼티 중, 기본형 데이터는 그대로 복사 + 참조형 데이터는 다시 그 내부의 프로퍼티를 복사한다. 이걸 재귀적 수행 이라고한다.var user = { name: 'wonjang', urls: { portfolio: '<http://github.com/abc>', blog: '<http://blog.com>', facebook: '<http://facebook.com/abc>', } }; // 1차 copy var user2 = copyObject(user); // 2차 copy -> 이렇게까지 해줘야만 해요..!! user2.urls = copyObject(user.urls); user.urls.portfolio = '<http://portfolio.com>'; console.log(user.urls.portfolio === user2.urls.portfolio); user2.urls.blog = ''; console.log(user.urls.blog === user2.urls.blog);
var copyObjectDeep = function(target) { var result = {}; if (typeof target === 'object' && target !== null) { for (var prop in target) { result[pop] = copyObjectDeep(target[prop]); } } else { result = target; } return result; }
이렇게 하면 전체 다 복사가 가능하다//결과 확인 var obj = { a: 1, b: { c: null, d: [1, 2], } }; var obj2 = copyObjectDeep(obj); obj2.a = 3; obj2.b.c = 4; obj2.b.d[1] = 3; console.log(obj); console.log(obj2);
- 불변 객체
- 💡**재귀적으로 수행한다?** ⇒함수나 알고리즘이 자기 자신을 호출하여 반복적으로 실행되는 것을 말합니다 😎
- 깊은복사
- //위 패턴을 우리 예제에 적용해봅시다. var user = { name: 'wonjang', gender: 'male', }; var user2 = copyObject(user); user2.name = 'twojang'; if (user !== user2) { console.log('유저 정보가 변경되었습니다.'); } console.log(user.name, user2.name); console.log(user === user2);
- //이런 패턴은 어떨까요? var copyObject = function (target) { var result = {}; // for ~ in 구문을 이용하여, 객체의 모든 프로퍼티에 접근할 수 있습니다. // 하드코딩을 하지 않아도 괜찮아요. // 이 copyObject로 복사를 한 다음, 복사를 완료한 객체의 프로퍼티를 변경하면 // 되겠죠!? for (var prop in target) { result[prop] = target[prop]; } return result; }
- 얕은복사
- // user 객체를 생성 var user = { name: 'wonjang', gender: 'male', }; // 이름을 변경하는 함수 정의 // 입력값 : 변경대상 user 객체, 변경하고자 하는 이름 // 출력값 : 새로운 user 객체 // 특징 : 객체의 프로퍼티에 접근하는 것이 아니라, 아에 새로운 객체를 반환 -> 불변 var changeName = function (user, newName) { return { name: newName, gender: user.gender, }; }; // 변경한 user정보를 user2 변수에 할당하겠습니다. // 불변이기 때문에 user1은 영향이 없어요! var user2 = changeName(user, 'twojang'); // 결국 아래 로직이 수행되겠네요. if (user !== user2) { console.log('유저 정보가 변경되었습니다.'); } console.log(user.name, user2.name); // wonjang twojang console.log(user === user2); // false 👍
- 불변 객체의 필요성
- 객체의 속성에 접근해서 값을 변경하면 가변이 성립한다.
- 참조형 데이터가 ‘가변값’이라고 할 때의 ‘가변’은 참조형 데이터 자체를 변경할 경우가 아니라, 그 내부의 프로퍼티를 변경할 때 성립한다고 할 수 있다.
- //기본형 데이터 var a = 10; var b = a; //참조형 데이터 var obj1 = { c: 10, d: 'ddd' }; var obj2 = obj1; b = 15; obj2 = { c: 20, d: 'ddd'};
- 자바스크립트에서 중첩객체란, 객체 안에 또 다른 객체가 들어가는 것을 말해요. 이번 주차 초반에 살펴보았듯이 객체는 배열, 함수 등을 모두 포함하는 상위개념이기 때문에 배열을 포함하는 객체도 중첩객체라고 할 수 있답니다.
- 변수에 값이 지되지 않는 경우, 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근했을때
- .이나 []로 접근하려 할 때, 해당 데이터가 존재하지 않는 경우
- return문이 없거나 호출되지 않는 함수의 실행 결과
null주의 : typeof nullvar a; console.log(a); // (1) 값을 대입하지 않은 변수에 접근 var obj = { a: 1 }; console.log(obj.a); // 1 console.log(obj.b); // (2) 존재하지 않는 property에 접근 // console.log(b); // 오류 발생 var func = function() { }; var c = func(); // (3) 반환 값이 없는 function console.log(c); // undefined
var n = null; console.log(typeof n); // object //동등연산자(equality operator) console.log(n == undefined); // true console.log(n == null); // true //일치연산자(identity operator) console.log(n === undefined); //false console.log(n === null); // true
실행컨텍스트(스코프,변수,객체,호이스팅)
자바스크립트의 실행 컨텍스트는 실행할 코드에 제공할 환경정보를 모아놓은 객체이다.- 선언된 변수를 위로 끌어올린다. = 호이스팅(hoisting)
- 외부 환경 정보를 구성한다.
- this값을 설정한다.
- 컨텍스트 구성
- 구성방법
- 전역공간
- eval()함수
- 함수(우리가 흔히 실행컨텍스트를 구성하는 방법)
- 구성방법
결국 특정 실행 컨텍스트가 생성되는 시점이 콜 스택의 맨 위에 쌓이는 (노출되는) 순간을 의미한다.// ---- 1번 var a = 1; function outer() { function inner() { console.log(a); //undefined var a = 3; } inner(); // ---- 2번 console.log(a); } outer(); // ---- 3번 console.log(a);
- VariableEnvironment 현재 컨텍스트 내의 식별자 정보(=record)를 갖고 있다.
- 식별자 정보 var a = 3 위의 경우 var a를 의미한다.
- ⭐️LexicalEnvironment⭐️ 현재 컨텍스트 내의 식별자 정보(=record)를 갖고 있다.
- 식별자 정보 var a = 3 위의 경우 var a를 의미한다.
- ThisBinding this 식별자가 바라봐야할 객체
- VE, LE모두 동일하며, ‘environmentRecord’와 ‘outerEnvironmentReference’로 구성
- environmentRecord(=record)
- 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.
- 함수에 지정된 매개변수 식별자, 함수자체, var로 선언된 변수 식별자 등
- outerEnvironmentReference(=outer)
- let a ;
- 복제의 방식
- 레코드는 식별자 정보
- 구성요소
- LE : 스냅샷을 유지하지 않는다. 즉, 실시간으로 변경사항을 계속해서 반영한다.
- 이 두가지는 담기는 항목은 완벽하게 동일하다. 그러나, 스냅샷 유지여부는 다르다.
- 실행 컨텍스트 객체의 실체 (= 담기는 정보)
- 순서로 진행이 된다.
- 이미지를 봤을때 코드실행 → 전역(in) → 전역(중단) + outer(in) → outer(중단) + inner(in) → inner(out) + outer(재개) → outer(out) + 전역(재개) → 전역(out) → 코드종료
- 동일 환경에 있는 코드를 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이것을 위에있는 스택의 한 종류인 콜스택에 쌓아올린다.
- queue은 선입선출 느낌
- stack은 후입선출 느낌
- 스택 vs 큐
- 자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 다음과 같은 일을 한다.
- typeof null이 object인 것은 유명한 javascript 자체 버그이다.
- 용도 : ‘없다’를 명시적으로 표현할 때
- 둘 다 없음을 나타내는 값이다. 다만 목적이 다른다.
- /** 선언과 할당을 풀어 쓴 방식 */ var str; str = 'test!'; /** 선언과 할당을 붙여 쓴 방식 */ var str = 'test!';
- 식별자 = 변수명
- var testValue = 3
- 복제의 방식