자바스크립트 객체 문제>

dev.dave·2023년 7월 29일

Javascript

목록 보기
103/167

자바스크립트 객체 문제>


결론 :

객체는 주소값을 저장한다.(별도의 공간에 실체를(실제값을) 따로 저장을 해놓는다.)

이게 핵심입니다.

(정수처럼 그냥 값을 저장하는게 아님)


var json = [
{
id : 'user1',
pw : 'user1pw',
dd : 'sadsad'
},
{
id : 'user2',
pw : 'user2pw',
dd : 'sadsad'
},
{
id : 'user3',
pw : 'user3pw',
dd : 'sadsad'
}
]

이 거는 지금, 객체의 배열이다.
(json 포맷이기도하고)

그래서 저기서
id 랑 pw 만 뽑아서
다른변수에 저장하려고 한다.

참고로 접근할때는
json[0].id
이런식으로 접근한다.

그래서

결론적으로 말하면

for문(반복문)을 돌리는것 까지는 맞다.
근데 어떻게 돌리는가
객체는 여기서 어떻게 적용이 되서 진행이 되는가에 대한 부분을 배워보자.

일단

for문을 돌려보자...

처음에는 코드를 이렇게 짜봤다.(결론은 원하는 결과물이 안나옴)

var jsonResult = []; // 여기에 최종 결과물을 담아 배열로담아 출력한다.

var temp = {}; // 여기는 이제 임시로 결과물 받아서 jsonResult 여기로 다시 저장하는 그런 역할 하는곳

for(var i = 0 ; i < json.length ; i++){
temp.id = json[i].id;
temp.pw = json[i].pw;
jsonResult.push(temp);

}

console.log(jsonResult);

그래서 json.length 해서 json배열안의 객체만큼 반복문을 돌고
그래서 안의 내용이 실행이되는건데,,,

이제,, id 랑 pw 를 템프.id 이런식으로 객체 형식으로 담아서 ~해서
jsonResult에 푸쉬로 temp내용을 넣겠단 이야기다.

뭔가 그럴싸하지만,,,

결과물은

id : user3 , pw : user3pw
id : user3 , pw : user3pw
id : user3 , pw : user3pw

이렇게 끝놈만 3번 출력된다.

내가 원한건 유저1 유저2 유저3 다나오는건데,,,ㅡㅡ;

이건 스코프문제는 아니다.(익명함수 처리해도 똑같다)
그럼 뭐가 문제냐면,

객체의 특성 문제이다.

일단 이걸 배우고 가자...

var num = 1;
var num2 = 1;

var obj1 = { id : 10 };
var obj2 = { id : 10 };

if( num === num2 ){console.log('num true');} //요게 찍힌다
if( obj1 === obj2 ){console.log('obj true');}

이거는 결과가 뭐냐면

num true 가 나온다

객체도 분명 값이 같은데 ,,, 참이라고 안한다.

근데

var num = 1;
var num2 = 1;

var obj1 = { id : 10 };
var obj2 = obj1; // 이부분을 요렇게 바꾸면

if( num === num2 ){console.log('num true');} // 참
if( obj1 === obj2 ){console.log('obj true');} //참

둘다 콘솔로그에 다 참으로 찍힌다...

즉,

사실 객체들끼리 첫번쨰 예문처럼 비교하는건 의미가없다.

사실 객체 끼리 비교하려면,

그 안쪽 id에 접근해서 비교를해야 진짜 비교가 된다.

obj1.id === obj2.id 이렇게 말이다.

일단,

우리가 변수를 선언하면 num1 = 1; 이렇게 하면,


이런 메모리 공간이 있을때,
저기서 한 공간에 1을 넣어버리고, 이름은 num
이렇게 된다.

아무튼 변수는 저렇게 저장된다는 거다.

그리고

컴퓨터가 찾을때도,
num을 찾으면 거기에 1이 있으니까 그걸 가져오는거다.

즉, 메모리 공간에 1이라는 값을 넣고, 라벨로 변수명을 붙여놓는다는거다.

그럼 만약
var1 = var2
이렇게 하면

var2 공간에 var1의 값을 복사해 와서 var2공간에 붙여넣기하는거다.

그러나

객체는 독특하게 동작? 한다.

var obj1 = { id : 10 };

이라고 하면,

변수처럼 그냥 메모리공간에 값을 넣고 라벨을 붙이고 이렇게 안한다.

객체를 변수에 저장할때는 주소 값을 넣는다.
즉,
임의의 메모리 공간에 진짜값을 저장한다.
{ id : 10 }; 이게 어딘가 저장이 된다 예를들어 12번방에 저장이 되고,그리고
3번방 값은 주소값 '12번방' 이라고 저장을 하고
obj1 이라는 이름의 라벨을 단다(변수명)

즉, 컴퓨터가 obj1찾을때
obj1이라는 변수를 찾으니
위치가 3번방에 저장되있고,
들어가 까봤더니 값이 주소값이네?
12번방으로 가보세요 이렇게 되있다.
그럼 12번방으로 가보니 { id : 10 }; 요 값이 딱 저장이 되있더라 라는것이다.

즉, 객체를 변수로 저장하면,

진짜 값은 따로 저장되 있고,
변수명에 있는 값은 주소값이다 라는 말이다.
그럼 그 주소를 따라 가면 진짜 값이 나오는거고,

그래서

저 위의 코드를 다시 보면,

var num = 1;
var num2 = 1;

var obj1 = { id : 10 };
var obj2 = obj1; // 이부분을 요렇게 바꾸면

if( num === num2 ){console.log('num true');} // 참
if( obj1 === obj2 ){console.log('obj true');} //참

여기 코드에서

var obj2 = obj1;
요 부분이 뭐냐면
obj2에 obj1을 대입 시켜라 라는 말이다.
그럼
까고 보면
일단 그럼
obj1의 변수에 가면 주소값이 있겠쥬?
그럼 예를들어 12번방에 주소값이 있으면,
12번 가서 값을 가지고 복사해오는게 절대 아니다.
그냥
주소값만 복사해온다.
즉, 주소값이 12번방 이라고 했을때
12번방 이라는 연결고리만 복사해와서 obj2에 대입시키는거다.

그러면 obj2에 저장된 값은 즉, 12번방 이라는 주소값이 저장이 되있고,
obj2를 콘솔에 찍으면,
12번 주소값으로 가서 그안에 내용을 가져온다.

그럼
즉, 같은 주소값을 공유한다는말이 되는거고,
같은 주소를 가리키게되는거고,
즉, 공유되니까 누가 12번방의 값을 변경시키면, 변경된 값이 출력이 되겠쥬?

고로, obj1 에서 값을 변경시키면 obj2를 찍어도 변경된 값이
나올꺼고,
obj2에서 변경시키면, obj1을 찍으면 변경된값이 뜰것이다.
왜냐면 둘다 주소값을 공유해서 결국 같은 값을 소유한 것이니
변경을 하게되고 변경된 값을 서로 가지고있는 셈이되는거다.

*여기서 주소값은 참조값이라고 부르기도한다.

다른 예제를 보자>>>>>>>

var num1 = 10;
var num2 = num1;
console.log('num1' , num1); // 10

num1 = 11;
console.log('num1' , num1); // 11

var obj1 = { id : 10 };
var obj2 = obj1;
console.log('obj1.id' , obj1.id);

obj2.id = 11;
console.log('obj1.id' , obj1.id);

여기서 num1 과 num2 는 별개이다.
즉 각자 저장공간을 가지고있어서
그 공간에 복사를 해오는거고,

객체는 각자 공간이 있다,
근데 그 공간에 주소값이 저장이 되있어서
그럼 그 주소를 따라서 거기서 값을 얻어오는 방식이다.

그래서 obj2.id 에서 값을 변경하면
obj1.id 에서도 수정된 값이 나오는거다.
왜냐면 공유하니까...

주소값(참조값)

여튼 참조값은 하나고 여러군데 복사해줄수 있다.

var obj1 = { id:10 };
var obj2 = obj1; //참조값공유
var obj3 = obj2; //참조값공유
var obj4 = obj3; //참조값공유
obj3.id++; //값수정
obj4.id++; //값수정
console.log(obj1.id); //12

즉, 실체는 하난데, 저렇게 참조값을 복사하고,
변수명은 여러개가 될수 있는거죠

그리고 그 변수명 무엇을 조작하든 실체인 { id:10 } 이란 객체를 제어할수있구요.

그리고

var obj1 = { id:10 };
var obj2 = { id:10 };

이 두 객체는 일단 서로 다른 객체이죠,
즉, 각자의 공간을 가지고 있고 , 각자의 주소값을 가지고있고,
각자의 주소값에 해당된 값이 거기에 저장이 되있겠죠.
아무튼 두 객체는 서로다른객체에요 아직까지는 공유한게 없어요.

{} 를 할때마다
새로운 객체가 만들어지고,
그 주고값(참조값)을 가져온다고 보시면 되요.

이건 언어의 동작 방식입니다.
이 방식이 더 효율적이라고 생각했기에
이런식으로 설계한 거겠죠.

기본적으로
객체는 큰 메모리를 차지합니다.
그렇기에
일반적인 정수처럼 막 할당을 해줄 수 가 없는거에요.
그래서 객체를 많이 만들면 메모리를 많이 먹게되는거죠,
그래서
메모리 가비지 컬렉션이란게 있는데

자바스크립트에서는

var obj1 = { id:10 };
이렇게 하고 나중에

var obj1 = null;
이렇게 해주면
메모리는 다시 반환이 되는 처리를 해주는거죠

다시 사용가능한 메모리로요.

아무튼,

다시 본문으로 돌아가서,!!

(function (){

var json = [
{
id : 'user1',
pw : 'user1pw',
dd : 'sadfasdf',
},
{
id : 'user2',
pw : 'user2pw',
dd : 'sadfasdf',
},
{
id : 'user3',
pw : 'user3pw',
dd : 'sadfasdf',
}
]

var jsonResult = [];
var temp = {};

for (var i=0; i<json.length; i++){
(function (i,json){
temp.id = json[i].id;
temp.pw = json[i].pw;
jsonResult.push(temp);
})(i,json);
}

console.log(jsonResult);

})();

이 코드에서

일단 포문은 잘 동작했고,
근데
temp가 문제인것이죠.

temp는 객체죠.

포문 밖에 있는 객체요.

그리고

temp는 객체니까 주소값이 있다는 말이죠?
즉, temp의 선언과 동시에 주소값을 할당 받았습니다.
(주소값은 하나를 할당받았죠!)

그래서

jsonResult.push(temp); 에서

저 temp를 배열에 넣고 하는건데요.

다시 포문으로 가면요,

처음 반복 돌떄는,

id : 'user1',
pw : 'user1pw',

이렇게 한번 돌겠죠,

그리고 두번쨰 돌고,,

근데 문제는

저 temp의 주소값이 하나라는 사실입니다.

jsonResult에 객체가 세개가 들어가는데

그 세개의 객체 변수는 ,사실은 하나의 실체(주소값)만 가르켜요

왜냐면

반복문돌고 다 temp 로 들어가잖아요.
근데 그럼 한주소값에 들어간다는 소린데,
그러면, 한번돌면 한번 저장되고, 그다음 또저장 될텐데, 그면 덮어쓰기를 하는거고, 그다음 돌때 또 덮어쓰기하는 겁니다.
그면 temp에는
뭐가 남냐면

temp의 주소값으로 들어가보면 값은

id : 'user3',
pw : 'user3pw',

요렇게 딱 마지막 값만 저장이 달랑 되있더라 라는 겁니다.

즉,

포문 한번돌때,

temp 로 저장되고 바로 jsonResult 으로 저장이 되는데,

첫번째 돌때는

id : 'user1',
pw : 'user1pw',

요게 jsonResult 안에 0번쨰로 저장이 되겠죠,

그럼

그 다음 반복문 돌면

id : 'user2',
pw : 'user2pw',

요게 temp 로 가서 저장이되고 (덮어쓰기)

그리고

jsonResult 이리로 1번쨰로 저장이 됩니다.

그럼 현재 jsonResult배열 안에는 총 2개의 객체가 저장되있고,

(0번째 1번쨰 이렇게)

그러면

0번쨰는
id : 'user1',
pw : 'user1pw',

요랫는데

포문이 두번돌면서 덮어쓰기했으니

id : 'user2',
pw : 'user2pw',

요렇게 수정이 되겠죠
(왜냐면 같은 주소값를 가르키고 있으니까!(공유되니까))

그리고
1번쨰 배열에

id : 'user2',
pw : 'user2pw',

요게 새로 저장이 됩니다.

그리고 마지막으로 포문이 돕니다.

그럼
temp를 거쳐서 (또 덮어쓰고)

id : 'user3',
pw : 'user3pw',

이렇게요,,,
그리고

jsonResult배열 에 저장이 이어서 됩니다.

즉, 총 3개의 객체가 최종적으로 jsonResult배열 에 저장이 된 상태고,

그 내용은

0번째
id : 'user3',
pw : 'user3pw',
(이미 수정됬고 아까 두번쨰 돌떄~)

1번쨰
id : 'user3',
pw : 'user3pw',
(방금 마지막 포문 돌고나서 temp에서 덮어써서 요렇게 수정됨)

그리고

2번째
id : 'user3',
pw : 'user3pw',
(마지막으로 더해진 값 그대로임)

그래서

결과물이 덮어쓰인채로

id : 'user3',
pw : 'user3pw',
요것만 3번 저장이 됩니다.

그래서 해결법이 있습니다.


해결법>>>>>>>>>>>>>>>>>>>>>>>>

var jsonResult = [];
var temp = {}; // 여기를 삭제하고

for (var i=0; i<json.length; i++){
var temp = {}; // 이쪽으로 옴김
temp.id = json[i].id;
temp.pw = json[i].pw;
jsonResult.push(temp);

}

console.log(jsonResult);

즉, 포문 밖에 있던 var temp = {}; 를
포문 안에 같이 넣어서
실행하는 건데요,

이렇게 되면

포문이 돌때마다,
객체를 새로 할당하겠죠
즉, 각각의 temp 객체가 만들어지는 겁니다.
그러니까
temp 가 총 각각 3개 만들어지는데요,
즉, 각자의 주소값을 가진 temp 가 된다는 말이고,
그럼 그 temp를
jsonResult 에 저장하면

유저1 유저2 유저3 이렇게 원하는대로 값이 나옵니다.

즉,

temp를 보니 객체구나,
객체니까 메모리에 또 다른 메모리 주소값이 있군,
(그리고 그 메모리 주소값을 참고해서)
{id:'user1',
pw:'user1pw'}
이걸 넣고

두번쨰 포문돌고,
temp = {
{id:'user2',
pw:'user2pw'}
}

아 temp 네 객체네 그럼 메모리에 또 다른 메모리 주소값이 있군,
이런식으로,,,

결론은

정리하자면,

객체는 주소값을 저장한다.(별도의 공간에 실체를(실제값을) 따로 저장을 해놓는다.)

이게 핵심입니다.

(정수처럼 그냥 값을 저장하는게 아님)

그래서

막 주소값 체인도 만들수 있다.

암튼,,그렇다..끝

profile
🔥개인 메모 / 다른블로그 자료 참조 / 다른블로그 자료 퍼옴 (출처표기) /여기저기서 공부 했던 내용 개인메모 & 참고 / 개인 기록 용도 블로그 입니다.🔥

0개의 댓글