1. 데이터 타입 심화 2
ⅰ. 불변 객체
- 불변 객체란?
- 객체로 예를 들면, 객체의 속성에접근해서 값을 변경하면 가변이 성렵함
하지만, 객체 데이터 자체를 변경(새로운 데이터를 할당)하고자 한다면 기존 데이터는 변경이 되지 않으므로 불변하다고 할수 있음
- 객체로 예를 들면, 객체의 속성에접근해서 값을 변경하면 가변이 성렵함
- 불변 객체의 필요성
// user 객채를 생성
let user = {
name: "wonjang",
gender: "male",
};
// 이름을 변경하는 함수, 'changeName'을 정의
// 입력값 : 변경대상 user 객체, 변경하고자 하는 이름
// 출력값 : 새로운 user 객체
// 특징 : 객체의 프로퍼티(속성)에 접근해서 이름을 변경! => 가변
const changeName = (user, newName) => {
// 2개 인자를 받음.
// 첫번째 user => user객체는 이름과 성을 포함하고 있음
// 두번째 newName
let newUser = user; // 인자로 받은 user객체 복사(할당하는 방식으로)
newUser.name = newName; // 새롭게 만든 복사한 newUser의 속성에 접근해서 새로운 이름을 할당
return newUser;
};
// 변경한 user정보를 user2 변수에 할당
// 가변이기 때문에 user1도 영향을 받게됨
const user2 = changeName(user, "twojang");
// 결국 아래 로직은 skip하게 됨
if (user !== user2) {
console.log("유저 정보가 변경되었습니다.");
}
console.log(user.name, user2.name); // twojang twojang
console.log(user === user2); // true => 가변성의 문제점
✔ 위에 코드로 객체의 가변성이 문제가 발생되는것을 알수 있음
// user 객채를 생성
let user = {
name: "wonjang",
gender: "male",
};
// 이름을 변경하는 함수, 'changeName'을 정의
// 입력값 : 변경대상 user 객체, 변경하고자 하는 이름
// 출력값 : 새로운 user 객체
// 특징 : 객체의 프로퍼티에 접근하는 것이 아니라, 아예 새로운 객체를 반환 => 불변
const changeName = (user, newName) => {
// 기존은 새로운 객체를 복사해서 속성을 접근해서 변경하는 방식으로 변경
// 밑에 함수는 항상 새로운 객체를 생성해서 return하게 됨
return {
name: newName,
gender: user.gender,
};
};
// 변경한 user정보를 user2 변수에 할당
// 가변이기 때문에 user1도 영향이 없음
const user2 = changeName(user, "twojang");
// 결국 아래 로직이 실행됨
if (user !== user2) {
console.log("유저 정보가 변경되었습니다.");
}
console.log(user.name, user2.name); // wonjang twojang
console.log(user === user2); // false
✔ 위에 코드는 좋은 방법은 아니고 문제점이 발생함
✔ 예시는 속성이 2개니깐 괜찮지만 속성 양이 많아지면 하드 코딩이 너무 많아짐
=> 이러한 이유때문에 얕은 복사라는 방법을 사용하게됨
- 얕은 복사
👇 얕은 복사의 패턴 예시
// copyObject는 target(객체)를 받음
const copyObject = (target) => {
let result = {};
// for ~ in 구문을 이용하여, 객체의 모든 프로퍼티에 접근할 수 잇음
// 하드코딩을 하지 않아도됨
// 이 copyObject로 복사를 한다음, 복사를 완료한 객체의 프로퍼티를 변경하면됨
for (let prop in target){
result[prop] = target[prop]
}
return result
};
👇 적용 예시
const copyObject = (target) => {
let result = {};
for (let prop in target) {
result[prop] = target[prop];
}
return result;
};
// 예제에 적용하기
let user = {
name: "wonjang",
gender: "male",
};
let user2 = copyObject(user);
user2.name = "twojang";
if (user !== user) {
console.log("유저 정보가 변겨오디었습니다.");
}
if (user !== user2) {
console.log("유저 정보가 변경되었습니다.");
}
console.log(user.name, user2.name); //wonjang twojang
console.log(user === user2); // false
✔ 이러한 얕은 복사 패턴에도 문제가 발생하게됨
=> why? 중첩된 객체에 대해서는 완벽한 복사를 할수 없기 때문 이것이 바로 얕은 복사의 한계
📚 얕은 복사는 바로 아래 단계의 값만 복사하기 때문(for-in문은 조건문의 객체에 대해서 수행하는 것이기 때문에)
=> 중첩된 객체의 경우 참조형 데이터가 저장된 프로퍼티를 복사할 때 주소값만 복사하게됨
- 깊은 복사
- 내부의 모든 값들을 하나하나 다 찾아서 모두 복사하는 방법
// 중첩된 객체에 대한 얕은 복사
let 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'; // 이름은 변경됨 why? 1depth(가장상위) 이기 때문
// 바로 아래 단계에 대해서는 불변성을 유지하기 때문에 값이 달라지죠.
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
✔ 위에 코드의 문제를 해결할려면 결국 user.urls 프로퍼티도 불변 객체로 만들어야함!!!!!!
let 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);
✔ 중첩된 객체의 중첩된 객체는 또 불변객체로 만들어야 하기 때문에 임시적인 방법
=> 자동적으로 수행할 수 있는 방법을 찾아야함!!
⭐ 재귀적 수행(recursive)
- 객체의 프로퍼티 중, 기본형 데이터는 그대로 복사 + 참조형 데이터는 다시 그 내부의 프로퍼티를 복사
❓ 재귀적으로 수행한다라는 말은 함수나 알고리즘이 자기 자신을 호출하여 반복하여 실행하는 것을 말함
=> 각종 알고리즘에서 사용됨?...
- 결론적으로 적용한코드
const copyObjectDeep = (target) => {
let result = {};
if (typeof target === 'object' && target !== null) {
for (let prop in target) {
// 수행을 하지만 얕은복사와 다르게 자기자신을 수행함 =>재귀적 수행
result[prop] = copyObjectDeep(target[prop]);
}
} else {
result = target;
}
return result;
}
// 함수가 호출될때 함수 스스로를 다시 한번 안쪽에서 호출을 하면서
// 객체안의 깊은 곳까지 다 확인할수 있다
✔ 모든 요소 하나하나를 다 불변성을 유지하게끔 바꾸게됨
- 마지막 방법! JSON(=JavaScript Object Notation)을 이용하는 방법도 존재
but!!! 완벽한 방법은 아니기 때문에 내용만 참고- 장점
- JSON.stringify() 함수를 사용하여 객체를 문자열로 변환한 후, 다시 JSON.parse() 함수를 사용하여 새로운 객체를 생성하기 때문에, 원본 객체와 복사본 객체가 서로 독립적으로 존재
=> 복사본 객체를 수정해도 원본 객체에 영향을 미치지 않음 - JSON을 이용한 깊은 복사는 다른 깊은 복사 방법에 비해 코드가 간결하고 쉽게 이해
- JSON.stringify() 함수를 사용하여 객체를 문자열로 변환한 후, 다시 JSON.parse() 함수를 사용하여 새로운 객체를 생성하기 때문에, 원본 객체와 복사본 객체가 서로 독립적으로 존재
- 단점
- JSON을 이용한 깊은 복사는 원본 객체가 가지고 있는 모든 정보를 복사않음
ex. 함수나 undefined와 같은 속성 값은 복사되지 않음 - JSON.stringify() 함수는 순환 참조(Recursive Reference)를 지원하지 않음
=> 객체 안에 객체가 중첩되어 있는 경우, 이 방법으로는 복사할 수 없음
- JSON을 이용한 깊은 복사는 원본 객체가 가지고 있는 모든 정보를 복사않음
- JSON을 이용한 깊은 복사는 객체의 구조가 간단하고, 함수나 undefined와 같은 속성 값이 없는 경우에 적합한 방법, 만약 객체의 구조가 복잡하거나 순환 참조가 있는 경우에는 다른 깊은 복사 방법을 고려해야함
- 장점
ⅱ. undefined와 null
- 없음을 의미하는 값이지만, 두가지는 미세하게 다르고, 목적도 다름
- undefined
- 사용자(=개발자)가 직접 지정할 수도 있지만 일반적으로는 자바스크립트 엔진에서 값이 있어야 할 것 같은데 없는 경우, 자동으로 부여
- undefined가 나오는 case
- 변수에 값을 지정되지 않은 경우, 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할때
- .이나 []로 접근하려 할때, 해당 데이터가 존재하지 않는 경우
- return문이 없가나 호출되지 않는 함수의 실행결과
// 변수의 값이 지정되지 않은 경우 - 데이터 영역에 메모리주소가 없음
let a;
console.log(a); // undefined
// 존재하지 않는 property에 접근하는 경우
const obj = { a: 1 };
console.log(obj.a); // 1
console.log(obj.b); // undefined
// 함수내부에 로직이 없음(return문이 없음)
const func = () => {};
const c = func();
console.log(c); // undefined
📍 헷깔리지 말아야하는 undefined
✔ 지금 undefined로 나오는 이 변수가 필요에 의해 할당한건지 자바스크립트 엔진이 변환한건지 구분할수 없음
✔ 없다를 명시적으로 표현할때는 undefined를 사용하지 말아야함
- null
- null의 용도는 없다를 명시적으로 표현할 때 사용
- null의 주의점!!!!
⭐ typeof null - typeof null을 하면 object인 것은 유명한 javascript 자체 버그!!!!
const n = null;
console.log(typeof n); // object
// 동등연산자(equality operator)
// type까지는 일치하지 않아도 되는것
console.log(n == undefined); // true
console.log(n == null); // ture
// 일치연산자(identity operator)
// 이것을 사용하면 null 과 undefined는 구분이 명확해짐
console.log(n === undefined); // false
console.log(n === null); // true
'공부 > JavaScript' 카테고리의 다른 글
JavaScript - startsWith(), endsWith(), includes() (0) | 2024.09.02 |
---|---|
JavaScript - map과 filter 차이 (0) | 2023.07.08 |
1주차 팀과제(야구게임) (2) | 2023.06.13 |
JavaScript 문법 종합 3주차(1) (0) | 2023.06.12 |
JavaScript 문법 종합 2주차(숙제) (0) | 2023.05.25 |