heyday2024 님의 블로그
[3주차 과제] 객체 복사, this 이해 본문
가장 아래의 코드가 실행 되었을 때, “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을 출력함.
'프론트엔드 부트캠프' 카테고리의 다른 글
[4주차 JS 문법(2)] 콜백지옥과 비동기 제어 (0) | 2024.10.22 |
---|---|
[4주차 JS 문법(1)] 콜백함수 (제어권, this 바인딩) (1) | 2024.10.21 |
[JS 기초 문법 3주차(3)] 실행 컨텍스트의 세번째 요소, ThisBindings (0) | 2024.10.20 |
Web APIs , DOM, 로컬 스토리지, 이벤트 처리 방식 (0) | 2024.10.18 |
[Github 특강] Github 이용해서 협업 과정 + 팁(dev) (2) | 2024.10.17 |