2024/04/24 자바스크립트 문법 3

YIS·2024년 4월 24일
post-thumbnail

3주차
데이터 타입(심화), 실행 컨텍스트, this

데이터 타입 심화

1. 데이터 타입의 종류

값의 저장 방식과, 불변성 여부에 따라 기본형과 참조형으로 나뉨.

  • 기본형
    값이 담긴 주소값을 바로 복제하고, 불변성을 띔.

  • 참조형
    값이 담긴 주소값들로 이루어진 묶음을 가리키는 주소값을 복제, 불변성을 띄지않음


2. 메모리와 데이터에 관한 배경지식

  • 비트 : 컴퓨터가 이해할 수 있는 가장 작은 데이터 단위. 0과 1로 표현. 이 비트들이 모여 메모리를 구성
  • 바이트 : 8개의 비트가 하나의 바이트를 구성.
  • 메모리 : 모든 데이터는 바이트 단위의 식별자인 메모리 주소값을 통해 구분
  • 메모리 관리 : JavaScript에서는 변수에 값을 할당할 때 별도의 데이터 타입 크기 지정 없이
    자동으로 메모리가 할당. 숫자는 8바이트로 저장.
  • 식별자,변수 : let temp = 5; 일때 temp가 식별자(변수명),5가 변수(데이터)

3. 변수 선언과 데이터 할당

변수명과 데이터는 각각의 주소를 가짐

  • 변수명에 바로 값을 바로 대입하지않는 이유(새로만드는 이유)

    *자유로운데이터변환
    만약 변수명에 데이커값을 바로 입력했을시
    이미 선언된 변수명에 할당된 데이터값보다 더 큰 데이터값을 저장할경우 뒤에 저장되있는
    모든 변수명주소들이 밀려지는현상.

    *메모리의 효율적 관리
    1만개의 변수에 숫자1을 할당하는 상황(숫자는 8바이트 고정)
    바로대입 => 1만*8바이트 =8만 바이트
    변수(데이터)영역에 저장 (데이터영역저장되는 크기는 2바이트가정) 1만*2바이트 =2만 바이트

4. 기본형 데이터와 참조형 데이터

  • 메모리를 기준으로 다시한번 생각해보는 두 가지 주요 개념
    -변수 : 변수 영역 메모리를 변경할 수 있음 (불변하지 않음)
    -상수 : 변수 영역 메모리를 변경할 수 없음 (불변하다)

  • 불변값과 불변성(with 기본형 데이터)

var a = 'abc';
a= a + "def";
//a주소값 데이터부분에 abc를 저장한 주소(ex.@5001)가 들어갔다고 가정함.
// a+ "def"가 abc를 저장한주소 @5001에 직접 추가되는게 아님
//@5002에 별도로 "abcdef"가 생기고 이주소가 a주소값 데이터부분에 할당.
//@5001주소는 더이상 사용되지않기 때문에 가비지컬렉터로 수거됨
  • 가변값과 가변성(with 참조형 데이터)
    *참조형 데이터의 변수 할당 과정
// 참조형 데이터는 별도 저장공간(obj1을 위한 별도 공간)이 필요
var obj1 = {
	a: 1,
	b: 'bbb,
};

*기본형 데이터의 변수 할당 과정과 차이점 : 객체의 변수(프로퍼티) 영역의 별도 존재 여부
*참조형 데이터가 불변하지 않다(가변하다)라고 하는 이유

var obj1 = {
	a: 1,
	b: 'bbb',
};

// 데이터를 변경.
obj1.a = 2;


데이터 영역에 저장된 값은 여전히 계속 불변값이지만,
obj1을 위한 별도 영역은 변경이 가능.
이것때문에 참조형 데이터를 불변하지 않다라고 표현.
위의 사진에서 상수냐 변수냐를 구분하는곳은 변수부분.
값이 불변이냐 불변하지않냐를 구분하는곳은 데이터+OBJ를 위한 별도공간

*중첩객체의 할당

var obj = {
	x: 3,
	arr: [3, 4, 5],
}

*참조 카운트가 0인 메모리 주소처리

참조 카운터 : 객체를 참조하는 변수나 다른 객체의 수를 나타내는 값
0인경우 가비지컬렉터에 의해 메모리에서 제거

가비지 컬렉터 : 더 이상 사용되지 않는 객체를 자동으로 메모리에서 제거하는 역할

  • 변수 복사의 비교
var a = 10; //기본형
var obj1 = { c: 10, d: 'ddd' }; //참조형

var b = a; //기본형
var obj2 = obj1; //참조형

  • 복사 이후 값 변경(객체의 프로퍼티 변경)
var a = 10; //기본형
var obj1 = { c: 10, d: 'ddd' }; //참조형

var b = a; //기본형
var obj2 = obj1; //참조형

b = 15;
obj2.c = 20;

*기본형
15라는 값이 데이터부분에 없어서 5003에 생성
b에 @5003주소를 할당. a와b는 다른 데이터 영역의 주소를 참조

*참조형
20이라는 값을 데이터부분에서 찾고 없어서 5004에 할당.
obj2의 주소 7103에 값을 5004로 갈아 끼움.
obj1도 주소 7103를 가져오고 있기 때문에 obj1의 c값도 바뀜.
원하지 않았던 결과.

  • 복사 이후 값 변경(객체 자체를 변경)
//기본형 데이터
var a = 10;
var b = a;

//참조형 데이터
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1;

b = 15;
obj2 = { c: 20, d: 'ddd'};

참조형 데이터가 ‘가변값’이라고 할 때의 가변은
참조형 데이터 자체를 변경할 경우가 아니라,
그 내부의 프로퍼티를 변경할 때 성립

5.불변 객체

  • 불변 객체의 정의
    객체의 속성에 접근해서 값을 변경하는것 가능.(원하지 않는 결과가 나올 수있음.)
    객체 데이터 자체를 변경(새로운 데이터를 할당)하고자 한다면
    기존 데이터는 변경되지 않음.

  • 불변 객체의 필요성

    -객체의 가변성의 문제에 대한 예시

var user = {
	name: 'wonjang',
	gender: 'male',
};
var changeName = function (user, newName) {
	var newUser = user;
	newUser.name = newName;
	return newUser;
};
// 함수표현식으로 changeName'을 정의
// 매개변수로 user,newName 받음.
// 새로운 변수newUser 을 선언. 그리고 user값을 할당함.
// newUser.name을 매개변수로 받은 newName로 할당.
// 그리고 newUser을 리턴함. 

var user2 = changeName(user, 'twojang');
//user2라는 변수선언. 거기에 changeName함수를 할당하는데 
//매개변수에 객체user와 문자열'twojang'을 넣음
//newUser안에 user객채가 들어가게되고
//newUser의 name을 매개변수로 받은 'twojang'으로 교체.
//user2객체의 name속성에 접근해서 값 변경시 그 속성을 가리키고있는 주소값의 데이터는
//user의 name과 같기때문에 user.name도 'twojang'값을 가리키게됨.

// 아래 로직은 skip.
if (user !== user2) {
	console.log('유저 정보가 변경되었습니다.');
}

console.log(user.name, user2.name); // twojang twojang
console.log(user === user2); // true

-위의 예시를 개선한버전
var user = {
	name: 'wonjang',
	gender: 'male',
};

var changeName = function (user, newName) {
	return {
		name: newName,
		gender: user.gender,
	};
};
// 함수표현식으로 changeName'을 정의
//user,newName를 매개변수로 받음
//return값으로 새로운 객체를 생성
//그안에 프로퍼티로 name 프로퍼티값으로 매개변수newName를 할당.
//마찬가지로 gender에 매개변수user안의 속성gender의 값을 할당

var user2 = changeName(user, 'twojang');
//user2변수에 changeName을 할당
//새로운 객체 프로퍼티값 name에 twojang을 할당.
//새로운 객체 gender에 user객체의 gender를 할당.
//user과 user2의 프로퍼티네임은 같지만, 그값은 각각의 다른 주소에서 값을 받아옴
//왜? 객체를 새로만들었으니까

// 아래 로직은 실행됨.
if (user !== user2) {
	console.log('유저 정보가 변경되었습니다.');
}

console.log(user.name, user2.name); // wonjang twojang
console.log(user === user2); // false 👍

단 user속성이 10개 100개라면?
리턴값으로 만들어야하는 속성도 10개 100개만들어야해서 이방법도 완벽한 방법은 아님

  • 더 나은 방법 : 얕은 복사
    for..in문을 이용해 객체의 모든 프로퍼티에 접근,
var copyObject = function (target) {
	var result = {};
    for (var prop in target) {
		result[prop] = target[prop];
	}
	return result;
}
//함수표현식으로 copyObject를 만듬.
//빈객체 result를 만들고
//for..in문으로 포인문안에서만 쓸 변수prop에 매개변수 target를 순회하면서 값을 넣어줌
//매개변수target의 키:값들을 result에 넣어줌

var user = {
	name: 'wonjang',
	gender: 'male',
};

var user2 = copyObject(user);
user2.name = 'twojang';
//user2는 copyObject함수를 실행후 결과값을 할당.
//for..in문으로 상위수준의 속성값, 즉 키와 값만을 복사함. 객체안의 객체나 배열같은경우는 
//여전히 참조

if (user !== user2) {
	console.log('유저 정보가 변경되었습니다.');
}

console.log(user.name, user2.name);
console.log(user === user2);

이방법도 완벽하진않음. 복사하는 객체안에 중첩객체,배열이 있을경우 그값은 여전히 원본과
같은 주소를 참조하게됨.

  • 중첩된 객체에 대한 깊은 복사 살펴보기
    var copyObject = function (target) {
      var result = {};
      for (var prop in target) {
        result[prop] = target[prop];
      }
      return result;
    }

	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);
	//user와 user2의 name는 다른주소를 참조하게됨

    // 2차 copy -> 이렇게까지 해줘야만 해요..!!
    user2.urls = copyObject(user.urls);
	//user의 urls의 값들이 user2의 urls의 값들과 다른주소를 참조하게됨.
	//user2의urls를 얕은복사로 참조하는 값들을 바꾸긴 했지만
	//만약 user2의 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[prop] = copyObjectDeep(target[prop]);
		}
	} else {
		result = target;
	}
	return result;
}
//함수표현식으로 copyObjectDeep선언 
//조건으로 매개변수로 들어오는 target값이 object. 즉 객체나 배열이고 &&앤드연산자로
// null값이 아닌경우만 재귀적으로 객체의 프로퍼티를 복사하게함
//null도 object타입으로 반환하기때문에 따로 필터링 해줘야함
// copyObjectDeep(target[prop])는 매개변수 target의 현재속성값prop를 인자로 받아서
// 자기자신을 호출함.  
//객체가 아닌경우 result = target;으로 바로 넣어줌.

//결과 확인
var obj = {
	a: 1,
	b: {
		c: null,
		d: [1, 2],
	}
};
var obj2 = copyObjectDeep(obj);
//a는 객체아니므로 직접 복사
//b는 객체이므로 b내부의 프로퍼티(c,d)도 복사대상이됨.
//c는 null,객체가 아니므로 직접복사.
//d는 배열이므로 d내부의 프로퍼티도 복사대상이됨.
//d안의 1,2는 객체가 아니므로 직접복사
obj2.a = 3;
obj2.b.c = 4;
obj2.b.d[1] = 3;

console.log(obj);
console.log(obj2);
  • JSON(=JavaScript Object Notation)을 이용하는 방법

장점:
원본과 복사본 객체가 독립적으로 존재하여, 복사본 수정이 원본에 영향을 미치지 않음.
코드가 간결하고 이해하기 쉬움.

단점:
함수나 undefined와 같은 일부 속성 값은 복사되지 않음.
순환 참조를 지원하지 않아, 객체 내에 중첩된 객체가 있는 경우 복사할 수 없음.
적합한 경우: 구조가 간단하고, 함수나 undefined 속성이 없는 객체에 대한 깊은 복사가 필요할 때. 복잡하거나 순환 참조가 있는 경우에는 다른 방법을 고려해야 함.

profile
엉덩이가 무거운 사람

0개의 댓글