Object prototypes

김동현·2023년 3월 2일
0

자바스크립트

목록 보기
9/22

The prototype chain

브라우저 콘솔에서 다음과 같이 객체 리터럴을 만들어보자.

const myObj = {
	city: "Madrid",
  	greet(){
    	console.log(`Greetings from ${this.city}`);
    },
};

위의 객체는 city 속성과 greet() 메소드를 가지고 있다.

콘솔차에서 myObj 뒤에 . 를 입력하면 모든 멤버들의 이름이 리스트로 팝업될 것이다.

myObj의 멤버들이 리스트로 보여짐

보면 알겠지만, citygreet 말고도 많은 멤버들이 있다는 것을 확인할 수 있다.

이 멤버들은 무엇이며, 어디에서 왔을까?

자바스크립트의 모든 객체는 프로토타입이라 불리는 내장된 속성을 가지고 있다.

프로토타입은 그 자체로 하나의 객체이기 때문에, 프로토타입은 또 자신만의 프로토타입을 가질 것이고, 이렇게 쭉쭉 이어지는 것을 프로토타입 체인이라 불린다.

프로토타입 체인은 프로토타입이 null인 객체에 도달하면 끝난다.

프로토타입을 가리키는 객체의 속성을 참조할때는 .prototype 가 아니라 .__proto__ 로 한다.
물론 이는 표준이 아니지만 실제로 모든 브라우저에서 사용할 수 있다.
객체의 프로토타입에 접근하는 표준 방법은 Object.getPrototypeOf() 메서드를 이용하는 것이다.

어떠한 객체의 속성에 접근한다고 가정해보자.

찾고자 하는 속성이 객체 자체에서 찾을 수 없을 경우 그 객체의 프로토타입에서 속성을 검색한다.

여전히 속성을 찾을 수 없을 경우 프로토타입의 프로토타입에서 검색하고, 속성을 찾았거나 체인의 끝에 도달하면 검색이 멈춘다.

속성을 못찾은채로 끝나면 undefined 가 반환된다.

예를 들어 myObj.toString() 을 호출했다 가정해보자.

  • toStringmyObj 에 있는지 찾는다.

  • 찾을수 없으므로 myObj 의 프로토타입에서 toString 을 찾는다.

  • toString 을 찾았고 호출한다.

myObj 의 프로토타입이 뭘까?

Object.getPrototypeOf(myObj);

이것은 Object.prototype 이라는 객체이며, 모든 객체가 기본적으로 가지고 있는 가장 최상위 프로토타입이다.

그림으로 관계 표시

Object.prototype 의 프로토타입이 null 이므로 프로토타입 체인의 끝이다.

객체의 프로토타입이 항상 Object.prototype 인 것은 아니다.

const myDate = new Date();
let object = myDate;

do {
  object = Object.getPrototypeOf(object);
  console.log(object);
} while (object);

// Date.prototype
// Object { }
// null

브라우저의 콘솔창 캡쳐

myDate 의 프로토타입이 Date.prototype 객체이고, 그것의 프로토타입이 Object.prototype 이라는 것을 보여준다.

그림으로 관계 표시

실제로, myDate.getMonth() 와 같은 메서드를 호출할 때는 Date.prototype 에 정의된 메서드를 호출하는 것이다.

Shadowing properties

객체의 프로토타입에 정의된 멤버이름과 같은 이름으로 객체에 정의하면 어떻게 될까.

const myDate = new Date(1995, 11, 17);

console.log(myDate.getYear()); // 95

myDate.getYear = function () {
  console.log("something else!");
};

myDate.getYear(); // 'something else!'

getYear() 을 호출하면 브라우저는 먼저 myDate에서 해당 이름을 가진 멤버를 찾고 찾지 못할 경우에만 프로토타입을 확인한다.

따라서 myDate에 정의된 버전의 getYear() 가 호출된다.

이를 shadowing the property 라고 부른다.

Setting a prototype

자바스크립트에는 객체의 프로토타입을 설정하는 다양한 방법이 있다.

그중 Object.create() 와 constructor를 이용하는 두 가지 방법을 알아보자.

Using Object.create

Object.create() 메서드는 새 객체를 만들고 새 객체의 프로토타입으로 사용할 객체를 지정할 수 있다.

const personPrototype = {
  greet() {
    console.log("hello!");
  },
};

const carl = Object.create(personPrototype);
carl.greet(); // hello!

위의 코드는 personPrototype 객체를 프로토타입으로 하는 새 객체를 생성해서 carl 변수에 할당하는걸 보여준다.

Using a constructor

자바스크립트에서의 모든 함수는 prototype 이라는 속성을 가지고 있따.

constructor로써 함수를 호출하게 되면 이 속성은 새로 생성된 객체의 프로토타입으로 설정된다.

따라서 constructor의 prototype 을 설정하면 constructor로 생성된 모든 객체에 대해 prototype 이 프로토타입으로 적용된다.

const personPrototype = {
  greet() {
    console.log(`hello, my name is ${this.name}!`);
  },
};

function Person(name) {
  this.name = name;
}

Object.assign(Person.prototype, personPrototype);
// or
// Person.prototype.greet = personPrototype.greet;

위의 코드에서 Person 함수의 prototype 속성을 personPrototype 객체로 정의하기 위해 Object.assign 을 이용했다.

Object.assign() 메서드는 두 번째 매개변수부터 마지막 매개변수까지의 값들을 첫 번째 매개변수에 덮어씌우는 역할을 한다.
첫 번째 매개변수 자체를 반환한다.

const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };
const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

아래와 같이 단순히 객체를 대입하면 되지않나라는 의문점이 들 수 있다.
Person.prototype = personPrototype

함수의 prototype 속성에는 해당 함수를 참조하는 constructor 속성이 들어있다.
브라우저 콘솔창에서의 결과화면

만약 단순히 객체를 대입해서 덮어쓰기 해버리면 constructor 속성이 지워지기때문에 따로 추가해줘야 한다.

아니면 객체를 대입하는 것이 아닌, 객체의 멤버를 추가해주면 된다.
Person.prototype.greet = personPrototype.greet

Object.assign() 메서드를 이용하면 일일이 멤버를 추가하지 않고 한큐에 가능하다.

새로 생성된 객체는 personPrototype 객체가 가지고 있는 greet() 메서드를 사용할 수 있다.

const reuben = new Person("Reuben");
reuben.greet(); // hello, my name is Reuben!

Own properties

Person constructor로 만들어진 객체는 두 가지 멤버를 가진다.

  • name 속성 : constructor에서 설정되므로 Person 객체에 직접 표시된다.

  • greet() 메서드 : prototype 에서 설정된다.

일반적으로 데이터 속성은 constructor(생성자)에 정의되어 있고 메서드는 프로토타입에 정의되어 있다.

메서드는 생성자로 생성된 모든 객체에 대해 동일한 반면, 데이터 속성은 각 객체에 고유한 값을 가지도록 해야하기 때문이다.

여기서 name 과 같은 객체에 직접 정의된 속성을 Own properties라고 하며, 정적 메서드인 Object.hasOwn() 를 사용하여 확인할 수 있다.

const irma = new Person("Irma");

console.log(Object.hasOwn(irma, "name")); // true
console.log(Object.hasOwn(irma, "greet")); // false

Object.hasOwnProperty() 메서드를 이용해도 되지만, 가능한 경우 Object.hasOwn() 을 사용하는 것이 좋다.
이유는 모르지만 MDN에서 그렇게 권장한다.


Prototypes and inheritance

프로토타입은 자바스크립트의 강력하고 매우 유연한 기능으로 코드를 재사용하고 객체를 결합할 수 있다.

특히 프로토타입으로 상속을 구현할 수 있다.

상속은 프로그래머로 하여금 시스템의 일부 객체가 다른 객체의 더 전문화된 버전이라는 생각을 표현할 수 있게 해주는 객체 지향 프로그래밍 언어의 기능이다.

[참고] : MDN

profile
프론트에_가까운_풀스택_개발자

0개의 댓글