연습장

14. Prototype 프로토타입 본문

JavaScript

14. Prototype 프로토타입

js0616 2024. 7. 17. 23:41

https://poiemaweb.com/js-prototype

 

Prototype | PoiemaWeb

자바스크립트의 모든 객체는 자신의 부모 역할을 하는 객체와 연결되어 있다. 그리고 이것은 마치 객체 지향의 상속 개념과 같이 부모 객체의 프로퍼티 또는 메소드를 상속받아 사용할 수 있게

poiemaweb.com

 


1. 프로토타입 객체

 

Java와 C++은 클래스 기반 객체지향 프로그래밍 언어입니다. 객체를 생성하기 전에 클래스를 정의하고 이를 통해 객체(인스턴스)를 생성합니다.

 

자바스크립트는 이와 달리 프로토타입 기반 객체지향 프로그래밍 언어입니다. 클래스 없이도 객체를 생성할 수 있습니다.

 

모든 자바스크립트 객체는 부모 역할을 담당하는 프로토타입 객체와 연결되어 있습니다. 이 객체는 상속 개념과 유사하게 부모 객체의 프로퍼티나 메서드를 상속받아 사용할 수 있습니다.

 

프로토타입 객체는 모든 자바스크립트 객체가 기본적으로 상속받는 객체로, 프로토타입 체인을 통해 최종적으로 Object.prototype에 이르게 됩니다.

 


 

1.1 Object 생성자

Object 생성자 함수는 자바스크립트의 기본 내장 객체 중 하나로, 모든 객체의 기본이 되는 생성자 함수입니다.

일반적으로 직접 호출되는 경우보다는 객체 리터럴이나 다른 생성자 함수를 통해 객체를 생성할 때 사용됩니다.


let
 obj1 = {}; // 객체 리터럴을 사용한 객체 생성
let obj2 = new Object(); // Object 생성자 함수를 사용한 객체 생성
 

 

이를 상속받은 모든 객체는 Object.prototype에 정의된 메서드와 속성들을 사용할 수 있습니다.


let
 obj = {};
console.log(obj.toString()); // 기본적으로는 Object.prototype.toString()을 호출하게 됨
 

 

obj 객체는 내장 생성자 ' Object' 에 의해 생성되며 'Object.prototype' 메서드를 상속받게 되어 toString 이라는 함수를 사용할 수 있게 된다.

 


 

1.2 obj.__proto__

객체가 직접적으로 참조하는 프로토타입을 가리킵니다.

 
function greet(name) {
    console.log(`Hello, ${name}!`);
}

// greet 함수는 Function.prototype을 프로토타입으로 가리킵니다.
console.log(greet.__proto__ === Function.prototype); // true

// Function.prototype 역시 Object.prototype을 프로토타입으로 가리킵니다.
console.log(Function.prototype.__proto__ === Object.prototype); // true

console.log(greet.__proto__.__proto__ === Object.prototype); // true

 

greet.__proto__는 Function.prototype을 가리키고 있습니다. 따라서 greet 함수는 Function.prototype에 정의된 메서드들을 상속받습니다.

Function.prototype.__proto__는 Object.prototype을 가리키고 있습니다. 따라서 Function.prototype 역시 Object.prototype에 정의된 메서드들을 상속받습니다.

 

모든 함수 객체 -> Function.prototype -> Object.prototype의 메서드들을 상속받아 사용할 수 있습니다.

 


 

1.3 Object.getPrototypeOf()

그러나 __proto__는 내부적으로 사용되는 비표준 속성이며, ECMAScript 6부터는 Object.getPrototypeOf() 메서드를 사용하는 것이 권장됩니다.

let obj = {};

// Object.getPrototypeOf() 메서드를 사용하여 obj의 프로토타입을 가져옵니다.
let prototype = Object.getPrototypeOf(obj);
console.log(prototype === Object.prototype); // true

// 다른 예시: 함수 객체의 경우
function greet(name) {
    console.log(`Hello, ${name}!`);
}

let funcPrototype = Object.getPrototypeOf(greet);
console.log(funcPrototype === Function.prototype); // true

 


 

1.4 그외 Object 생성자에 포함된 프로퍼티

  • Object.prototype: 모든 객체의 기본 프로토타입 객체를 가리킵니다.
  • Object.length: 생성자 함수인 Object의 인수 개수를 나타냅니다 (값은 1입니다)
  • Object.name: 생성자 함수의 이름을 나타냅니다 ("Object")
  • Object.getPrototypeOf(obj): 객체(obj)의 프로토타입을 반환합니다
  • Object.setPrototypeOf(obj, prototype): 객체(obj)의 프로토타입(prototype)을 설정합니다.

 

2. [[Prototype]] vs prototype 프로퍼티

 
let obj = {};
console.log(obj)
 

 

개발자 도구에서 보게되면 다음과 같이 [[Prototype]] 이라는걸 가지게 된다.

 

 

자바스크립트의 모든 객체는 [[Prototype]]이라는 인터널 슬롯(internal slot)를 가진다.

[[Prototype]]의 값은 null 또는 객체이며 상속을 구현하는데 사용된다.

[[Prototype]] 객체의 데이터 프로퍼티는 get 액세스를 위해 상속되어 자식 객체의 프로퍼티처럼 사용할 수 있다.

하지만 set 액세스는 허용되지 않는다.

 


[[Prototype]]

  • 함수를 포함한 모든 객체가 가지고 있는 내부 속성이다.
  • 객체가 생성될 때 해당 객체의 부모 역할을 하는 프로토타입 객체를 지정
  • 함수 객체의 경우 Function.prototype를 가리킨다.
// 함수 정의
function greet(name) {
    console.log(`Hello, ${name}!`);
}

// greet 함수의 프로토타입 체인 확인
console.log(greet.__proto__ === Function.prototype); // true

// Function.prototype에 메서드 추가
Function.prototype.sayHi = function() {
    console.log('Hi there!');
};

// greet 함수를 통해 생성된 객체의 프로토타입 체인 확인
let obj = new greet('Alice');
console.log(obj.__proto__ === greet.prototype); // true
console.log(obj.__proto__.__proto__ === Function.prototype); // true

// 추가한 메서드 호출
obj.sayHi(); // 출력: Hi there!

 

  1. greet 함수가 정의되고, greet.__proto__는 Function.prototype을 가리킵니다.
  2. Function.prototype에 sayHi라는 메서드를 추가하여 모든 함수 객체가 상속받을 수 있도록 합니다.
  3. greet 함수를 통해 생성된 obj 객체의 프로토타입 체인을 확인합니다. obj.__proto__는 greet.prototype을 가리키고, greet.prototype.__proto__는 Function.prototype을 가리킵니다.
  4. obj.sayHi()를 호출하여 추가한 메서드가 정상적으로 동작하는지 확인합니다.

 

prototype 프로퍼티

  • 생성자 함수 객체만 가지고 있는 프로퍼티이다.
  • 해당 생성자 함수로 생성된 객체들의 프로토타입을 지정합니다.
  • 함수가 객체를 생성할 때 그 객체들이 공유할 프로퍼티들을 정의한다.
// 생성자 함수 선언
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// Person 생성자 함수의 prototype 속성에 메서드 추가
Person.prototype.sayHello = function() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

// Person 생성자 함수를 사용하여 객체 생성
let person1 = new Person('Alice', 30);
let person2 = new Person('Bob', 25);

// 객체들의 프로토타입 체인 확인
console.log(person1.__proto__ === Person.prototype); // true
console.log(person2.__proto__ === Person.prototype); // true

// 메서드 호출
person1.sayHello(); // 출력: Hello, my name is Alice and I am 30 years old.
person2.sayHello(); // 출력: Hello, my name is Bob and I am 25 years old.

 

위 예시에서 Person 생성자 함수는 name과 age라는 속성을 가진 객체를 생성합니다. Person.prototype에 sayHello라는 메서드를 추가하여, 이 메서드는 Person 생성자 함수를 통해 생성된 모든 객체에서 공유할 수 있습니다.

각 객체(person1, person2)는 Person.prototype을 프로토타입으로 가지며, sayHello 메서드를 호출할 수 있습니다. 이렇게 하여 생성자 함수를 사용하여 객체를 생성하고, 이 객체들이 공통된 프로토타입을 공유하는 것을 확인할 수 있습니다.


 

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

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);

  // __proto__ 는 프로토타입
  console.log(foo.__proto__ === Person.prototype)
 
  // Person() 생성자 함수를 생성한 객체는 Function() 생성자 함수이다.
  console.log(Person.constructor === Function);

 

Person() 생성자 함수에 의해 생성된 객체를 foo 가 있을때, foo 객체 입장에서 자신을 생성한 객체는 Person() 생성자 함수이며, foo 객체의 프로토타입 객체는 Person.prototype이다. 따라서 프로토타입 객체 Person.prototype의 constructor 프로퍼티는 Person() 생성자 함수를 가리킨다.

 

 

__proto__ 는 prototype 을 가리키는것이고  constructor 는 생성자, 생성자 객체를 가리킨다. 


 

4. Prototype chain
자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근하려고 할 때,

해당 객체에 접근하려는 프로퍼티 또는 메소드가 없다면,

[[Prototype]]이 가리키는 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례대로 검색한다. 이것을 프로토타입 체인이라 한다.

 

var student = {
    name: 'Lee',
    score: 90
  }
 
  // Object.prototype.hasOwnProperty()
  console.log(student.hasOwnProperty('name')); // true

 

student 객체는 hasOwnProperty 메소드를 가지고 있지 않으므로 에러가 발생하여야 하나 정상적으로 결과가 출력되었다. 이는 student 객체의 [[Prototype]]이 가리키는 링크를 따라가서

student 객체의 부모 역할을 하는 프로토타입 객체(Object.prototype)의 메소드 hasOwnProperty를 호출한다.


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

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

 

4.1.1 객체 리터럴 방식

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

 

var person = {
    name: 'Lee',
    gender: 'male',
    sayHello: function(){
      console.log('Hi! my name is ' + this.name);
    }
  };


함수 객체인 Object() 생성자 함수는 일반 객체와 달리 prototype 프로퍼티가 있다.

prototype 프로퍼티

함수 객체가 생성자로 사용될 때 사용하며,

이 함수를 통해 생성된 객체의 부모 역할을 하는 객체, 즉 프로토타입 객체를 가리킨다.

 

[[Prototype]]

객체의 입장에서 자신의 부모 역할을 하는 객체, 즉 프로토타입 객체를 가리킨다.

 

 

 

 

4.2 생성자 함수로 생성된 객체의 프로토타입 체인
생성자 함수로 객체를 생성하기 위해서는 우선 생성자 함수를 정의하여야 한다.

// 함수 선언식
function square(number) {
    return number * number;
  };

// 함수 표현식
var square = function square(number) {
return number * number;
};

 

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

함수표현식으로 함수를 정의할 때 함수 리터럴 방식을 사용한다.

함수 리터럴 방식은 Function() 생성자 함수로 함수를 생성하는 것을 단순화 시킨 것이다.

 

결론, 함수 선언식(축약) -> 함수 표현식 -> Function() 생성자 함수 이며

모든 함수 객체의 prototype 객체는 Function.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');

 

 

 

 

 

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

 

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

 


 

요약.

 
console.log(Object.__proto__ === Function.prototype); // true
console.log(Function.prototype.__proto__ === Object.prototype); // true
 

 

  1. 모든 함수 객체는 Function.prototype을 상속받는다.
  2. 모든 객체는 Object.prototype을 상속받습니다.
  3. 모든 함수 객체 -> Function.prototype -> Object.prototype 상속

단, Object 생성자 함수는 Function.prototype을 직접적으로 상속받지 않으며, Function.prototype 간접적으로 사용가능


 

5. 프로토타입 객체의 확장
프로토타입 객체도 객체이므로 일반 객체와 같이 프로퍼티를 추가/삭제할 수 있다. 그리고 이렇게 추가/삭제된 프로퍼티는 즉시 프로토타입 체인에 반영된다.

 

 

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(); // Hi! my name is Lee

 

 

 

생성자 함수 Person은 프로토타입 객체 Person.prototype와 prototype 프로퍼티에 의해 바인딩되어 있다. Person.prototype 객체는 일반 객체와 같이 프로퍼티를 추가/삭제가 가능하다. 위의 예에서는 Person.prototype 객체에 메소드 sayHello를 추가하였다. 이때 sayHello 메소드는 프로토타입 체인에 반영된다. 따라서 생성자 함수 Person에 의해 생성된 모든 객체는 프로토타입 체인에 의해 부모객체인 Person.prototype의 메소드를 사용할 수 있게 되었다.

 


 

6. 원시 타입(Primitive data type)의 확장
자바스크립트에서 원시 타입(숫자, 문자열, boolean, null, undefined)을 제외한 모든것은 객체이다. 그런데 아래 예제를 살펴보면 원시 타입인 문자열이 객체와 유사하게 동작한다.

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 객체의 프로토타입 객체 String.prototype에 메소드를 추가하면 원시 타입, 객체 모두 메소드를 사용할 수 있다.

var str = 'test';

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

console.log(str.myMethod());      // myMethod
console.log('string'.myMethod()); // myMethod
console.dir(String.prototype);

 

자바스크립트는 표준 내장 객체의 프로토타입 객체에 개발자가 정의한 메소드의 추가를 허용한다.

 

 

 

 

요약

원시 타입은 메소드를 호출할 때 원시 타입과 연관된 객체로 일시적으로 변환되어 프로토타입 객체를 공유하게 된다.

자바스크립트는 표준 내장 객체의 프로토타입 객체에 개발자가 정의한 메소드의 추가를 허용한다.

 


 

7. 프로토타입 객체의 변경
객체를 생성할 때 프로토타입은 결정된다. 결정된 프로토타입 객체는 다른 임의의 객체로 변경할 수 있다. 이것은 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미한다. 이러한 특징을 활용하여 객체의 상속을 구현할 수 있다.

이때 주의할 것은 프로토타입 객체를 변경하면

프로토타입 객체 변경 시점 이전에 생성된 객체 : 기존 프로토타입 객체를 [[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()

 

① constructor 프로퍼티는 Person() 생성자 함수를 가리킨다.

② 프로토타입 객체 변경 후, Person() 생성자 함수의 Prototype 프로퍼티가 가리키는 프로토타입 객체를 일반 객체로 변경하면서 Person.prototype.constructor 프로퍼티도 삭제되었다. 따라서 프로토타입 체인에 의해 bar.constructor의 값은 프로토타입 체이닝에 의해 Object.prototype.constructor 즉 Object() 생성자 함수가 된다.

 

 

 

bar 를 다시 Person 에 이어보려고 했는데 뭔가 어렵다.. 잘안된다.  일단은 바꾸면 끊어진다는것만 기억.

 

 


 

8. 프로토타입 체인 동작 조건

 

객체에서 프로퍼티를 접근할 때, 해당 프로퍼티가 직접적으로 객체에 존재하지 않으면 JavaScript는 프로토타입 체인을 따라 상위 프로토타입 객체에서 해당 프로퍼티를 찾습니다.


객체의 프로퍼티에 값을 할당하는 경우, 프로토타입 체인이 동작하지 않는다. 

이는 객체에 해당 프로퍼티가 있는 경우, 값을 재할당하고 해당 프로퍼티가 없는 경우는 해당 객체에 프로퍼티를 동적으로 추가하기 때문이다.

 

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'

 

 

 

 foo 객체의 gender 프로퍼티에 값을 할당하면 프로토타입 체인이 발생하는게 아닌 foo 객체에 프로퍼티를 동적으로 추가한다.


프로토 타입이란?

 

object.__proto__ 

object.prototype 

object.constructor 

 

Prototype chain 이란

해당 객체에 접근하려는 프로퍼티 또는 메소드가 없을때, [[Prototype]] 이 가리키는 링크를 따라 상위에서 검색 하는것

 

 

 

https://chatgpt.com/

 

'JavaScript' 카테고리의 다른 글

16. Strict mode  (0) 2024.07.18
15. Scope 스코프  (0) 2024.07.18
13. Type Checking 타입 체크  (0) 2024.07.17
12. Function 함수  (0) 2024.07.16
11. 객체와 변경불가성(Immutability)  (0) 2024.07.09