본문 바로가기

개발/연구

[js 기본] 객체

기본적으로 빈 Object는 {} 또는 Object()로 생성해요.

객체 리터럴

var person1 = {
	name : 'Miracle',
	age : 20
}

console.log(person1)

//	output : {name: "Miracle", age: 20}

 

하지만 이러한 방식은 person1, person2 ... 프러퍼티만  수정하기 또한 번거롭고, *property 값만 다른 여러개의 객체를 생성하는 데에 매우 불편하죠.

*property : 속성 이란 뜻으로, JS에서는 객체 내부의 속성을 의미한다. 예를 들어 person.name의 'name'가 property이다.

생성자 함수

function Person(name, gender){
  var three = 3

  this.name = name;
  this.gender = gender;
  this.sayHello = function(){
    console.log('Hi~', this.name);
  }
}

var person1 = new Person('lee', 'male');
var person2 = new Person('kim', 'female');

console.log(person1['name']);
console.log(person1.gender)
console.log(person1.three)

//output :
//	lee
//	male
//	undefined

생성자 함수 내부에서의 this로 바인딩된 부분은 외부에서 접근이 가능하지만, 함수 내부에서 선언된 변수는 유효 범위에 따라 결정지어지게 됩니다. 쉽게 얘기해서 Person 내부의 three는 Private이고, this.name, this.gender, this.sayHello는 Public인 셈이지요.

 

delete person1.name
console.log(person1)

//	output : Person { gender: 'male', sayHello: [Function] }

delete로 해당 요소를 지울 수 있습니다. 그렇다면 요소들을 모두 조회할 수는 없을까요?

for - in 문

var person = {
  'first-name': 'Ung-mo',
  'last-name': 'Lee',
  gender: 'male'
};

// prop에 객체의 프로퍼티 이름이 반환된다. 단, 순서는 보장되지 않는다.
for (var prop in person) {
  console.log(prop + ': ' + person[prop]);
}

/* 
output :
first-name: Ung-mo
last-name: Lee
gender: male
*/

 

이렇게  각 property들을 조회할 수 있습니다. 그렇지만, 순서를 보장받을 수 없지요. Object에는 Array와는 다르게 순서가 없기 때문이지요. 그리고 for-in문은 단점이 있는데요.

// 배열 요소들만을 순회하지 않는다.
var array = ['one', 'two'];
array.name = 'my array';

for (var index in array) {
  console.log(index + ': ' + array[index]);
}

/*
output : 
0: one
1: two
name: my array
*/

이와 같이 모든 property를 조회한다는 단점이 있어요. 의도하지 않은 데이터까지 조회할 수 있죠. 그렇기에 ES6에서는 새로운 문법인 for-of문이 탄생했습니다.

for - of 문

const array = [1, 2, 3];
array.name = 'my array';

for (const [index, value] of array.entries()) {
  console.log(index, value);
}

/*
output :
0 1
1 2
2 3
*/

이와 같이 모든 property가 아닌, 배열의 요소들을 순서대로 조회할 수 있습니다.

 

Object 심화

Js의 타입에는 *원시 타입*참조 타입이 존재해요. 원시타입은 변경 불가능(Immutable)한 타입인데 반해, 참조 타입인 객체(Object)는 프로퍼티를 삽입, 삭제, 수정이 가능하기에 변경 가능(mutable)한 타입이죠.

따라서 참조 타입은 *힙 영역에 로딩이 돼요. 그리고 참조 타입은 포인터로 이루어진 값이죠. 같은 값을 비교하게 된다면 각자의 포인터를 비교하게 될거예요. 아래 예제를 보도록 해요.

var a = {}, b = {}, c = {}; // a, b, c는 각각 다른 빈 객체를 참조
console.log(a === b, a === c, b === c); // false false false

a = b = c = {}; // a, b, c는 모두 같은 빈 객체를 참조
console.log(a === b, a === c, b === c); // true true true

같은 값을 담아도 같지 않다고 표현되고 있죠? 주소(포인터)를 비교하게 되어서 그렇습니다. 대입 또한 마찬가지예요. 변수에 대입을 하게 된다면 값이 아닌 주소를 대입하게 됩니다.

var foo = { val: 10 };
var bar = { val: 10 };

console.log(foo.val, bar.val); // 10 10
console.log(foo === bar);      // false

var baz = bar;

console.log(baz.val, bar.val); // 10 10
console.log(baz === bar);      // true

이렇게 차이가 있답니다. 그렇다면, immutable한 타입에 의도치 않은 변경이 발생될 우려도 있겠지요? 해결할 수 있는 방안을 알아봅시다.

 

*힙 영역 : 힙 영역은 사용자에 의해 메모리 공간이 동적으로 할당되고 해제 됨.

*원시 타입 :

  • boolean
  • null
  • undefined
  • number
  • string
  • symbol (ES6)

*참조 타입 :

  • Object

Object.assign

ES6부터 도입된 메서드예요. IE 환경에서는 작동되지 않으니 참고해주세요. 해당 메서드를 사용하면 객체 타입을 새로운 주소에 복사를 할 수 있어요. 

그렇지만 완전한 Deep Copy는 아니예요. 객체 속 객체는 주소가 같기 때문이예요. 코드를 보도록 하죠.

const user1 = {
  name: 'Lee',
  address: {
    city: 'Seoul'
  }
};

// 새로운 빈 객체에 user1을 copy한다.
const user2 = Object.assign({}, user1);
// user1과 user2는 참조값이 다르다.
console.log(user1 === user2); // false

user2.name = 'Kim';
console.log(user1.name); // Lee
console.log(user2.name); // Kim

// 객체 내부의 객체(Nested Object)는 Shallow copy된다.
console.log(user1.address === user2.address); // true

user1.address.city = 'Busan';
console.log(user1.address.city); // Busan
console.log(user2.address.city); // Busan

이렇게 객체 속 객체까지는 복사가 되지 않습니다. 객체 속 객체는 얕은 복사(Shallow Copy)가 되지요. 이러한 방어적 복사는 비용이 발생하게 된다는 점도 유의 하셔야합니다. 그리고, 굳이 복사를 하지 않더라도 불변 객체로 만드는 방법도 있답니다.

Object.freeze

const user1 = {
  name: 'Lee',
  address: {
    city: 'Seoul'
  }
};

// Object.assign은 완전한 deep copy를 지원하지 않는다.
const user2 = Object.assign({}, user1, {name: 'Kim'});

console.log(user1.name); // Lee
console.log(user2.name); // Kim

Object.freeze(user1);

user1.name = 'Kim'; // 무시된다!

console.log(user1); // { name: 'Lee', address: { city: 'Seoul' } }

console.log(Object.isFrozen(user1)); // true

객체를 불변으로 변경함으로서 의도치 않은 변경을 막을 수 있는 방법이지요. 하지만 이 또한 객체 속 객체. 즉 깊은 동결(?)은 기대할 수 없어요.

ref :
https://poiemaweb.com/js-object
https://poiemaweb.com/js-immutability