2021. 3. 23. 11:07ㆍFront-end/Javascript
프로토타입
자바스크립트는 멀티 패러다임 프로그래밍 언어
- 자바스크립트는 명령형, 함수형, 프로토타입 기반, 객체지향 프로그래밍을 지원하는 멀티 패러다임 프로그래밍 언어이다.
- C++나 Java 같은 클래스 기반 객체지향 프로그래밍 언어의 특징인 클래스,상속,캡슐화를 위한 키워드인 public, private, protected등이 없어서 자바스크립트는 객체지향 언어가 아니라고 오해하는 경우도 있으나 클래스 기반의 객체지향 프로그래밍 언어보다 효율적이며 더 강력한 객체지향 프로그래밍 능력을 지니고 있는 프로토타입 기반의 객체지향 프로그래밍 언어임
- ES6에서 클래스가 도입되었으나 ES6의 클래스가 기존의 프로토타입 기반 객체지향 모델을 폐지하고 새로운 객체지향 모델을 제공하는 것은 아님
- 클래스는 생성자 함수보다 엄격
- 클래스는 생성자 함수에서는 제공하지 않는 기능도 제공
- 기존 프로토타입 기반 패턴의 문법적 설탕(Syntatic sugar)
- 클래스와 생성자 함수는 모두 프로토타입 기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지는 않음
- 따라서 클래스를 프로토타입 기반 객체 생성 패턴의 단순한 문법적 설탕으로 보기보다는 새로운 객체 생성 매커니즘으로 보는 것이 좀 더 합당하다고 할 수 있다
자바스크립트는 객체 기반 프로그래밍 언어
- 자바스크립트를 이루고 있는 거의 모든 것이 객체임
- 원시 타입의 값을 제외한 나머지 값(함수,배열,정규표현식) 은 모두 객체
객체 지향 프로그래밍
- 실세계의 실체는 특징이나 성질을 나타내는 속성이 있고 이를 통해 실체를 인식하거나 구별할 수 있다.예) 사람 - 이름,주소,성별,나이,신장,체중,학력,성격,직업 등 다양한 속성을 갖음이러한 방식을 프로그래밍에 접목시킨 것이 바로 객체 지향 프로그래밍
- 추상화 : 다양한 속성 중에서 프로그래밍에 필요한 속성만 간추려 내어 표현하는 것
- 객체지향 프로그래밍은 객체의 상태를 나타내는 데이터와, 상태를 조작할 수 있는 동작으로 묶은 자료구조 라고 할 수 있다.이 때 상태의 데이터를 프로퍼티, 동작을 메서드 라고 한다.
- 상속(inheritance) : 객체지향 프로그래밍의 핵심 개념, 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것
- 자바스크립트는 프로토타입을 기반으로 상속을 구현하여 불필요한 중복을 제거한다
프로토타입을 사용하지 않았을 경우(중복 발생)
function Circle(radius){
this.radius = radius;
this.getArea = function () {
return Math.PI * this.radius * this.radius;
};
}
//반지름이 1인 인스턴스 생성
const circle1 = new Circle(1);
//반지름이 2인 인스턴스 생성
const circle2 = new Circle(2);
console.log(circle1.getArea === circle2.getArea); //false
console.log(circle1.getArea()); // 3.141592 ...
console.log(circle2.getArea()); // 12.56637 ...
이런 함수가 있다고 했을 때
Circle 생성자 함수가 생성하는 모든 객체(인스턴스)는 radius 프로퍼티와 getArea 메서드를 갖는다.
radius 프로퍼티 값은 일반적으로 인스턴스마다 다르다(같을 수도 있지만)
그러나 getArea 메소드는 모든 인스턴스가 동일한 내용의 메소드를 사용하므로 단 하나만 생성하여 모든 인스턴스가 공유해서 사용하는 것이 바람직하다.
그런데 이 Circle 생성자 함수는 인스턴스를 생성할 때마다 getArea 메소드를 중복 생성하고 모든 인스턴스가 중복 소유한다.
🔥이처럼 동일한 생성자 함수에 의해 생성된 모든 인스턴스가 동일한 메소드를 중복 소유하는 것은 메모리도 시간도 불필요하게 낭비이다.🔥
⭐위의 예제를 프로토타입을 기반으로 상속을 통해 중복을 제거해보자
function Circle(radius){
this.radius = radius;
}
Circle.prototype.getArea = function (){
return Math.PI * this.radius * this.radius;
}
//반지름이 1인 인스턴스 생성
const circle1 = new Circle(1);
const circle2 = new Circle(2);
console.log(circle1.getArea === circle2.getArea); //true
console.log(circle1.getArea()); // 3.141592 ...
console.log(circle2.getArea()); // 12.56637 ...
Circle 생성자 함수가 생성한 모든 인스턴스는 자신의 프로토타입, 즉 상위(부모) 객체 역할을 하는 Circle.prototype의 모든 프로퍼티와 메소드를 상속받는다.
getArea 메소드는 단 하나만 생성되어 프로토타입인 Circle.prototype의 메소드로 할당되어 있다. 따라서 Circle 생성자 함수가 생성하는 모든 인스턴스는 getArea 메소드를 상속받아 사용할 수 있다.
즉, 자신의 상태를 나타내는 radius 프로퍼티만 개별적으로 소유하고 내용이 동일한 메소드는 상속을 통해 공유하여 사용하는 것이다.
생성자 함수가 생성할 모든 인스턴스가 공통적으로 사용할 프로퍼티나 메소드를 프로토타입에 미리 구현해두면 생성자 함수가 생성할 모든 인스턴스는 별도의 구현 없이 상위(부모) 객체인 프로토타입의 자산을 공유하여 사용할 수 있다.
프로토타입 객체
프로토타입은 어떤 객체의 상위(부모) 객체의 역할을 하는 객체로서, 다른 객체에 공유 프로퍼티(메소드 포함)를 제공한다.
프로토타입을 상속받은 하위(자식) 객체는 상위 객체의 프로퍼티를 자신의 프로퍼티처럼 자유롭게 사용할 수 있다.
모든 객체는 [[Prototype]] 이라는 내부 슬롯을 가지며 이 내부 슬롯의 값은 프로토타입의 참조이다(null인 경우도 있음)[[Prototype]] 에 저장되는 프로토타입은 객체 생성 방식에 의해 결정된다.
모든 객체는 하나의 프로토타입을 갖는다. 그리고 모든 프로토타입은 생성자 함수와 연결되어 있다.
객체와 프로토타입과 생성자 함수는 다음 그림과 같이 서로 연결되어 있다.
즉 [[Prototype]] 내부 슬롯에는 직접 접근할 수 없지만, 위 그림처럼 __proto__ 접근자 프로퍼티를 통해 자신의 [[Prototype]] 내부 슬롯이 가리키는 프로토타입에 간접적으로 접근할 수 있다.
그리고 프로토타입은 자신의 constructor 프로퍼티를 통해 생성자 함수에 접근할 수 있고, 생성자 함수는 자신의 prototype 프로퍼티를 통해 프로토타입에 접근할 수 있다.
__proto__ 접근자 프로퍼티
- 모든 객체는 __proto__ 접근자 프로퍼티를 통해 자신의 프로토타입에 접근할 수 있다.
🍦프로토타입 쉽게 이해하기
- 자바스크립트에서 함수는 객체(object)로 간주된다.
- 그렇기 때문에 프로퍼티를 가질 수 있다.
function Person(name,first,second){
this.name = name;
this.first = first;
this.second = second;
}
이런 함수를 작성했다고 한다면, 함수는 객체이기 때문에 Person이라고 하는 객체가 생성이 된다.
그런데 이 때, Person 말고 객체가 또 하나 생긴다. Person의 prototype객체. 즉 객체가 2개가 생긴다.
- Person 객체
- Person's prototype 객체
그리고 이 두개의 객체는 서로 연관되어 있고 관련되어 있기 때문에 서로가 서로를 알아야 된다.
그래서 우선 Person의 객체는 내부적으로 prototype이라고 하는 프로퍼티가 생기고,
그 프로퍼티는 Person의 prototype 객체를 가리킨다.
그래서 Person.prototype이라고 하면 Person의 prototype객체인 것이다.
function Person(name,first,second){
this.name = name;
this.first = first;
this.second = second;
}
Person.prototype.sum = function(){}
이렇게 정의를 하게 되면 Person의 프로토타입 객체에 지금 sum이라는 프로퍼티가 없으니까 생성하고 여기에 함수를 정의할 것이다.
function Person(name,first,second){
this.name = name;
this.first = first;
this.second = second;
}
Person.prototype.sum = function(){}
var kim = new Person("kim", 10, 20);
위의 코드를 작성하게 되면 kim이라는 객체가 생기는데,
이 객체는 person이라고 하는 constructor function 이 실행되면서 this의 값이 세팅된 결과 이렇게 프로퍼티 값들이 생성이 되고 동시에 __proto__(언더바언더바 프로토 언더바언더바)가 생성이 된다.
그리고 kim이라는 객체가 생성이 될 때 __proto__라는 프로퍼티는 이 kim이라는 객체를 생성한 person의 프로토타입이 __proto__가 되는 것이다.
그럼 우리는 Person.prototype으로도 Person's prototype에 접근할 수 있고 kim.__proto__를 통해서도 Person's prototype에 접근할 수 있다.
function Person(name,first,second){
this.name = name;
this.first = first;
this.second = second;
}
Person.prototype.sum = function(){}
var kim = new Person("kim", 10, 20);
var lee = new Person("Lee",10,10);
console.log(kim.name);
이 상태에서 kim.name이라는 것을 콘솔에 출력하려고 하면 자바스크립트는 kim이라는 객체의 name이라고 하는 프로퍼티가 있는지를 찾아본다. name은 있다. 그러면 name이라고 하는 프로퍼티에 저장된 값을 출력할 것이고, 혹시나 name이라는 값이 없다면 어떻게 동작하도록 약속되어있냐면 __proto__가 가리키는 객체에 name이 있는지 다시 찾아본다.
function Person(name,first,second){
this.name = name;
this.first = first;
this.second = second;
}
Person.prototype.sum = function(){}
var kim = new Person("kim", 10, 20);
var lee = new Person("Lee",10,10);
console.log(kim.name);
kim.sum();
그 다음에 kim.sum이라고 코딩을 하면 어떻게 될까?
kim이라는 객체에는 sum이라는 메소드가 없다.
그러면 자바스크립트는 __proto__를 통해서 Person's prototype에 sum이 있는지를 찾는다. 있으니까 그것을 사용한다.
'Front-end > Javascript' 카테고리의 다른 글
event.target VS event.currentTarget (0) | 2021.03.29 |
---|---|
클래스(Class) (0) | 2021.03.23 |
[Ajax] 리팩토링 함수화 (0) | 2021.03.21 |
[Ajax] Ajax의 적용 (0) | 2021.03.21 |
[Ajax] fetch API - response 객체 (0) | 2021.03.21 |