ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 4. 불변 객체 (깊은 복사 얕은 복사)
    JavaScript/3주차-데이터타입, 실행컨텍스트, this 2023. 5. 25. 16:40

     

    이름을 변경하는 함수

     

    이름을 변경하는 함수, 'changeName'을 정의
    입력값 : 변경대상 user 객체, 변경하고자 하는 이름
    출력값 : 새로운 user 객체
    특징 : 객체의 프로퍼티(속성)에 접근해서 이름을 변경

     

    var user = {
    	name: 'jake',
    	gender: 'male',
    };
    
    // 객체의 속성을 복사하는 변경 방법
    
    var changeName = function (user, newName) {
        var newUser = user;
        newUser.name = newName;
        return newUser;
    };
    
    var user2 = changeName(user, 'sangwoo');
    
    if (user !== user2) {
    	console.log('유저 정보가 변경되었습니다.');
    }
    
    console.log(user.name, user2.name); 
    console.log(user === user2);

     

    위에 보면 

    객체의 속성을 복사하는 변경 방법을 사용했는데, 이 방법으로 하면 변경되지 않는다.

    지금 user name을 'jake'에서 'sangwoo'로 변경하려는 방법인데, 객체의 속성을 변경했으므로 변경안됨.

    하지만 아래와 같이 '객체의 속성'이 아닌, '객체 자체'를 변경하면 변경하고자 하는 이름값이 변경된다.

     

    1. 객체 자체를 변경

     

    var user = {
    	name: 'jake',
    	gender: 'male',
    };
    
    // 객체 자체를 변경하는 방법
    
    var changeName = function (user, newName) {
        return {
            name: newName,
            gender: user.gender,
        };
    };
    
    var user2 = changeName(user, 'sangwoo');
    
    if (user !== user2) {
    	console.log('유저 정보가 변경되었습니다.');
    }
    
    console.log(user.name, user2.name); 
    console.log(user === user2);

     

    다시 비교

     

    // 객체의 속성을 복사하는 변경 방법 (이 방법으로 하면 변경안됨)
    
    var changeName = function (user, newName) {
        var newUser = user;
        newUser.name = newName;
        return newUser;
    };
    
    
    // 객체 자체를 변경하는 방법 (이 방법으로 하면 변경됨)
    
    var changeName = function (user, newName) {
        return {
            name: newName,
            gender: user.gender,
        };
    };

     

     

    그러나, 객체 자체를 변경한다고 해서 다 변경되는 것은 아니다. 예를 들어 아래처럼 속성이 10개로 가정해보면 return도 10개를 해줘야한다. 속성이 1,000개면 return도 1,000개를 해줘야 한다. 이는 하드코딩임. 불가능

     

    var user = {
        name: 'jake',
        gender: 'male',
        height: 186,
        weight: 81,
        reach: 193,
        eyevision: 2.0,
        bench: 80,
        deadlift: 160,
        squat: 110,
        religion: 'christian',
    };
    
    return {
        name: newName,
        gender: user.gender,
        height: user.height,
        weight: newWeight,
        reach: user.reach,
        eyevision: newEyevision,
        bench: newBench,
        deadlift: newDeadlift,
        squat: newSquat,
        religion: user.Christian,
    };

     

    객체 자체를 복사할 경우 return값도 일일히 복사하는 하드코딩을 해야 하기 때문에, 자바스크립트에서는 '얕은 복사'를 이용한다. for ~ in 구문을 이용하여, 객체의 모든 프로퍼티(속성값)에 접근할 수 있다. 즉 하드코딩을 하지 않아도 됨.

     

    이 copyObject로 복사를 한 다음, 복사를 완료한 객체의 프로퍼티를 변경하면됨. 이렇게 하면 속성이 여러개여도 괜찮다. 

     

    2. 얕은 복사

    var copyObject = function (target) {
    	var result = {};
    
    	for (var prop in target) {
    		result[prop] = target[prop];
    	}
    	return result;
    }

     

    copyObject와 for - in을 이용한 얕은 복사 예제.

     

    var copyObject = function(target){
        var result = {}
    
    for (var prop in target){
        result[prop] = target[prop]
    }
    return result
    }
    
    var user = {
    	name: 'jake',
    	gender: 'male',
    };
    
    var user2 = copyObject(user);
    user2.name = 'sangwoo';
    console.log(user2.name);

     

    3. 깊은 복사

     

    하지만 얕은 복사도 문제가 있다. 중첩된 객체에 대해서는 완벽한 복사를 할 수가 없다.
    for - in문은 중첩된 객체(객체 안에 있는 객체)에 대해서는 복사하지 못한다.

    얕은 복사가 아닌, 깊은 복사를 해야 문제를 해결할 수 있다.

    아래를 보면 객체 user 안에 객체 urls 가 중첩되어 있다. 

     

    var user = {
        name: 'jake',
        urls: {
            portfolio: 'http://github.com/abc',
            blog: 'http://blog.com',
            facebook: 'http://facebook.com/abc',
        }
    };
    
    var user2 = copyObject(user);
    user2.name = 'sangwoo';
    console.log(user.name === user2.name);

     

    이것을 위처럼 copyObject로 복사할지라도 1depth니까 이름은 변경이 되는데, 2depth인 urls의 portfolio 주소는 변경할 수 없다.  copyObject는 forEach문처럼 전체를 한바퀴 돌면서 1depth만, 즉 객체안의 객체까지는 복사하지 않고 1depth만 돌면서 복사해오기 떄문이다.


    따라서 중첩된 객체까지 복사해오기 위한 방법이 필요함. 그게 깊은복사

     

    깊은 복사를 하기 위해선 재귀적 수행이 필요하다. 객체의 프로퍼티 중 기본형 데이터는 그대로 복사하고, 참조형 데이터는 다시 그 내부의 프로퍼티를 복사한다.

    재귀적 수행을 적용한 깊은복사 코드
    if문 : 복사하려는 대상이 object이면서 null이 아닌 경우

     

    (console.log 하나하나찍으면서 해보기. 재귀적 수행은 내가 호출될떄 내 스스로를 다시한번 안쪽에서 호출하면서 객체안의 깊은 곳까지 다 훑어보겠다는 뜻. 깊은 복사를 통해 모든 요소 하나하나를 불변성을 유지하게끔 바꾼다.)

     

    아래는 재귀적 수행을 적용한 코드

    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;
    }

    //결과 확인

    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);

    // 우린 지금 불변성을 유지하는 방법에 대해 찾고있으며, 불변성을 유지하는 방법은 얕은복사, 깊은복사가 있고 깊은복사는 재귀적수행을 이용하는 방법이 가장 알맞다.

    댓글

Designed by Tistory.