객체와 프로퍼티

hanyoko·2023년 6월 19일
0

JAVASCRIPT

목록 보기
19/32
post-thumbnail

객체 (Object)

  • 우리 주변의 사물(Object)상태(State)행위(Behavior)로 나타내어 추상화하는 개념이다.
  • 상태는 어떠한 사물의 특성을 나타내고, 행위는 그 사물이 가지는 행동을 나타낸다.
  • 즉, 사물에 대한 모델링을 위해 사물을 특정짓는 변수와 함수를 그룹핑 한 것이라고 할 수 있다.

    고양이를 이용해 예를 들어보면, 고양이의 이름, 나이, 품종 등으로 고양이의 상태를 나타낼 수 있다. 고양이의 행동으로는, 음식을 먹는 행동, 달리기, 점프하기, 구르기, 쥐잡기 등이 있을 것 같다.

자바스크립트에서는 이러한 것들을 통해서 고양이라는 객체를 표현할 수 있다. 방금 예를 들었던 고양이를 객체 리터럴을 이용해 표현하면 이렇게 할 수 있다.

let kitty = {
  name: "Dinah",
  age: 3,
  species: "Short hair",
  health: 5,

  eat: function() {
    this.health = this.health + 1;
  },
  run: function() {
    console.log(this.name + " Running!!");
  }
}

console.log(kitty.name);  //-> Dinah
console.log(kitty.health);  //-> 5

kitty.eat();
console.log(kitty.health);  //-> 6

kitty.run();  //-> Dinah Running!!

객체의 프로퍼티와 메소드

프로퍼티 (Property)

자바스크립트의 객체에서 프로퍼티이름이 붙어있는 값을 나타낸다..
객체의 프로퍼티는 객체명 다음에 점과 프로퍼티명을 붙이거나, 객체명 다음에 대괄호를 둘러싼 프로퍼티명의 문자열을 써서 접근할 수 있다는 것. 이 프로퍼티가 객체의 상태를 나타내는 데 쓰인다. 프로퍼티는 멤버 변수 (Member variables) 라고도 불려요.

메소드 (Method)

함수가 프로퍼티에 저장될 경우 이러한 함수를 메소드라고 불린다. 즉 메소드는 객체 내에 정의된 함수로, 객체의 프로퍼티 명이 함수의 이름으로 쓰인다. 객체가 소유한 메소드를 호출하기 위해서는 함수 호출과 비슷하게 객체명 다음에 점을 붙이고 메소드명() 을 사용한다. 메소드는 멤버 함수 (Member function) 라고도 불린다.


this

this는 함수가 실행될때 그 함수를 호출한 객체를 가리키는 키워드.
다시 말해, 함수에서 참조하는 메모리 영역을 나타낸다. 따라서 객체 메소드 내부의 this는 객체를 가리켜요. 위 코드에서는 kitty라는 객체를 가리키게 되죠.

자바스크립트에서 this는 함수가 호출되어 실행되는 시점에 결정된다. 즉, this의 값은 함수가 호출되었을 때 그 함수가 속해있던 객체를 가리키게 되며, 호출될 때 this 가 가르키는 객체가 변경될 경우 this 의 값도 바뀌게 된다.

this 가 가리키는 대상은 상황에 따라 다음과 같다.

전역 : 전역에서 this의 값은 전역객체를 가리킨다. Node.js 의 경우 global 객체를, 웹브라우저의 경우 Window 객체를 가르킨다.

전역 함수 : 전역에서 함수를 호출하게 되면 함수 내부의 this는 전역 객체를 가르킨다. 이는 함수를 전역에서 선언하고 호출할 경우 함수는 전역객체 내부에 정의되기 때문이다.

객체 : 생성자로 생성한 객체 내부에서의 this는 생성한 객체를 가리킨다. 인스턴스의 메소드 내부에서도 역시 메소드를 호출한 객체를 가리키게 된다.

이벤트 핸들러 : 이벤트 핸들러 내부에서는 이벤트가 발생한 요소 객체를 가리킨다.


객체 생성자

객체 리터럴을 이용해 객체를 생성하는 방법외에도 생성자를 이용해 객체를 생성하는 방법도 있다.

function Kitty(name, age, species) {
  this.name = name;
  this.age = age;
  this.species = species;
  this.health = 5;
  
  this.eat = function() {
    this.health = this.health + 1;
  };
  
  this.run = function() {
    console.log(this.name + " Running!!");
  };
}

위와 같이 객체를 생성하기 위해 정의한 함수를 생성자 함수라고 부른다. 생성자 함수의 이름은 다른 함수와 구분하기 위해 관례적으로 첫글자가 대문자로 시작하는 PascalCase 표기법을 사용한다.

객체의 생성은 new 키워드를 이용해 생성할 수 있습니다. 이렇게 new 연산자를 통해 생성한 객체를 인스턴스라고도 부른다.

let dinah = new Kitty("Dinah", 5, "Short hair");
let cheshire = new Kitty("Cheshire", 10, "Scottish");

console.log(dinah.age);    // 5
console.log(dinah.run());  // Dinah Running!!
console.log(cheshire.name);  // Cheshire

위 예에서 new 키워드를 이용해 객체를 생성할 때 넘겨준 인수는 this 를 사용해 객체의 프로퍼티에 대입된다. 따라서 생성한 객체의 프로퍼티는 객체 생성 시 넘겨준 인수를 이용해 초기화된다.

생성자를 이용해 객체를 생성하면 동일한 타입의 객체를 여러개 생성 할 수 있으므로 효율적으로 객체를 관리할 수 있다.


Prototype

객체 내부에서 프로퍼티를 이용해 메소드를 정의하면 생성된 모든 인스턴스에 인스턴스 개수만큼의 메서드가 생성된다. 이럴 경우 인스턴스가 많아질 경우 동일한 메서드를 여러개 생성하게 되어 메모리 소비가 심해지게 되는데, 이는 프로토타입 객체에 메소드를 정의하는 방식으로 해결할 수 있다.

함수 객체에는 prototype 프로퍼티가 정의되어 있다. prototype 프로퍼티는 프로토타입 객체를 가리키고 있는데, 동일한 생성자로 생성한 모든 객체는 동일한 프로토타입 객체를 가리키게 된다.

따라서 객체 생성자의 프로토타입 객체에 메소드를 추가하면 생성되는 모든 인스턴스가 메소드를 공유하므로 메모리 낭비를 줄일 수 있다.

function Kitty(name, age, species) {
  this.name = name;
  this.age = age;
  this.species = species;
  this.health = 5;
}

Kitty.prototype.eat = function() {
  this.health = this.health + 1;
};

Kitty.prototype.run = function() {
  console.log(this.name + " Running!!");
};

let dinah = new Kitty("Dinah", 5, "Short hair");
let cheshire = new Kitty("Cheshire", 10, "Scottish");

console.log(dinah.age);    // 5
console.log(dinah.run());  // Dinah Running!!
console.log(cheshire.name);  // Cheshire

아래와 같이 prototype 메소드를 객체 리터럴을 대입하는 형태로 추가할 수도 있다. 다만 이렇게 사용할경우 메소드를 동적으로 추가할 수 없게되므로 주의가 필요하다.

Kitty.prototype = {
  eat: function() {},
  run: function() {}
}

프로퍼티 property

instanceof 연산자

instanceof 연산자는 객체의 인스턴스가 해당 객체 생성자의 객체인지 확인할 때 사용

function Kitty() {};
let dinah = new Kitty();

dinah instanceof Kitty;  // true
dinah instanceof Object;  // true
dinah instanceof Array;  // false

위와 같이 사용자가 생성한 객체는 객체 자신과 부모 객체인 Object 객체의 인스턴스라는 것을 확인할 수 있습니다.


hasOwnProperty

hasOwnProperty 메소드를 사용하면 객체에 인수로 지정한 프로퍼티가 존재하는지 확인할 수 있다.

dinah.hasOwnProperty("name"); // false
dinah 객체에는 name 프로퍼티가 존재하지 않는 것을 확인할 수 있다.


Object.getOwnPropertyDescriptor

Object.getOwnPropertyDescriptor 메소드를 사용하면 객체 프로퍼티의 프로퍼티 디스크립터를 반환한다.

메소드의 첫번째 인자로는 객체명, 두번째 인자로 화가인할 프로퍼티를 문자열로 지정한다.

const obj = { foo: "bar" };
Object.getOwnPropertyDescriptor(obj, 'foo');
//=>
{
  value: 'bar',
  writable: true,
  enumerable: true,
  configurable: true
}

객체 프로퍼티의 속성

Object.getOwnPropertyDescriptor 메소드를 사용하면 위와 같이 객체 프로퍼티의 속성을 확인할 수 있다.

객체 프로퍼티는 내부적으로 몇가지 속성을 가지고 있는데, 이러한 프로퍼티 속성은 쓰기 가능, 열거 가능, 재정의 가능 속성을 가지고 있다.

writable

프로퍼티에 쓰기 가능 여부를 정의한다.

만약 writable의 값이 false 라면 프로퍼티의 값을 수정할 수 없다.


enumerable

프로퍼티가 for…in 문 등의 반복문으로 찾을 수 있는 대상인지를 정의한다.

즉, 열거 가능한지 확인한다.


configurable

프로퍼티의 내부 속성을 수정할 수 있는지를 확인한다.

만약 false 라면 해당 프로퍼티의 내부 속성은 수정할 수 없다. 그리고 configurable 속성은 한번 false 로 지정하고 나면 true로 되돌릴 수 없다.


Object.defineProperty

Object.defineProperty 메소드를 사용하면 객체 프로퍼티의 내부 속성값을 변경할 수 있다.

만약 obj 객체의 foo 프로퍼티의 읽기가능 속성을 false로 변경하려면 아래와 같이 사용한다.

Object.defineProperty(obj, 'foo', {writable: false});

이제 아래와 같이 foo 프로퍼티의 값을 변경하려고 하더라도 프로퍼티의 읽기속성이 잠겨있으므로 수정할 수 없게 된다.

obj.foo = "baz";

console.log(obj);  // { foo: 'bar' }

Object.keys

Object.keys 메소드는 객체 프로퍼티 중 열거 가능한 프로퍼티의 목록을 배열로 만들어서 반환한다.

Object.keys(obj);  // [ 'foo' ]

객체 보호

객체에는 객체의 보호를 위해 객체를 잠그는 속성들을 가지고 있다.

Object.freeze

freeze 메소드는 객체를 불변 객체로 만든다.

불변 객체 : 객체 새로운 프로퍼티를 추가하거나 변경하는 것이 불가능한 객체를 의미한다.
이렇게 객체를 불변 객체로 만들게 되면 해당 객체는 오직 읽기만 가능한 객체가 된다.

let person = { name: "Yohan", age: 10 };

Object.freeze(person);

Object.isFrozen(person); // true

Object.isFrozen 메소드를 사용하면 객체의 freeze 여부를 확인할 수 있다.

아래와 같이 객체 프로퍼티의 값을 변경하거나 새로운 프로퍼티를 추가하는 것이 불가능해진다.

person.name = "John";
person.gender = "Female";
delete person.age;

console.log(person);  // { name: 'Yohan', age: 10 }

Object.seal

seal 메소드는 객체에 새로운 프로퍼티를 추가하거나 삭제, 수정할 수 없게 된다. 오직 읽기와 쓰기만 가능한 객체가 만들어진다.

let person = { name: "Yohan", age: 10 };

Object.seal(person);
Object.isSealed(person);  // true

Object.isSealed 메소드를 사용해 객체의 seal 여부를 확인할 수 있다.

아래와 같이 seal을 사용하면 객체 프로퍼티의 값을 변경하는 것은 가능하지만 새로운 프로퍼티를 추가하는 것이 불가능해진다.

person.name = "John";
person.gender = "Female";
delete person.age;

console.log(person);  // { name: 'John', age: 10 }

Object.preventExtensions

preventExtensions 메소드는 객체에 새로운 프로퍼티를 추가할 수 없게 만든다.

let person = { name: "Yohan", age: 10 };

Object.preventExtensions(person);
Object.isExtensible(person);  // false

Object.isExtensible 메소드를 사용해 객체의 확장가능 여부를 확인할 수 있다.

0개의 댓글