옹재
소소한 개발 블로그
옹재
전체 방문자
오늘
어제
  • 분류 전체보기 (66)
    • 개발 관련 서적 스터디 (6)
    • 프론트엔드 스터디 (36)
      • Javascript (16)
      • Typescript (13)
    • Vue 스터디 (22)
    • 개발 관련 스터디 (2)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 프론트엔드 스터디
  • CSS
  • scss
  • 프레임워크 없는 프론트엔드 개발
  • 개발서적 스터디

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
옹재
프론트엔드 스터디/Javascript

Prototype이란?

Prototype이란?
프론트엔드 스터디/Javascript

Prototype이란?

2021. 7. 12. 11:46
728x90
반응형

프로토타입 객체

Java, C++과 같은 클래스 기반 객체지향 프로그래밍 언어와 달리 자바스크립트는 프로토타입 기반 객체지향 프로그래밍 언어입니다.

클래스 기반 객체지향 프로그래밍 언어는 객체 생성 이전에 클래스를 정의하고 이를 통해 객체를 생성합니다. 하지만 프로토타입 기반 객체지향 프로그래밍 언어는 클래스 없이도 객체를 생성할 수 있습니다.

자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어 있습니다. 이것은 마치 상속 개념과 같이 부모 객체의 프로퍼티 또는 메소드를 상속받아 사용할 수 있게 합니다. 이러한 부모 객체를 프로토타입 객체 또는 프로토타입이라고 합니다.

프로토타입 객체는 생성자 함수에 의해 생성된 각각의 객체에 공유 프로퍼티를 제공하기 위해 사용합니다.

function person() { }

console.dir(person)

prototype

ECMAScript spec에서는 자바스크립트의 모든 객체는 [[Prototype]]이라는 인터널 슬롯(internal slot)를 가진다. [[Prototype]]의 값은 null 또는 객체이며 상속을 구현하는데 사용된다. [[Prototype]] 객체의 데이터 프로퍼티는 get 액세스를 위해 상속되어 자식 객체의 프로퍼티처럼 사용할 수 있다. 하지만 set 액세스는 허용되지 않는다. 라고 되어있습니다.

[[Prototype]]의 값은 Prototype 객체이며 _ _ proto _ _ accessor property로 접근할 수 있습니다. _ _ proto _ _ 프로퍼티에 접근하면 내부적으로 Object.getPrototypeOf가 호출되어 프로토타입 객체를 반환합니다.

person 객체는 _ _ proto _ _ 프로퍼티로 자신의 부모 객체인 Function.prototype을 가리키고 있습니다.

[[Prototype]] vs prototype 프로퍼티

모든 객체는 자신의 프로토타입 객체를 가리키는 [[Prototype]] 인터넛 슬롯을 갖으며 상속을 위해 사용됩니다. 함수도 객체이므로 [[Prototype]] 인터널 슬롯을 갖는데 함수 객체는 일반 객체와 달리 prototype 프로퍼티도 소유합니다.

  • [[Prototype]]
    • 함수를 포함한 모든 객체가 가지고 있는 인터널 슬롯입니다.
    • 객체의 입장에서는 자신의 부모 역할을 하는 프로토타입 객체를 가리키며 함수 객체의 경우 Function.prototype를 가리킨다.
  • prototype 프로퍼티
    • 함수 객체만 가지고 있는 프로퍼티입니다.
    • 함수 객체가 생성자로 사용될 때 이 함수를 통해 생성될 객체의 부모 역할을 하는 객체를 가리킵니다.

prototype

constructor 프로퍼티

프로토타입 객체는 constructor 프로퍼티를 갖습니다. 이 constructor 프로퍼티는 객체의 입장에서 자신을 생성한 객체를 가리킵니다.

예를 들어 person() 생성자 함수에 의해 생성된 객체를 foo라고 한다면 이 foo 객체를 생성한 객체는 person() 생성자 함수입니다. 이 때 foo 객체 입장에서 자신을 생성한 객체는 person 생성자 함수이며, foo 객체의 프로토타입은 person.prototype입니다.

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

var foo = new Person('Lee');

// Person() 생성자 함수에 의해 생성된 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(Person.prototype.constructor === Person);

// foo 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(foo.constructor === Person);

// Person() 생성자 함수를 생성한 객체는 Function() 생성자 함수이다.
console.log(Person.constructor === Function);

Prototype chain

자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티 또는 메소드가 없다면 [[Prototype]]이 가리키는 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례대로 검색합니다. 이를 프로토타입 체인이라고 합니다.

var student = {
  name: 'Lee',
  score: 90
}

// Object.prototype.hasOwnProperty()
console.log(student.hasOwnProperty('name')); // true

student 객체는 hasOwnProperty() 메소드를 가지고 있지 않으므로 에러가 발생하여야 하나 정상적으로 출력되었습니다. 이는 student 객체의 [[Prototype]]이 가리키는 링크를 따라가서 student 객체의 부모 역할을 하는 프로토타입 객체의 메소드 hasOwnProperty를 호출했기 때문입니다.

객체 리터럴 방식으로 생성된 객체의 프로토타입 체인

객체 생성 방법은 3가지가 있습니다.

  • 객체 리터럴
  • 생성자 함수
  • Object() 생성자 함수

객체 리터럴 방식으로 생성된 객체는 결국 내장 함수인 Object() 생성자 함수로 객체를 생성하는 것을 단순화시킨 것입니다. 자바스크립트 엔진은 객체 리터럴로 객체를 생성하는 코드를 만나면 내부적으로 Object() 생성 함수를 사용하여 객체를 생성합니다.

Object() 생성자 함수는 물론 함수입니다. 따라서 함수 객체인 Object() 생성자 함수는 일반 객체와는 달리 prototype 프로퍼티가 있습니다.

  • prototype 프로퍼티는 함수 객체가 생성자로 사용될 때 이 함수를 통해 생성된 객체의 부모 역할으 하는 객체, 즉 프로토타입 객체를 가리킵니다.
  • [[Prototype]]은 객체의 입장에서 자신의 부모 역할을 하는 객체, 즉 프로토타입 객체를 가리킵니다.
var person = {
  name: 'Lee',
  gender: 'male',
  sayHello: function(){
    console.log('Hi! my name is ' + this.name);
  }
};

console.dir(person);

console.log(person.__proto__ === Object.prototype);   // ① true
console.log(Object.prototype.constructor === Object); // ② true
console.log(Object.__proto__ === Function.prototype); // ③ true
console.log(Function.prototype.__proto__ === Object.prototype); // ④ true

Object literal Prototype chaining

생성자 함수로 생성된 객체의 프로토타입 체인

함수선언식의 경우 자바스크립트 엔진이 내부적으로 기명 함수표현식으로 변환합니다.

var square = function square(number) {
  return number * number;
};

결국 함수선언식, 함수표현식 모두 함수 함수 리터럴 방식을 사용합니다. 함수 리터럴방식은 Function() 생성자 함수로 함수를 생성하는 것을 단순화 시킨 것입니다.

객체를 생성하는 방식은 아래 3가지 방식이 있습니다.

객체 생성 방식 엔진의 객체 생성 인스턴스의 prototype 객체
객체 리터럴 Object() 생성자 함수 Object.prototype
Object() 생성자 함수 Object() 생성자 함수 Object.prototype
생성자 함수 생성자 함수 생성자 함수 이름.prototype
function Person(name, gender) {
  this.name = name;
  this.gender = gender;
  this.sayHello = function(){
    console.log('Hi! my name is ' + this.name);
  };
}

var foo = new Person('Lee', 'male');

console.dir(Person);
console.dir(foo);

console.log(foo.__proto__ === Person.prototype);                // ① true
console.log(Person.prototype.__proto__ === Object.prototype);   // ② true
console.log(Person.prototype.constructor === Person);           // ③ true
console.log(Person.__proto__ === Function.prototype);           // ④ true
console.log(Function.prototype.__proto__ === Object.prototype); // ⑤ true

constructor function prototype chaining

foo 객체의 프로토타입 객체 Person.prototype 객체와 Person() 생성자 함수의 프로토타입 객체인 Function.prototype의 프로토타입 객체는 Object.prototype 객체입니다.

이는 객체 리터럴 방식이나 생성자 함수 방식이나 결국은 모든 객체의 부모 객체인 Object.prototype 객체에서 프로토타입 체인이 끝나기 때문입니다. 이때 Object.prototype 객체를 프로토타입 체인의 종점이라고 합니다.

프로토타입 객체의 확장

프로토타입 객체도 객체이므로 일반 객체와 같이 프로퍼티를 추가/삭제할 수 있습니다. 이렇게 추가/삭제된 프로퍼티는 즉시 프로토타입 객체이 반영됩니다.

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

var foo = new Person('Lee');

Person.prototype.sayHello = function(){
  console.log('Hi! my name is ' + this.name);
};

foo.sayHello();

extension of prototype

원시 타입의 확장

원시 타입을 제외한 모든 것은 객체입니다. 하지만 아래 예제를 보면 원시 타입인 문자열이 객체와 유사하게 동작합니다.

var str = 'test';
console.log(typeof str);                 // string
console.log(str.constructor === String); // true
console.dir(str);                        // test

var strObj = new String('test');
console.log(typeof strObj);                 // object
console.log(strObj.constructor === String); // true
console.dir(strObj);
// {0: "t", 1: "e", 2: "s", 3: "t", length: 4, __proto__: String, [[PrimitiveValue]]: "test" }

console.log(str.toUpperCase());    // TEST
console.log(strObj.toUpperCase()); // TEST

원시 타입 문자열과 String() 생성자 함수로 생성한 문자열 객체의 타입은 분명 다릅니다. 원시 타입은 객체가 아니므로 프로퍼티나 메소드를 가질 수 없으나 원시타입으로 프로퍼티나 메소드를 호출할 때 원시 타입과 연관된 객체로 일시적으로 변환되어 프로토타입 객체를 공유하게 됩니다.

var str = 'test';

String.prototype.myMethod = function() {
  return 'myMethod';
}

console.log(str.myMethod());
console.dir(String.prototype);

console.log(str.__proto__ === String.prototype);                 // ① true
console.log(String.prototype.__proto__  === Object.prototype);   // ② true
console.log(String.prototype.constructor === String);            // ③ true
console.log(String.__proto__ === Function.prototype);            // ④ true
console.log(Function.prototype.__proto__  === Object.prototype); // ⑤ true

String constructor function prototype chaining

프로토타입 객체의 변경

객체를 생성할 때 프로토타입은 결정됩니다. 하지만 결정된 프로토타입 객체는 임의의 객체로 변경할 수 있습니다. 이러한 특징으로 객체의 상속을 구현할 수 있습니다.

이 때 주의할 점은

  • 프로토타입 객체 변경 시점 이전에 생성된 객체
    기존 프로토타입 객체를 [[Prototype]]에 바인딩한다.
  • 프로토타입 객체 변경 시점 이후 생성된 객체
    변경된 프로토타입 객체를 [[Prototype]]에 바인딩한다.
function Person(name) {
  this.name = name;
}

var foo = new Person('Lee');

// 프로토타입 객체의 변경
Person.prototype = { gender: 'male' };

var bar = new Person('Kim');

console.log(foo.gender); // undefined
console.log(bar.gender); // 'male'

console.log(foo.constructor); // ① Person(name)
console.log(bar.constructor); // ② Object()

changing prototype

프로토타입 체인 동작 조건

객체의 프로퍼티를 참조하는 경우, 해당 객체에 프로퍼티가 없는 경우, 프로토타입 체인이 동작합니다.

객체의 프로퍼티에 값을 할당하는 경우, 프로토타입 체인이 동작하지 않습니다. 이는 객체에 해당 프로퍼티가 있는 경우, 값을 재할당하고 해당 프로퍼티가 없는 경우는 해당 객체에 프로퍼티를 동적으로 추가하기 때문입니다.

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

Person.prototype.gender = 'male'; // ①

var foo = new Person('Lee');
var bar = new Person('Kim');

console.log(foo.gender); // ① 'male'
console.log(bar.gender); // ① 'male'

// 1. foo 객체에 gender 프로퍼티가 없으면 프로퍼티 동적 추가
// 2. foo 객체에 gender 프로퍼티가 있으면 해당 프로퍼티에 값 할당
foo.gender = 'female';   // ②

console.log(foo.gender); // ② 'female'
console.log(bar.gender); // ① 'male'

condition of prototype chaining

728x90
반응형

'프론트엔드 스터디 > Javascript' 카테고리의 다른 글

자바스크립트 실행 컨텍스트  (0) 2021.07.12
자바스크립트에서 scope란?  (0) 2021.07.12
자바스크립트에서 함수란?  (0) 2021.07.09
strict mode(엄격모드)란?  (0) 2021.07.09
Javascript에서 this란?  (0) 2021.07.09
  • [[Prototype]] vs prototype 프로퍼티
  • constructor 프로퍼티
  • Prototype chain
  • 객체 리터럴 방식으로 생성된 객체의 프로토타입 체인
  • 생성자 함수로 생성된 객체의 프로토타입 체인
  • 프로토타입 객체의 확장
  • 원시 타입의 확장
  • 프로토타입 객체의 변경
  • 프로토타입 체인 동작 조건
'프론트엔드 스터디/Javascript' 카테고리의 다른 글
  • 자바스크립트 실행 컨텍스트
  • 자바스크립트에서 scope란?
  • 자바스크립트에서 함수란?
  • strict mode(엄격모드)란?
옹재
옹재

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.