(ES6에서는 클래스 문법이 생겼다지만. 동작 및 방식에 대해 아직 공부하지 않았으므로 언급하지 않고 다음으로 미루겠습니다.)
자바(Java)나 파이썬(Python) 등, 보통 객체지향언어들은 클래스(Class) 기반의 언어라고 합니다.
하지만 자바스크립트는 프로토타입 기반(Prototype-based programming)의 객체지향언어입니다.
그래서 자바스크립트로 프로그래밍을 하다 보면
필수적으로 프로토타입 기반 프로그래밍이라는 말을 접하게 됩니다.
자바스크립트 그 자체로 불릴 만큼 중요한 개념인 프로토타입에 관해서 공부해보겠습니다.
프로토타입(Prototype)
"
원래의 형태 또는 전형적인 예, 기초 또는 표준이다.
시제품이 나오기 전의 제품의 원형으로……. 블라블라….
중요한 기능들이 포함된 시스템의 초기 모델이다.
"
- 위키백과
프로토타입에 대한 핵심 단어들을 기억하고 넘어가 보겠습니다.
핵심단어는 [기초, 표준, 제품의 원형, 초기 모델]입니다.
자바스크립트에서 프로토타입의 개념은 프로토타입 객체(Prototype Object), 프로토타입 링크(Prototype Link)를 통틀어 말합니다.
자바스크립트에서 객체가 생성될때, 생성된 객체의 부모가 되는 객체의 원형을 프로토타입 객체(Prototype Object)라고 하며 생성된 객체와 부모 객체와의 참조를 이어주는 링크를 프로토타입 링크(Prototype Link)라고 합니다.
그리고 이 프로토타입 객체를 프로토타입이라고 말하며 객체지향의 상속개념과 같이 부모객체의 속성, 메소드를 상속받아 객체를 재생성하며 프로그래밍하는 기법을 프로토타입 프로그래밍(Prototype-based programming)이라고합니다.
프로토타입 기반 프로그래밍(Prototype-based programming)
다른 명칭으로는
클래스 리스(class-less) 프로그래밍,
프로토타입 지향(prototype-oriented) 프로그래밍,
인스턴스(instance-based) 프로그래밍이라고 불리기도 합니다.
프로토타입 프로그래밍은 자바 클래스의 상속을 흉내 내는 방식의 프로그래밍인데, 클래스와의 차이점을 간단히 확인하고 넘어가겠습니다.
클래스 기반
클래스라는 추상화된 개념을 선언한 뒤, 이 클래스를 기반으로 객체에 상속을 지원합니다.
여기서 주목해야 할 점은 객체의 형식이 정의된 클래스는 객체가 아닌 개념이라는 점입니다.
// 1. 클래스 정의
public class Person{
public string country = "korea";
public string name;
public int age;
// 2. 클래스 생성자 정의
public Person(string name, int age){
this.name = name;
this.age = age;
}
}
// 3. 객체 생성
Person boy = new Person("yoonhee", "12");
프로토타입 기반
프로토타입 원형 객체를 생성한 뒤, 이 객체를 이용해서 클래스의 상속을 흉내 냅니다.
여기서 주목해야 할 점은 프로토타입은 객체입니다.
// 1. 프로토타입 객체 정의
var base = function(){
this.country = "korea"
}
// 2. 프로토타입 객체 생성자 정의
var Person = function(name, age){
this.name = name;
this.age = age;
}
// 3. 생성자에 프로토타입 상속
Person.prototype = base;
// 4. 객체 생성
var boy = new Person("yoonhee", "12");
매우 유사해 보이지만 프로토타입에서는 생성자에 프로토타입을 상속받는 절차가 필요한 것을 확인할 수 있습니다.
자바의 클래스를 흉내낸다고 하는 이유는,
클래스는 개념이며 이 개념을 통해 객체를 생성하여 인스턴스로 만듭니다.
하지만 프로토타입은 클래스와 같은 개발 방식을 흉내내지만, 인스턴스입니다.
클래스는 객체의 형식이 정의된 개념
프로토타입은 객체
인스턴스란(Instance)?
객체 지향 프로그래밍에서 인스턴스는 해당 클래스의 구조로 컴퓨터 저장공간에서 할당된 실체를 의미한다.
프로토타입 객체(Prototype Object)
자바스크립트는 함수(Function) 자료형으로 객체를 선언할 때 생성자(Constructor)를 부여받습니다.
함수 자료형
var func = new Function();
var protoFunc = new func();
var func2 = function(){};
var protoFunc2 = new func();
console.log(func.prototype); // { constructor : f(), __proto__ : Object }
console.log(func2.prototype); // { constructor : f(), __proto__ : Object }
function(){}는 new Function()으로 선언한것과 같습니다.
함수 자료형 외의 다른 자료형에서는 프로토타입 객체를 생성할 수 없습니다.
var obj = {};
var protoObj = new obj(); // obj is not a constructor
var num = 123;
var protoNum = new num(); // num is not a constructor
var boolean = true;
protoBoolean = new boolean() // boolean is not a constructor
그리고 이 생성자(Constructor)가 있는 객체만이 자바스크립트의 new 키워드를 통해 객체를 생성할 수 있습니다.
new 키워드를 통해 객체를 선언하면 함수의 생성과 함께 프로토타입 객체(Prototype Object)도 같이 생성됩니다.
Person 함수를 선언해보겠습니다.
function Person(){}
Person 함수 객체와
Person 함수의 프로토타입 객체가 생성되며
각 객체는 서로를 참조할 수 있게 연결되는 속성을 가지고 있습니다.
1.
Person 객체의 prototype을 통한 프로토타입 객체확인
2.
Person 객체의 constructor 는 Person 객체에 접근할 수 있다.
Person 객체의 __proto__는 Person 객체의 원형에 접근할 수 있습니다.
이때 Person 객체의 원형은 new Function이므로 자바스크립트 네이티브 코드가 반환됩니다.
그리고 객체와 객체 사이에 참조하는 속성들을 레퍼런스 변수라고 합니다.
또한, 이 참조는 동적으로 추가된 사항에 대해서도 접근할 수 있습니다.
레퍼런스 변수 예제
1.생성자를 부여받은 객체만이 new 키워드를 통한 객체 생성이 가능하다고 한 것을 기억해야 합니다.
function Person(x) {
this.x = x;
}; // 프로토타입 객체 생성자 정의
var A = new Person('hello'); // 객체 생성
console.log(A.x); // hello
// Person.x와 동일한 결과
console.log(A.prototype.x) // syntax error
// A는 생성자의 권한이 없는 단일객체이기에 문법오류를 일으킨다.
2.프로토타입 원형 객체의 내부 스코프에 선언한 변수 x와 prototype으로 선언한 x의 위치를 확인합니다.
function Person(){
this.x = 'Person x';
}; // 프로토타입 객체 생성자 정의
Person.prototype.x = 'prototype x'; // 프로토타입 선언
var newPerson = new Person(); // 객체 생성
console.log(newPerson.x); // Person x
console.log(newPerson.__proto__.x); // prototype x
console.log(newPerson);
/*
{
x : 'Person x',
__proto__ :
x : 'prototype x',
constructor : f Person(),
__proto__ : Object
}
*/
new 키워드를 통해 생성한 객체 newPerson은 원형 객체 Person을 참조하고 있기 때문에
Person.x = newPerson.x이 됩니다.
prototype으로 선언한 x는 __proto__ 레퍼런스 변수를 통해 원형 객체 Person의 x를 참조할 수 있습니다.
프로토타입 링크(Prototype Link)
__proto__ 속성은 프로토타입 링크라고 불리며 모든 객체에 존재하는 레퍼런스 속성이자 객체의 원형을 참조합니다. 객체가 생성될 때 프로토타입이 결정되며 사용자가 임의로 변경할수도 있습니다.
사용자가 임의로 동적으로 변경하는 특징을 사용해 상속을 구현할 수도 있습니다.
이 프로토타입 링크를 통해 상위 __proto__를 접근할 수 있으며 이것을 프로토타입 체인(Prototype chain)이라고 합니다. __proto__ 속성의 동작은 내부적으로 Object.getPrototypeOf가 호출되며 프로토타입객체를 반환합니다.
__proto__와 prototype는 둘다 프로토타입객체를 가리키고 있지만 두 속성의 관점은 차이가 있다고합니다.
__proto__ | prototype |
모든 객체가 가지고 있다. |
함수 객체만 가지고 있다. |
부모 프로토타입 객체를 가리킨다. (함수의 경우 Function.prototype을 가리킨다.) |
생성자를 가진 원형으로 생성할 수 있다. |
참고 : https://poiemaweb.com/js-prototype
{}나 new Object로 선언한 객체는 자바스크립트에서 제공하는 네이티브 코드를 원형으로 두고 있어서 __proto__는 네이티브 코드를 contstuctor로 가지고 있습니다.
var object = {};
var object2 = new Object();
예제
원형 객체 Person의 prototype을 상속받습니다.
function Person(){}
Person.prototype.getType = function(){
return "사람입니다";
};
var yoon = new Person();
var jun = new Person();
console.log(yoon.getType()); // 사람입니다
console.log(jun.getType()); // 사람입니다
1. __proto__와 prototype의 위치
function Person(){
this.x = 'Person x';
};
Person.prototype.x = 'prototype x';
var newPerson = new Person();
console.log(newPerson.x); // Person x
console.log(newPerson.__proto__.x); // prototype x'
- Person 함수 내부 스코프에 this.x를 선언
- Person의 prototype에 x를 선언
newPerson 객체의 x에 접근할 때 Person 내부 스코프에 선언한 대로 반환합니다.
__proto__.x로 참조 시 newPerson의 prototype에 접근하여 x를 반환합니다.
그렇다면 프로토타입 객체 원형의 내부 스코프에 this.x를 선언하지 않고 진행했을때에는 어떤 결과가 나오는지 확인해봅니다.
2. 프로토타입체인, 내부에 선언된 값이 없다면 상위를 참조
function Person(){};
Person.prototype.x = 'prototype x';
var newPerson = new Person();
console.log(newPerson.x); //prototype x
console.log(newPerson.__proto__.x); // prototype x'
- Person 내부에 선언된 x의 값을 찾고 x의 값이 없으면 __proto__를 통해 상위 객체에서 x의 값을 찾는다.
위의 예제에서는 prototype.x가 있기 때문에 prototype.x의 값을 반환한다.
프로토타입객체 내부에 변수가 없으면 해당 변수의 값을 찾기 위해 상위 프로토타입을 이어 참조하면서 해당 변수가 있을때까지 반복하여 값을 찾습니다. 끝까지 값이 없으면 undefined를 반환합니다.
이렇게
객체와 객체의 단방향 공유관계를 프로토타입 체인(Prototype Chain)이라고 합니다.
프로토타입체인은 동적으로 상속된 내용을 참조하기 때문에 실행할 때 값이 변경될 수 있으며 동적으로 변경되는 객체의 메서드나 속성을 찾아가는 과정을 프로토타입 룩업이라고 합니다.
__proto__를 통한 직접적인 접근은 참조에 대한 확인일 뿐 개발 시에는 지양해야 하며 Object.getPrototypeOf() 메서드를 사용하여 프로그래밍해야 합니다.
상위 객체를 참조하는 예제는 아래와 같습니다.
var obj = function(){}
console.log(obj.__proto__); // f(){[native code]};
console.log(obj.__proto__.__proto__); // { constructor : f, __defineGetter : f, ..... }
console.log(obj.__proto__.__proto__.__proto__); // null
출처 및 참고
- https://meetup.toast.com/posts/104
- https://velog.io/@yhe228/prototype%EC%9D%B4%EB%9E%80-6dk3v32r55
- https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67
'개념' 카테고리의 다른 글
자바스크립트 세미콜론(;) 꼭 써야하나요? (1) | 2020.03.05 |
---|---|
객체지향프로그래밍이란? 3요소 | 5원칙 | 그리고 추상화 (0) | 2020.02.25 |
Git Squash 깃 스쿼시, 커밋 연결하기/병합하기 소스트리에서 사용해보기 (0) | 2020.02.24 |
git Stash 깃 스태시, 소스트리에서 사용해보기 (1) | 2020.02.24 |
호이스팅(Hoisting)란? (0) | 2020.01.30 |
스코프(Scope)란? (0) | 2020.01.22 |
사파리 브라우저 쿠키(Cookie) 이슈 (0) | 2019.11.27 |
개발 버전표기 대략적으로 이해하기 (0) | 2019.11.15 |