Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

heyday2024 님의 블로그

[3주차 과제] 객체 복사, this 이해 본문

프론트엔드 부트캠프

[3주차 과제] 객체 복사, this 이해

heyday2024 2024. 10. 20. 20:07

가장 아래의 코드가 실행 되었을 때, “Passed ~” 가 출력되도록 getAge 함수를 채워주세요

var user = {
  name: "john",
  age: 20,
};

var getAged = function (user, passedTime) {
  // 여기를 작성해 주세요!
  // 깊은 복사 방식을 선택할까했지만, user라는 객체는 depth 가 1인 for loop로도 충분히 복사가 되고,
  // 상황에 맞는 얕은 복사 이용이 깊은복사보다 성능이 좋기 떄문에 나는 얕은 복사를 해볼 것이다.
  let newUser = { ...user };
  newUser.age += passedTime;
  return newUser;

  // let newUser = {};

  // for (let property in user) {
  //   newUser[property] = user[property];
  // }

  // newUser['age'] += passedTime;
  // return newUser;
};

var agedUser = getAged(user, 6);

var agedUserMustBeDifferentFromUser = function (user1, user2) {
  if (!user2) {
    console.log("Failed! user2 doesn't exist!");
  } else if (user1 !== user2) {
    console.log(
      "Passed! If you become older, you will be different from you in the past!"
    );
  } else {
    console.log("Failed! User same with past one");
  }
};

agedUserMustBeDifferentFromUser(user, agedUser);

- 얕은 복사는 위처럼 spread operator 를 사용해도되고, for 구문을 사용해도 된다.

- 개인적으로 spread operator 사용이 아무래도 훨씬 가독성도 좋고 편리한듯.

 

---- 객체 안에 객체가 있다는 가정 하에 깊은 복사도 해봤다--------
1. 재귀적 방법

var user = {
  name: "john",
  age: 50,
  kids: {
    Sophie: { age: 24, gender: "F" },
    Jane: { age: 19, gender: "F" },
    Caleb: { age: 21, gender: "M" },
    Tom: { age: 23, gender: "M" },
  },
};
console.log(typeof user["kids"] === Object);
console.log(typeof user["kids"] === "object");
var getAged = function (user, passedTime) {
  // 여기를 작성해 주세요!
  //만약 중첩객체를 속성으로 갖고 있다면 깊은 복사도 해보자.
  // 1. 재귀적 방법
  function deepCopy(obj) {
    let newUser = {};
    for (let prop in obj) {
      if (typeof obj[prop] === "object" && obj[prop] !== null) {
        newUser[prop] = deepCopy(obj[prop])
      }
      else {
        newUser[prop] = obj[prop];
      }
    }
    return newUser;
  }

  let result = deepCopy(user)
  result.kids['Jane'].age += passedTime;

  return result;
};

var agedUser = getAged(user, 6);

var agedUserMustBeDifferentFromUser = function (user1, user2) {
  if (!user2) {
    console.log("Failed! user2 doesn't exist!");
  } else if (user1 !== user2) {
    console.log(
      "Passed! If you become older, you will be different from you in the past!"
    );
  } else {
    console.log("Failed! User same with past one");
  }
};

agedUserMustBeDifferentFromUser(user, agedUser);
console.log(agedUser);

- 직관적이지만, 써야하는 코드가 다른 두방법에 비해 조금 많은 편이다.

 

2. JSON.stringfy, parse 사용(단, 함수나 undefined 등 JSON으로 변경할 수 없는 속성은 사용 불가..) 

var user = {
  name: "john",
  age: 50,
  kids: {
    Sophie: { age: 24, gender: "F" },
    Jane: { age: 19, gender: "F" },
    Caleb: { age: 21, gender: "M" },
    Tom: { age: 23, gender: "M" },
  },
};
console.log(typeof user["kids"] === Object);
console.log(typeof user["kids"] === "object");
var getAged = function (user, passedTime) {
  // 여기를 작성해 주세요!
  //만약 중첩객체를 속성으로 갖고 있다면 깊은 복사도 해보자.

  // 2. JSON.stringfy, parse 사용

  let newUser = JSON.stringify(user);
  let result = JSON.parse(newUser);

  result.kids["Jane"].age += passedTime;

  return result;

};

var agedUser = getAged(user, 6);

var agedUserMustBeDifferentFromUser = function (user1, user2) {
  if (!user2) {
    console.log("Failed! user2 doesn't exist!");
  } else if (user1 !== user2) {
    console.log(
      "Passed! If you become older, you will be different from you in the past!"
    );
  } else {
    console.log("Failed! User same with past one");
  }
};

agedUserMustBeDifferentFromUser(user, agedUser);
console.log(agedUser);

- 확실히 편리하고 작성하기 쉽지만, 함수나 undefined와 같은 속성은 복사가 안된다는 한계가 있다.


 

3. lodash 라이브러리의 deepClone 메서드 사용

// import _ from 'lodash';

var user = {
  name: "john",
  age: 50,
  kids: {
    Sophie: { age: 24, gender: "F" },
    Jane: { age: 19, gender: "F" },
    Caleb: { age: 21, gender: "M" },
    Tom: { age: 23, gender: "M" },
  },
};
console.log(typeof user["kids"] === Object);
console.log(typeof user["kids"] === "object");
var getAged = function (user, passedTime) {
  // 여기를 작성해 주세요!
  //만약 중첩객체를 속성으로 갖고 있다면 깊은 복사도 해보자.

  // 3. lodash의 cloneDeep 메소드 사용
  const _ = require('lodash');
  let newUser = _.cloneDeep(user);
  newUser.kids["Jane"].age += passedTime;

  return newUser;
};

var agedUser = getAged(user, 6);

var agedUserMustBeDifferentFromUser = function (user1, user2) {
  if (!user2) {
    console.log("Failed! user2 doesn't exist!");
  } else if (user1 !== user2) {
    console.log(
      "Passed! If you become older, you will be different from you in the past!"
    );
  } else {
    console.log("Failed! User same with past one");
  }
};

agedUserMustBeDifferentFromUser(user, agedUser);
console.log(agedUser);

  // 1. 재귀적 방법
  // function deepCopy(obj) {
  //   let newUser = {};
  //   for (let prop in obj) {
  //     if (typeof obj[prop] === "object" && obj[prop] !== null) {
  //       newUser[prop] = deepCopy(obj[prop])
  //     }
  //     else {
  //       newUser[prop] = obj[prop];
  //     }
  //   }
  //   return newUser;
  // }

  // let result = deepCopy(user)
  // result.kids['Jane'].age += passedTime;

// return result;
  

  // 2. JSON.stringfy, parse 사용

  // let newUser = JSON.stringify(user);
  // let result = JSON.parse(newUser);

  // result.kids["Jane"].age += passedTime;

  // return result;

- 정말 간단하고 편리하다. 라이브러리를 가져오기 위해 require을 사용했다. (모듈 형식에 따라 import로 가져오는 것과 require로 가져오는 것이 다름: ES 모듈을 사용한다면 import, Common JS 모듈을 사용하면(.js 파일), require 사용.) 

- 개인적으로는 deep Copy를 해야한다면 앞으로 lodash 라이브러리를 사용할 것 같다.

 

 

궁금해서 각각의 깊은 복사 방법의 성능을 찾아봤는데, (시간, 공간 효율) 모두 크게 다르지 않아서 성능 차이는 크지 않지만, 상황에 따라 편한대로 달리 쓰면 될 것 같다.

 

  • 일반적인 깊은 복사가 필요한 경우 Lodash의 _.cloneDeep를 사용하는 것이 가장 안전하고 효율적.
  • 특정한 커스터마이징이 필요하다면 재귀 함수를 사용권장.
  • 단순한 객체로, 함수나 순환 참조가 없는 경우에는 JSON의 stringify와 parse를 사용해도 좋을 듯.

 

 

출력의 결과를 제출해주세요, 그리고 그 이유 설명

var fullname = 'Ciryl Gane'

var fighter = {
    fullname: 'John Jones',
    opponent: {
        fullname: 'Francis Ngannou',
        getFullname: function () {
            return this.fullname;
        }
    },

    getName: function() {
        return this.fullname;
    },

    getFirstName: () => {
        return this.fullname.split(' ')[0];
    },

    getLastName: (function() {
        return this.fullname.split(' ')[1];
    })()

}

console.log('Not', fighter.opponent.getFullname(), 'VS', fighter.getName());
//Not Francis Ngannou VS John Jones

console.log('It is', fighter.getName(), 'VS', fighter.getFirstName(), fighter.getLastName);
//It is John Jones VS Ciryl Gane

 

1. fighter.opponent.getFullname(): 

호출의 주체가 fighter.opponent로, opponent 객체의 fullname은 Francis Ngannou임.

 

 

2. fighter.getName():

호출의 주체가 fighter로, fighter 객체의 fullname은 John Jones임.

 

 

3. fighter.getFirstName():

fighter 객체의 getFirstName은 화살표 함수이고, 화살표 함수는 this를 바인딩하지 않음 그래서 이미 바인딩된(제일 마지막에 바인드된) this, 즉, 상위 스코프에서 정의된 this를 가리키게 됨. 그것이 바로 전역객체이고, 이 전역 객체에서 전역변수, full name 에 이미 값이 할당되어있는데 그것이  Ciryl Gane임 하지만, 여기서 split(' ')[0] 메서드에 의해 첫번째 단어만 출력됨.

 

4. fighter.getLastName:

잘보면 fighter.getLastName은 뒤에 '()'가 없음 즉, 이 친구는 메소드가 아님. 이 친구는 즉시실행함수에 의해 이미 그 반환값을 가져버린 속성임. 호출되어서 실행된것이 아닌 이미 정의 되어졌을때 즉시 실행되었음으로 호출 주체가 없음. 그래서 this가 전역객체를 가리키고, 위에서 말했듯 전역변수 full name에 할당된 Ciryl Gane을 가져오는데 split(' ')[1] 에 의해 두번째 단어인 Gane을 출력함.