whatisthis?
javaScript. (22) 객체의 복사 본문
자바스크립트에서는 일반 String / Number / Boolean 값을
다음과 같이 복사(copy)할 수 있다.
const string = 'hello';
const copy = string;
copy; // 'hello'
function sayHello() {
console.log('hello');
}
const copyFunc = sayHello;
자바스크립트에서는 null과 undefined 를 제외한 모든 타입이 객체이므로
'객체' = 함수, 배열, 일반 객체도 복사할 수 있다.
❕ 단, 복사된 값을 조작할 때 차이가 있다.
let string = 'hello';
let copy = string;
copy = 'bye'; // 복사본의 값을 바꿔줌
string; // 'hello' ㅡ 원본은 변화X
위 예제에서 복사본인 copy의 값을 'bye'로 바꿔보았다.
copy의 값이 바뀌어도, 원본 string의 값은 변화가 없다.
그렇다면, 객체의 경우엔 어떨까? (배열 / 일반객체 / 함수)
❗ 참고 : 상속이 없는 '일반객체' 만 해당함.
let array = ['a', 'b,' 'c'];
let copyArr = array; // 배열(객체) 복사
copyArr[0] = 'change'; // 복사본 변경
console.log(array); // ['change', 'b', 'c'] ㅡ 원본배열이 바뀜
객체의 경우에는,
복사본을 변경해도 원본까지 바뀌게 된다.
copyArr 변수에 array 배열을 대입하여 복사했는데,
이때 '객체'의 특징으로
다른 변수에 대입할 때 값을 복사하는게 아니라 참조(메모리 주소)를 복사한다.
>> 문자열 / 숫자 / 불린 제외한 객체 모두.
변수(variable)은 모두 메모리에 저장된다.
그리고 대입을 하면 변수의 이름은 저장된 메모리의 주소를 가리킨다.
>> 즉, 대입을 통해 복사한다면 값은 하나인데 변수는 여러개인 것( = 주소가 여러개이므로 )
이것을 참조(Reference) 라고 한다.
위에서 copyArr 변수가 바뀌면 array값이 바뀌는 것이 바로 이 '참조' 때문이다.
ㅡ copyArr 변수는 array변수를 참조하고 있어서, 값은 하나지만 주소만 다르므로
값이 변경되면 원본의 값도 바뀌게 된다.
참조 현상이 일어나지 않게 하려면
메모리에 ['a', 'b', 'c']를 두개 만들어야 한다.
(즉, 값이 하나가 아니라 두개가 되도록 따로따로 운영)
객체는 대입(=)시 기본적으로 값(value)이 아닌 주소(reference)를 복사하므로
임의로 조작을 해야한다.
let array = ['a', 'b', 'c'];
let copy = Array.prototype.slice.call(array);
copy[0] = 'change';
console.log(array); // ['a', 'b', 'c']
위 예제에서는
1) Array.prototype.slice로 배열을 그대로 반환하도록 하였다.
- array.slice(0)과 같으므로 0개만큼 자르는 것과 같다 = 즉, 원본 배열 그대로 반환함
- array.slice(0)을 해도 좋지만, argument같은 것을 복사하려면 Array.prototype.slice를 한 후 call을 하는것이 좋음.
>> 배열이 아니라 유사배열일 때는 배열의 메서드를 못 쓰므로
2) call()을 통해 this값을 array로 설정해주고 호출한다.
복사(Copy)
📌 얕은복사(shallow copy)
- 가장 상위 객체만 새로 생성되고, 내부 객체들은 참조관계인 경우.
📌 깊은복사(deep copy)
- 내부 객체까지 모두 새로 생성된 것.
** 위 예제는 내부 객체가 없어서 shallow인지 deep인지 정하기 애매함.
1 / 얕은 복사 (shallow copy) - 예제
let array = [{ name: 'a' }, { name: 'b' }, { name: 'c' }]; // 객체(배열) 안에 내부객체 존재
let shallow = Array.prototype.slice.call(array);
shallow[0].name = 'd'; // 내부 객체의 값은 바뀜 (name)
shallow[1] = 'e'; // 내부 객체 자체는 바뀌지 X
console.log(array); // [{ name: 'd' }, { name: 'b' }, { name: 'c' }]
위 예제에서는 shallow[0].name은 array(원본)에 적용되지만(즉, 값이 바뀌지만)
shallow[1]은 적용되지 않는다. (내부객체 그 자체는 안바뀜)
>> 얕은 복사 (shallow copy)
** 가장 상위 객체에 직접 변경하는 것은 적용되지 않지만,
그 내부 객체들은 참조(reference)로 아직 이어져있어서 바뀌는 것임.
즉, 가장 바깥 껍데기만 복사됨. (안에 내부까지는 X)
2 / 깊은 복사 (deep copy) - 예제
function copyObj(obj) {
let copy = {};
if (Array.isArray(obj)) { // 1️⃣ obj가 배열이면
copy = obj.slice().map((v) => { // copy에 원본 배열 복사 ㅡ slice 이용 + map
return copyObj(v);
});
} else if (typeof obj === 'object' && obj !== null) { // 2️⃣ (null이 아닌) 객체면 (typeof 오류로 인해 null이 object로 뜸)
for (let attr in obj) { // for ... in문으로 obj 안에 속성들
if (obj.hasOwnProperty(attr)) { // 자기자신의 속성이면 (상속이 아니면)
copy[attr] = copyObj(obj[attr]); // 객체 복사 ㅡ 각 속성들을 객체 속성으로 저장
}
}
} else {
copy = obj; // 3️⃣ 일반형이면 (Number, String, Boolean)
}
return copy;
}
let obj = { a: 1, b: 2, c: [{ d: null, e: 'f' }] }; // obj 선언
let obj2 = copyObj(obj); // 함수 리턴값 ㅡ copy 저장
obj2.a = 3; // 🎲 값 변경해도
obj2.c[0].d = true;
console.log(obj.a); // 1 // ❌ 안바뀐다. ㅡ 내부 객체의 값도 적용 X
console.log(obj.c[0].d); // null
💡 for ... in
- obj 안의 키(key)를 순서대로 반복한다.
- 만약, 키가 숫자라면 순서대로 반복되지 X. (즉, 문자열인 키에만 사용할 것)
이때, for ... in을 돌리면 자신의 속성이 아닌 [[prototype]]에 존재하는 상속된 객체의 속성도 반복되므로
if (obj.hasOwnProperty(attr))를 통과한 obj 자신의 속성만이 복사되도록 하는 것.
+) 함수의 복사
- 함수는 복사시 bind를 하면 된다.
- this를 기존 함수와 같게 하면 함수가 똑같이 복사된다.
let func = function () {
alert('hi');
};
func2 = func.bind(this);
func2(); // 'hi'
REFERENCE
https://www.zerocho.com/category/JavaScript/post/5750d384b73ae5152792188d
이 포스팅은 zerocho님의 javascript 강의를 보고 작성한 글입니다.
공부+기록 용으로 작성한 것이며, 자세한 것은 위 포스팅을 참고하세요!
'WEB STUDY > JAVASCRIPT' 카테고리의 다른 글
javaScript. (24) 함수형(Functional) 프로그래밍 (0) | 2022.01.30 |
---|---|
javaScript. (23) 디자인 패턴 (싱글턴 / 모듈 / 생성자) (0) | 2022.01.30 |
javaScript. (21) 함수의 메소드와 argument (0) | 2022.01.30 |
javaScript. 비동기 처리와 Callback 함수 (0) | 2022.01.30 |
javaScript. (20) Event Listener / Callback (0) | 2022.01.29 |