자바스크립트 객체 기본

jkpapa·2023년 3월 27일
0

JavaScript

목록 보기
2/10

객체

  • 객체란 자바스크립트에서 원시 타입(숫자, 문자열, boolean, null, undifined, symbol)이 아닌 모든 데이터를 의미한다.
  • 복잡한 정보를 프로퍼티-(key와 value의 조합)으로 저장하는 자료형이다.
const person1 = {
  // 프로퍼티 key: value
  name: 'Tom', 
  age: 25,
  married: false
}; 
// 이름은 Tom, 나이는 25, 미혼인 정보를 담고 있는 객체
// 프로퍼티의 value부분에는 문자열, 숫자, boolean 등 다양한 자료형을 담을 수 있다.
// 기본적으로는 key를 따옴표("")로 감싸야 하지만 자바스크립트 코드에서는 생략이 가능하다.

console.log(typeof porson1); // Object
console.log(porson1); // {name: 'Tom', age: 25, married: false}

객체의 기본 사용법

1. 객체 생성과 프로퍼티 접근

  • 객체를 생성해보고 해당 객체의 프로퍼티에 접근해보자.

  • 객체의 프로퍼티에 접근하는 방법은 두 가지가 있다.

    • 마침표 프로퍼티 접근 연산자 object.key
    • 대괄호 프로퍼티 접근 연산자 object['key']
    • 객체 프로퍼티에 접근 한 경우 접근한 프로퍼티 key와 대응되어 있는 value값을 얻을 수 있다.
const food = {
  name: '치킨',
  price: '20000',
};

console.log(food); // {name: '치킨', price: '20000'}

console.log(food.name) // 치킨
console.log(food['price']) // 20000

// 객체의 프로퍼티를 수정 할 수도 있다.
food.name = '햄버거';
food.price = '5000';

표현식으로 키값 정의하기

  • 만약 key값을 설정할 때 객체 안에서 값을 변경하면서 설정하고 싶을 경우 대괄호[]안에 표현식을 작성하여 설정 할 수 있다.
  • 객체의 데이터를 동적으로 설정 할 수 있다.
  • 반복문으로 크기가 큰 객체를 생성해야 할 경우 유용 할 수 있겠다.
let idx = 0;
const  obj = {
  ['key-' + ++idx]: `value-${idx}`,
  ['key-' + ++idx]: `value-${idx}`,
  ['key-' + ++idx]: `value-${idx}`
}

console.log(obj); // {key-1: 'value-1', key-2: 'value-2', key-3: 'value-3'}

// 반복문으로 객체 만들기
const obj2 = {};
for (let index = 1; index < 4; index++) {
    obj2['key-' + index] = `value-${index}`;
}

console.log(obj); // {key-1: 'value-1', key-2: 'value-2', key-3: 'value-3'}

⚠️ 객체의 key값으로 배열이나 다른 객체를 사용시

  • 만약 객체 프로퍼티의 key값을 배열이나 객체와 같은 참조형을 사용하게 된다면 해당 객체에 담긴 데이터가 key값이 되지 않는다. 예를 들어 아래 코드를 보자
const objKey = { x: 1, y: 2 };
const arrKey = [1, 2, 3];

const obj = {
  [objKey]: '객체를 키값으로',
  [arrKey]: '배열을 키값으로'
}

console.log(
  obj[{ a: 1, b: 2, c: 3 }],
  obj['1,2,3']
);

// 출력결과
// 객체를 키값으로 배열을 키값으로
  • 객체에서 프로퍼티 key값으로 설정한 객체{ x: 1, y: 2 }와 배열 [1,2,3]과 다른 내용의 객체{ a: 1, b: 2, c: 3 }, 문자열 '1,2,3으로 접근해도 객체의 저장된 데이터가 정상적으로 출력된다. 왜 그럴까?

객체 obj를 출력해보자

console.log(obj);

/* 출력 결과
{ 1,2,3: "배열을 키값으로", [object Object]: "객체를 키값으로" }
*/
  • 객체의 key값이 우리가 설정한 값{ x: 1, y: 2 }, [1,2,3]과 완전히 달라진 것을 확인 할 수 있다. 그 이유는 무엇일까?

💡 객체 key값으로 배열을 넣을 경우 배열에 담긴 데이터로 만든 문자열을 key값으로 사용한다.

  • 위 예제에서는 key 배열[1,2,3]이 문자열 1,2,3으로 변환되었기 때문에 obj['1,2,3']으로도 접근이 가능했던 것이다.

💡 객체 key값으로 객체를 넣을 경우 [object Object]라는 문자열을 key값으로 사용한다. 어떤 객체를 넣어도 마찬가지로 동일한 [object Object]문자열을 뱉는다.

  • 위 예제에서는 key 객체와 접근 객체가 [object Object]문자열로 변환되어서 객체에 저장된 데이터가 다름에도 접근이 가능했던 것이다.

  • 즉, 실제로 해당 객체나 배열의 데이터의 값이 키 값이 되는 것이 아니다.

  • 비슷한 데이터 구조를 갖는 Map과 다른점이라고 할 수 있다.

객체를 사용할 때 객체나 배열을 key로는 쓰지 말자

프로퍼티 삭제 - delete

  • 삭제하고자 하는 프로퍼티 앞에 delete연산자를 붙여주면 프로퍼티가 삭제된다.
const person1 ={
  name: 'Tom'
  age: 25
}

delete person1.age;
// 만약 정의되지 않은 프로퍼티를 삭제하려고 하면 오류가 발생하지는 않지만 아무일도 일어나지 않는다.

2. Key의 동적 사용

  • 객체를 사용할 때 항상 같은 데이터만 가지고 있는 객체를 사용하는 경우는 많이 없을 것이다.
  • 애플리케이션 내에서 객체의 데이터는 동적으로 변환되어야 하는 경우가 많은데 이때 주의 할 점들을 알아보자.
const obj1 = {
  key1: 'value1',  
  key2: 'value2',
  key3: 'value3'
}
  • 아래 함수는 객체에 프로퍼티를 추가하는 함수와 삭제하는 함수이다. 하지만 해당함수를 호출했을 때 의도와는 다른 작업을 수행하게 된다. 왜 그럴까?
function addProperty(obj, key, value){
	obj.key = value;
}

function addProperty(obj, key, value){
	delete obj.key;
}
  • 개발자의 의도는 매개변수를 이용해서 객체에 프로퍼티를 추가하거나 삭제하는 것이다.
  • 하지만 obj.key라는 코드는 obj['key']와 같은 의미이다. 즉, obj내에 존재하는 key라는 키에 접근을 시도하는 것이지 매개변수로 전달된 key를 추가하는 것이 아니게된다.
  • 따라서 올바르게 프로퍼티를 추가하거나 삭제 할 수 있는 코드는 obj[key] = value;가 되겠다.

3. ES6 추가 문법

객체 선언 시 프로퍼티 키와 대입할 상수/변수 이름이 동일할 경우 단축표현이 가능하다.

  • 아래와 같은 경우이다. 객체가 가지고 있는 프로퍼티와 프로퍼티에 대입 할 식별자들과 같은 이름을 사용하고 있다.
const x = 1, y = 2; // 객체에 대입할 상수

const obj1 = { 
  x: x,
  y: y
}

console.log(obj1); // { x: 1, y: 2 }
  • 아래 코드는 같은 결과를 출력한다.
const x = 1, y = 2;
const obj = { x, y };

console.log(obj); // { x: 1, y: 2 }
  • 해당 문법을 사용하면 생성자 함수를 보다 간편하게 구현 할 수 있다.
function createObj(key1, key2, key3){
  // 매개변수의 이름이 key
  // 매개변수에 담긴 데이터가 value
  return { key1, key2, key3 }
}

createObj(1, 2, 3) 
// {key1: 1, key2: 2, key3: 3}

메서드 - 객체에 정의된 함수 프로퍼티

  • 함수도 key를 대응시켜서 객체의 프로퍼티로 들어갈 수 있었다.
  • 신규문법 메서드에서는 객체에 정의 할 함수에는 key값 할당을 생략 할 수 있다.
  • class와 비슷한 모습
// 이전 문법
const obj1 = {
  func: function(){
    console.log('Hello world');
  }
}

obj1.func(); // Hello world

// 메서드 정의
const obj2 = {
  func(){
    console.log('Hello world');
  }
}

obj2.func(); // Hello world
  • ES6부터는 위의 생략된 표현으로 정의된 함수만 메서드라고 부른다.

생성자 함수

  • 같은 형식을 가진 여러개의 객체가 필요하다면 어떻게 해야 할까?
    • 객체가 필요 할 때마다 객체 리터럴로 만들면 되지!
const person1 = {
  name: 'Tom',
  age:25
}

const person2 = {
  name: 'Mike',
  age:22
}

// ........
  • 만약 필요한 객체가 수 천, 수 만개라면? 🙄...

1. 생성자 함수로 객체 만들기

// 생성자 함수, 대문자로 시작해야한다.
function Person(name, age) {
  this.name = name;
  this.age = age;
  
  this.hello = function () {
    return `hello my name is ${this.name}, ${this.age} years old`
  }
  // return this
}
 
 // 인스턴스 생성

const person1 = new Person('Tom', 24);
const person2 = new Person('Bruno', 17);
const person3 = new Person('Jym', 26);
  
console.log(person1);
console.log(person2);
console.log(person3);
  • 생성자 함수에서의 this는 생성자 함수로 만들어질 인스턴스를 가리킨다.
  • 생성자 함수에서 반환값을 명시하지 않았다. 암묵적으로 this, 즉 현재 생성한 인스턴스를 반환한다.
    • 단, 생성자 함수 앞에 new를 붙이지 않으면 undefined가 된다.
    • 이 말은 fucntion으로 선언된 함수는 기본적으로 생성자 함수의 기능을 갖는다는 말이다.
    • 함수 호출시 new를 붙이는가 여부에 따라 호출 원리가 다르다.
function hello(){
    console.log('Hello world');
}

const f = new hello(); 
// Hello world
// f에는 hello {} 객체가 할당된다. 함수가 생성자 함수로 호출 된 것!!

hello();
// Hello world

const f2 = hello(); // 반환값이 없기 때문에 f2는 undifined
  • 생성자 함수는 객체가 아니다. 따라서 메서드를 정의 할 수 없다.

❓그런데 의문점이 든다.
객체를 생성하는 것이라면 꼭 생성자 함수를 만들어야 할까?
객체를 반환하는 함수를 사용하면 안될까?
두 방식의 차이가 뭘까?

2. 생성자 함수로 만들어진 객체

  • 프로토타입 - class가 나오기 이전 자바스크립트의 객체지향 프로그래밍 중 자바스크립트 객체지향의 중심

  • 다음 코드를 보자

function Person(name, age) {
  this.name = name;
  this.age = age;
  
  this.hello = function () {
    return `hello my name is ${this.name}, ${this.age} years old`
  }
}

const person1 = new Person('Tom', 25); // 생성
  • 생성자 함수를 사용해 인스턴스를 만드는 간단한 코드이다.
  • 여기서 인스턴스가 생성된 후 prototype으로 Person에 기능을 추가해보자. (사람은 인사만 할 수 있는게 아니니까)
Person.prototype.walk = function() {
  return `walking ${this.name}`
}
  • 생성자함수.prototype.property = 의 형태로 함수나 값을 추가 할 수 있는데, 이런 방식으로 추가한 프로퍼티는 이미 만들어진 인스턴스, 그리고 앞으로 만들어질 인스턴스들도 모두 포함하게 된다.
  • 즉, Person프로토타입 프로퍼티는 같은 생성자로 만들어진 인스턴스들이 모두 갖게 된다.
age: 24
hello: ƒ ()
name: "Tom"
[[Prototype]]: Object // 크롬 콘솔에 출력하면 Object라는 이름으로 나오지만 Person이라고 생각하면 된다.
walk: ƒ () //추가된 프로토타입
constructor: ƒ Person(name, age) // 프로토타입에 생성자 명시
[[Prototype]]: Object

⚠️ hellowalk는 종류가 다르다.

객체를 직접 반환하는 함수

  • 이제부터는 생성자 함수와 객체를 직접 반환하는 함수와의 차이점을 알아보자
function Person(name, age){
  return { name, age, 
          hello() { `hello my name is ${this.name}, ${this.age} years old`}
         }
}

const person1 = Person('Jym', 17);
// new Person();으로 진행해도 인스턴스로 생성되지 않는다.
console.log(person1);
/*
age: 17
hello: ƒ hello()
name: "Jym"
[[Prototype]]: Object
*/
  • 해당 코드에서 만들어진 객체는 생성자 함수로 만든 객체와 가지고 있는 데이터는 같지만, 반환값으로 만들어진 객체는 오리지날 순수 그냥 객체이다.

    어딘가에서 태어난 것이아니라 태초부터 존재한 유일무이한 존재

  • 따라서 프로토타입이 최상위 객체인 Object인 것을 확인 할 수 있다.

  • 앞서 생성자로 만들어진 것들은 어떤 하나의 객체(Person)에서 파생되어 생성된 인스턴스라는 점에서 차이가 있다.

생성자 함수 자체의 프로퍼티와 함수

  • 만들어지는 인스턴스가 아니라 생성자 함수 그 자체에 어떤 기능을 추가하고 싶을 때에는 어떻게 해야할까?

  • 자바스크립트에서는 함수도 객체라고 했다. 그렇다면 프로퍼티 접근자로 기능을 추가할 수 있지 않을까?

function Person(name, age) {
  this.name = name;
  this.age = age;
  
  this.hello = function () {
    return `hello my name is ${this.name}, ${this.age} years old`
  }
}

Person.heart = '두근두근';

const person1 = new ('Tom', 25);

console.log(person1.heart) // 에러 발생, heart 프로퍼티 없음
  • 객체 지향에 익숙하다면 static이라는 키워드를 떠올렸을 것이다.
  • 인스턴스를 생성하지 않고도 실행할 수 있는 기능을 정의 할 수 있다.
  • 인스턴스는 접근 할 수 없는 창조주의 영역이다.

0개의 댓글