[JS Data-types(데이터 타입)] 객체(object)

Chanki Hong·2022년 11월 15일
0

JavaScript

목록 보기
10/30
post-thumbnail

객체(object)

  • 단 하나의 값. (유일함)
  • 불변이었던 원시 값들과는 달리 여러 가지 값을 가질 수 있으며, 변할 수도 있음.
  • 본질적으로 컨테이너.
  • 내용물이 바뀌더라도 컨테이너가 바뀌는 건 아님.
  • 하나의 객체 리터럴에 여러 종류의 값(프로퍼티; property 또는 멤버)을 넣을 수 있음.
  • 어떤 타입이든 상관없고 다른 객체여도 괜찮음.
  • 배열과 다르게 순서가 보장되지 않음.
  • key(키)value(값)로 이루어짐.
  • 마치 여러개의 변수의 식별자(identifier)와 값(value)이 뭉쳐있는 것과 유사.
  • 키(이름)는 반드시 문자열 또는 심볼. (프로퍼티 식별자이기 때문에 식별자와 같음)
  • 또한 공백이 불가능 하고 ' (따옴표)로 감싸주면 가능.
  • 다만 유효한 식별자가 아님.
  • 객체에는 데이터 프로퍼티접근자(accessor) 프로퍼티가 존재.
  • 접근자 프로퍼티는 getter 함수와 setter 함수가 있고 나머지는 데이터 프로퍼티.
// hong1객체와 hong2객체는 프로퍼티는 같지만, 다른 객체를 의미함.(다른 주소를 가진 공간)
const hong1 = {
  name: 'Hong',
  age: 40,
};
const hong2 = { name: 'Hong', age: 40 }; // 한줄 선언.

객체 생성과 프로퍼티

  • 원시값도 const 선언이 권장 사항이지만, 객체는 필연적.
  • 원시값과 다르게 const 임에도 수정 할 수 있는 이유는 원시값은 불변 값 그 자체지만 객체는 컨테이너이기 때문에 확보한 일정 공간을 의미.
  • 일정 공간의 주소가 변하는 것이 아닌, 내용물이 변하는 것을 의미.
  • 객체는 {} 로 생성.
    • JS에서 {}는 코드 블럭, 객체, 비구조화 할당을 의미함.
  • 선언과 동시에, 객체의 프로퍼티를 할당 가능.
//ex1.
const ironMan = {
  name: '토니 스타크',
  actor: '로버트 다우니 주니어',
  alias: '아이언맨',
};
  • 선언 먼저 하고, 필요할 때 프로퍼티를 추가 가능.
//ex2.객체를 선언 한 뒤 프로퍼티를 추가하는 방법.
const obj = {}; // 객체를 먼저 선언한 뒤,
obj.color = 'yellow'; // color라는 프로퍼티에 'yellow'라는 값을 담아 객체에 추가.
  • 프로퍼티 제거시 delete 연산자 사용.
const hong1 = { name: 'Hong', age: 40 };
hong1.face = 'suck';
console.log(hong1); // { name: 'Hong', age: 40, face: 'suck' }
delete hong1.face;
console.log(hong1); // { name: 'Hong', age: 40 }

프로퍼티(멤버) 접근법

비구조화 할당(destructuring assignment)

  • 객체(배열 포함)를 변수(상수)로 해체.

객체 비구조화 할당

  • 변수(상수) 이름(식별자)과 객체의 프로퍼티 이름(key)이 일치.
  • 유효한 프로퍼티의 이름만 해당.
const ironMan = {
  name: '토니 스타크',
  actor: '로버트 다우니 주니어',
  alias: '아이언맨',
};

const { actor } = ironMan;
console.log(actor); // 로버트 다우니 주니어 출력.
//객체 선언.
const obj = { b: 2, c: 3, d: 4 };
//선언과 할당을 동시에 진행한 해체 할당.
const { a, b, c } = obj;
console.log(a); // undefined
console.log(b); // 2
console.log(c); // 3
console.log(d); // ReferenceError: d is not defined
// d라는 이름(key)을 가진 프로퍼티가 해체 할당 단계에서 없기 때문에, 선언조차 불가함.
  • 변수 선언과 비구조화 할당을 나누어 할때.
  • JS는 표현식 좌변을 블록으로 해석하기 때문에 할당만 하는 경우에는 괄호가 필연적임.
  • 즉, {}가 비구조화 할당을 의미한다는 것을 표현해야함.
  • 선언과 할당이 동시에 일어나는 비구조화 할당은 const 또는 let 키워드가 함께 표함되어 있어 괄호가 필요하지 않음.
const obj = { b: 2, c: 3, d: 4 };
let a, b, c;

{ a, b, c } = obj; // 에러 발생.

// 괄호를 이용하여 해결.
const obj = { b: 2, c: 3, d: 4 };
let a, b, c;

({ a, b, c } = obj);
console.log(a);
console.log(b);
console.log(c);
  • 함수에서 파라미터로 객체를 받아 값을 조회.
const ironMan = {
  name: '토니 스타크',
  actor: '로버트 다우니 주니어',
  alias: '아이언맨',
};

function print(hero) {
  const { alias, name, actor } = hero; // 비구조화 할당.
  const text = `${alias}(${name}) 역할을 맡은 배우는 ${actor} 입니다.`;
  // 객체의 값을 조회할때 객체 식별자가 필요 없어짐.
  console.log(text);
}

print(ironMan); // 아이언맨(토니 스타크) 역할을 맡은 배우는 로버트 다우니 주니어 입니다.
  • 파라미터 단계에서 적용 가능. (더욱 간결)
const ironMan = {
  name: '토니 스타크',
  actor: '로버트 다우니 주니어',
  alias: '아이언맨',
};

function print({ alias, name, actor }) {
  const text = `${alias}(${name}) 역할을 맡은 배우는 ${actor} 입니다.`;
  console.log(text);
}

print(ironMan); // 아이언맨(토니 스타크) 역할을 맡은 배우는 로버트 다우니 주니어 입니다.
  • key 를 그대로 변수이름으로 쓰지 않고, 새로운 이름에 할당.
  • let {key: newName} = object
const animal = {
  name: '멍멍이',
  type: '개',
};

const { name: nickname } = animal;
console.log(nickname); // 멍멍이

객체 깊은 곳을 비구조화 할당

const deepObject = {
  state: {
    information: {
      name: 'coldair',
      languages: ['korean', 'english', 'chinese'],
    },
  },
  value: 5,
};

const { name, languages } = deepObject.state.information;
const { value } = deepObject;

const extracted = { name, languages, value };

console.log(extracted);
/*
{
    name: 'coldair',
    languages: [ 'korean', 'english', 'chinese' ],
    value: 5
}
  */
  • 살작 지저분하지만, 가능한 방법.
const deepObject = {
  state: {
    information: {
      name: 'coldair',
      languages: ['korean', 'english', 'chinese'],
    },
  },
  value: 5,
};

const {
  state: {
    information: { name, languages },
  },
  value,
} = deepObject;

const extracted = { name, languages, value };

console.log(extracted);
/*
{
  name: 'coldair',
  languages: [ 'korean', 'english', 'chinese' ],
  value: 5
}
*/

배열 비구조화 할당

  • 변수(상수) 이름은 마음대로 이용가능. (순서가 보장)
  • 배열 순서 대로 대응하게 됨.
  • 대응 되어지지 않은 요소는 버려짐.
// 배열 선언
const arr = [1, 2, 3];
// 배열 비구조화 할당
let [x, y] = arr;
console.log(x); // 1
console.log(y); // 2
console.log(typeof x); // number
console.log(typeof y); // number
  • 기본값 설정 가능.
const array = [1];
const [one, two = 2] = array;

console.log(one); // 1
console.log(two); // 2
  • 응용하면 변수의 값을 서로 바꿀 수 있음.
// 비구조화 할당을 이용하지 않고 a와 b의 값을 바꾼다면,
let a = 5, b = 10;
let c; // 비구조화 할당을 이용하지 않으면 임시변수가 필연적임.
c = a;
a = b;
b = c;
console.log(a); // 10
console.log(b); // 5

// 비구조화 할당을 이용하여 a와 b의 값을 바꾸면 보기만해도 간결.
let a = 5, b = 10;
[a, b] = [b, a];
console.log(a); // 10
console.log(b); // 5

확산 연산자(spread operator)를 이용하면

  • 배열 비구조화 할당 시 대응 되지 않은 요소를 한번에 묶어 새로운 배열에 할당.
  • ... 키워드 이용.
const arr = [1, 2, 3, 4, 5];
let [x, y, ...rest] = arr;
console.log(x); // 1
console.log(y); // 2
console.log(rest); // [ 3, 4, 5]
console.log(typeof x); // number
console.log(typeof rest); // object

접근자(accessor) 프로퍼티

  • 객체 프로퍼티에는 데이터 프로퍼티와 접근자(accessor) 프로퍼티 두 가지 존재.
  • 접근자 프로퍼티는 settergetter 두가지 함수로 구성.
  • 접근자 프로퍼티는 함수 프로퍼티(매서드)와 유사하지만, 접근했을 때는 데이터 프로퍼티와 비슷하게 동작.
  • 때문에 동적 프로퍼티라고 부르기도 함.
  • 접근자 프로퍼티는 특정 값을 바꾸거나 조회할 때, 원하는 코드를 함께 실행.
  • 일반 함수와 다르게 조회만 해도 호출. (() 괄호가 쓰이지 않음; 일반 키 값처럼 사용)

getter함수

  • getter 함수에서는 반환 값(리턴값)이 필요.
const numbers = {
  a: 1,
  b: 2,
  get sum() {
    console.log('sum함수가 실행됩니다.!');
    return this.a + this.b;
  },
};

console.log(numbers.sum); // sum함수가 실행됩니다.! 3 출력
numbers.b = 5;
console.log(numbers.sum); // sum함수가 실행됩니다.! 6 출력

setter함수

  • setter 함수는 파라미터 값이 필요.
const dog = {
  _name: '멍멍이',
  set name(value) {
    console.log(`이름이 ${value}로 바뀝니다.`);
    this._name = value;
  },
};

console.log(dog._name); // 멍멍이
dog.name = '뭉뭉이'; // 이름이 뭉뭉이로 바뀝니다.
console.log(dog._name); // 뭉뭉이

getter와 setter 함수를 이용한다면

  • 직관적인 표시 가능.
const dog = {
  _name: '멍멍이',
  get name() {
    console.log(`_name을 조회합니다.`);
    return this._name;
  },
  set name(value) {
    console.log(`이름이 ${value}로 바뀝니다.`);
    this._name = value;
  },
};

console.log(dog.name); // _name을 조회합니다. 멍멍이
dog.name = '뭉뭉이'; // 이름이 뭉뭉이로 바뀝니다.
console.log(dog.name); // _name을 조회합니다. 뭉뭉이
  • 효율적인 객체를 만들 수 있음.
const numbers = {
  _a: 1,
  _b: 2,
  sum: 3,
  calculate() {
    console.log(`calculate`);
    this.sum = this._a + this._b;
  },
  get a() {
    return this._a;
  },
  get b() {
    return this._b;
  },
  set a(value) {
    this._a = value;
    this.calculate(); // calculate를 호출하여, sum값을 업데이트.
  },
  set b(value) {
    this._b = value;
    this.calculate(); // calculate를 호출하여, sum값을 업데이트.
  },
};

console.log(numbers.sum);
numbers.a = 5;
numbers.b = 7;
numbers.a = 9; // 값이 바뀔 때 마다 sum값이 계산되어 업데이트됨.
console.log(numbers.sum); // 따라서 조회할 때는 이미 계산된 값을 재사용.
// sum 값을 10번 호출 된다면, 매번 다시 계산 하여 보여주는 것 보다 위의 방법이 합리적.

프로퍼티 나열

for...in

  • 객체의 프로퍼티(key)를 하나씩 반환하는 루프.
  • 기억해야 할 것은 순서가 보장되지 않는 나열.(객체는 순서가 보장되지 않음)
  • 순서는 브라우저나 노드 등의 프로그램에서 속도나 효율 목적으로 언제든 바뀔 수 있음.
  • for...in 루프에는 키가 심볼 프로퍼티는 포함하지 않음.
const SYM = Symbol();
const o = { a: 1, b: 2, c: 3, [SYM]: 4 };

for (let prop in o) {
  console.log(prop); // a b c
}
  • 클래스의 설계 의도대로 사용하기 위해 hasOwnProperty 사용 권장.
const SYM = Symbol();
const o = { a: 1, b: 2, c: 3, [SYM]: 4 };

for (let prop in o) {
  if (!o.hasOwnProperty(prop)) continue;
  console.log(`${prop}: ${o[prop]}`); // a: 1 b: 2 c: 3
}
  • 배열에서 이용 가능하지만 권장 하지 않으며, 배열의 값(value)을 하나씩 반환하는 루프인 for...of루프를 지향.

hasOwnProperty를 사용하는 이유

  • 객체 네이티브 메서드.
  • 특정 프로퍼티의 소유 여부를 반환.
  • 소유하지 않았거나 프로토타입 체인에 정의 되었다면 false 반환.
  • 객체의 직접적인 속성인지 확인.
  • 즉, 상속한 속성을 무시하고, 객체 자체에 정의된 속성만을 확인할 수 있게 해줌.
  • 예기치 않은 에러를 방지하고 안전한 코드를 작성 가능하게 함.
const obj = {
  a: {},
};
console.log(obj.hasOwnProperty('a')); // true
console.log(obj.hasOwnProperty('b')); // false

Object.keys

  • 객체의 프로퍼티(key)를 배열로 반환.
const SYM = Symbol();
const o = { a: 1, b: 2, c: 3, [SYM]: 4 };

console.log(Object.keys(o)); // [ 'a', 'b', 'c' ]

래퍼 객체(Wrapper Objects)

배열(array)

함수(function)


0개의 댓글