관리 메뉴

공부기록용

JavaScript 문법 종합 3주차(데이터타입_심화2/불변객체예시) 본문

📚강의록📚/스파르타)Javascript

JavaScript 문법 종합 3주차(데이터타입_심화2/불변객체예시)

과부하가즈아 2023. 6. 13. 14:18

“불변하다”. 혹은, ‘불변객체’의 개념이 왜 필요한지, 불변성을 유지하는 방

 

객체의 속성에 접근해서 이름을 변경_오류발생

// 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

 

해결-1)객체의 속성에 접근하는 것이 아니라 아예 새로운 객체를 만들어버림

// 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 👍

해결-2)얕은 복사를 이용

  • 패턴과 적용(기본구조를 적용하는 법 설명)
//이런 패턴은 어떨까요?
var copyObject = function (target) {
	var result = {};

	// for ~ in 구문을 이용하여, 객체의 모든 프로퍼티에 접근할 수 있습니다.
	// 하드코딩을 하지 않아도 괜찮아요.
	// 이 copyObject로 복사를 한 다음, 복사를 완료한 객체의 프로퍼티를 변경하면
	// 되겠죠!?
	for (var prop in target) {
		result[prop] = target[prop];
	}
	return result;
}

  • 적용해보기
//위 패턴을 우리 예제에 적용해봅시다.
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 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에서의 복사(깊은복사)가 필요한 이유이며
// copyObject는 더 깊은 차원으로의 복사를 수행할 수 없기에 비롯된 오류인셈
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

> 객체user안의 중첩된 객체urls

 

  • 중첩된 객체의 깊은 복사
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[prop] = copyObjectDeep(target[prop]);
		}
	} else {
		result = target;
	}
	return result;
}

 

재귀호출

재귀 호출(recursive call)이란 함수 내부에서 함수가 자기 자신을 또다시 호출하는 행위를 의미합니다.
이러한 재귀 호출은 자기가 자신을 계속해서 호출하므로, 끝없이 반복되게 됩니다.

따라서 함수 내에 재귀 호출을 중단하도록 조건이 변경될 명령문을 반드시 포함해야 합니다.
http://www.tcpschool.com/c/c_function_recursive

 

Comments