생성자 함수, 프로토타입
아래 내용은 학원 수업과 "모던자바스크립트 Deep Dive : 이웅모 저"를 읽고 정리한 내용입니다.
1. 생성자 함수에 의한 객체 생성
객체를 생성하기 위해서는 객체 리터럴을 사용하는 경우가 가장 많다. 하지만 이 방법은 치명적인 단점이 존재하는데, 한번에 단 하나의 객체만 생성한다는 것이다. 동일한 프로퍼티를 가지고 있지만, 각 프로퍼티의 값은 수 많은 객체를 만들었고, 이 후 각각의 객체에 동일한 프로퍼티를 추가해야 한다고 생각해보자. 몇 개인지 모를 많은 객체들에 프로퍼티를 하나 하나 추가하는 것은 메모리가 낭비되는 것은 물론이고, 비효율적이며, 오타로 인한 실수를 발생시킬 가능성도 높아진다. 이런 단점을 해결하기 위해서는 생성자 함수를 사용하여 객체(인스턴스)를 생성하는 방법이 있다.
생성자 함수에 의한 객체 생성 방식의 장점
생성자 함수란 동일한 형태(프로퍼티 구조가 같은)를 가진 객체(인스턴스)를 생성하기 위한 템플릿처럼 사용할 수 있는 함수를 말한다. 예를 들어 쿠키틀(생성자 함수)을 이용하면 같은 모양(프로퍼티 키가 같은)을 하고 있는 다양한 맛(프로퍼티 값)의 쿠키를 빠르게 만들어 낼 수 있는 있는 것과 같다.
생성자 함수는 일반 함수와 동일하게 정의하고, new
연산자와 함께 호출하면 객체를 생성한다. 이렇게 생성된 객체는 인스턴스라고 한다. 각각의 인스턴스마다 달라야 하는 프로퍼티 값은 생성자 함수의 매개변수를 통해 전달받고, 이 값을 생성자 함수에 작성된 메서드에서 사용하기 위해서는 this.프로퍼티키
처럼 this
라는 키워드를 사용하여 참조해야 한다. this
는 함수가 호출되는 방식에 따라서 달라지게 되는데, 생성자 함수로 호출했을 경우에는 생성자 함수가 생성하게 될 인스턴스를 가르킨다.
중요한 것은 생성자 함수로 사용하기 위해 정의한 함수도 일반 함수로 호출이 가능하다는 것이다. 때문에 생성자 함수로 사용하기 위해서는 반드시 new
연산자와 함께 호출해야 한다는 것을 명심해야 한다.
// 생성자 함수
function Cookie(flavor, size) {
this.size = size;
this.flavor = flavor;
this.getWeight = function () {
return this.size * 1.5;
}
}
// 인스턴스 생성
const chocolateCookie = new Cookie('chocolate', 5);
const vanillaCookie = new Cookie('vanilla', 3);
생성자 함수의 인스턴스 생성 과정
-
인스턴스 생성과
this
바인딩
생성자 함수가 런타임 이전에 빈 객체(인스턴스)를 생성하고, 생성자 함수 내부의this
와 바인딩 됨 -
인스턴스 초기화
this
에 바인딩 되어 인스턴스에 프로퍼티와 메서드를 추가하고, 매개변수를 통해 전달받은 값을 인스턴스의 프로퍼티 값에 할당하여 초기화하거나 고정값을 할당한다. -
인스턴스 반환
완성된 인스턴스가 바인딩된this
(생성자 함수가 생성한 인스턴스)가 암묵적으로 반환된다.
유의점
일반 함수에서는 return
을 작성하지 않으면 undefined
가 반환되기 때문에 반드시 return
을 작성해야 한다고 했다. 하지만 생성자 함수에서는 return
을 생략해야 한다. 생성자 함수 내부에서 return
키워드를 통해 원시값을 작성할 경우 이 원시값은 무시되고, 객체를 작성할 경우에는 생성자 함수를 통해서 생성하려고 했던 인스턴스가 아닌 return
키워드를 통해 작성한 객체가 반환된다. 하지만 생성자 함수는 인스턴스를 생성해서 반환하기 위한 것이므로 반드시 return
을 생략해야 한다.
new.target
자바스크립트는 일반 함수와 생성자 함수가 별도로 구분되지 않다. 생성자 함수로 사용하기 위해서 정의했다고 하더라도 일반 함수로 호출할 수 있고, 이런 실수를 방지하기 위해서 파스칼 케이스로 작성하는 것을 일반적인 컨벤션으로 지키는 것이 좋다. 하지만 이 방법은 오류를 근본적으로 방지할 수 있는 방법은 아니다.
ES6에서는 이런 실수를 방지하기 위해서 new.target
을 지원한다. 함수 내부에서 new.target
을 사용하면 new
연산자와 함께 생성자 함수로 호출되었는지 확인할 수 있다. new
연산자와 함께 생성자 함수로서 호출되면 함수 내부의 new.target
은 함수 자신을 가리킨다. new
연산자 없이 일반 함수로서 호출된 함수 내부의 new.target
은 undefined
다. 단, new.target
은 IE를 지원하지 않는다.
// 생성자 함수
function Cookie(flavor, size) {
if (!new.target) {
return new Cookie(flavor, size);
}
this.size = size;
this.flavor = flavor;
this.getWeight = function () {
return this.size * 1.5;
}
}
// 인스턴스 생성
const chocolateCookie = new Cookie('chocolate', 5);
const vanillaCookie = new Cookie('vanilla', 3);
2. 프로토타입
객체지향 프로그래밍
사람은 어떤 사물에 대해서 인지를 하고, 구분할 때 그 사물의 특징이나 속성을 가지고 구분을 한다. 예를 들어서 사람 두명이 나란히 서 있을 때, 외쪽의 사람은 여자이며 안경을 썼고, 이름은 김규리이다. 오른쪽은 남자이며 안경을 쓰지 않았고, 이름은 홍길동이다. 와 같은식으로 말이다. 이러한 인지 방식을 프로그래밍에 접목하려는 것에서 시작된 것이 객체지향 프로그래밍 언어이다. 단, 프로그래밍에서는 모든 특징을 사용하는 것이 아니라 필요한 특징만 사용하게 되는데, 이렇게 필요한 특징만 간추려서 표현하는 것을 추상화라고 한다. 그리고 이렇게 필요한 특성만 모아서 자료구조로 만든 것을 객체라고 한다.
이 때 간추려진 특성들 중에서 상태를 나타내는 것을 프로퍼티라고 하고, 객체의 내부에서 값을 구하는 동작을 메서드라고 한다. 객체는 또 다른 객체와 연결되어 관계성을 가질 수도 있다.
상속과 프로토타입
상속이란 어떤 객체가 다른 객체의 프로퍼티 또는 메서드를 이어 받아서 사용할 수 있도록 하는 것을 말한다. 이 때 자바스크립트의 상속은 프로토타입을 기반으로 하고, 이를 통해서 불필요한 코드의 중복을 제거한다. 이쯤에서 생성자 함수를 다시 생각해 볼 필요가 있다. 생성자 함수는 코드를 반복해서 작성하는 수고를 줄여주지만, 생성자 함수를 통해서 생성된 인스턴스는 결국 공통된 코드를 각각 가지고 있기 때문에 코드를 재사용 한다는 관점에서는 여전히 단점을 가지고 있는 것이다.
그렇다면 이렇게 공통된 코드를 중복하여 작성하지 않고 재사용할 수 있을까? 바로 프로토타입을 통해서 상속받아서 사용하는 것이다. 일반적으로 상속이란 부모의 것을 자식이 물려 받는 것을 말하는데, 프로토타입도 이와 같이 생각할 수 있다. 프로토타입은 부모의 역할을 하는 객체이고, 자식의 역할을 하는 객체는 부모의 역할을 하는 객체인 프로토타입의 프로퍼티를 자신의 프로퍼티처럼 자유롭게 쓸 수 있다. 하지만 부모의 역할을 하는 객체는 자식의 역할을 하는 객체의 프로퍼티를 사용할 수 없다.
이렇게 프로퍼티를 찾아서 사용하고자 할 때 검색하는 메커니즘을 프로토타입 체인이라고 하고, 이 프로토타입 체인 역시 스코프 체인과 마찬가지로 단방향 링크트 리스트 형태를 가지고 있다.
// 생성자 함수
function Cookie(flavor, size) {
this.size = size;
this.flavor = flavor;
}
// 프로토타입
Cookie.prototype.getWeight = function () {
return this.size * 1.5;
}
// 인스턴스 생성
const chocolateCookie = new Cookie('chocolate', 5);
const vanillaCookie = new Cookie('vanilla', 3);
console.log(chocolateCookie.getWeight());
3. this
객체는 프로퍼티와 메서드로 구성된다. 이 때 메서드는 자신이 속해 있는 객체의 프로퍼티를 참조하고 변경할 수 있는데, 그러기 위해서는 내가 속한 객체가 어디인지를 알아야 한다.
생성자 함수는 자신이 생성할 인스턴스를 참조할 수 있어야 해당 인스턴스에 프로퍼티 또는 메서드를 추가할 수 있다. 그런데 인스턴스를 생성하기 위해서는 먼저 생성자 함수를 정의해야 한다. 하지만 생성자 함수를 정의할 때에는 아직 인스턴스가 생성되기 전이기 때문에 생성자 함수가 인스턴스를 직접 가리킬 수 없고, 이것을 해결하기 위해서 this
라는 특수한 식별자를 제공한다.
이 this
는 자기 참조 변수(self-referencing variable)로서 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있게 해준다. 그런데 이 this
는 함수를 호출하는 방식에 따라서 this
가 가르키는 객체가 달라진다. (this 바인딩이 동적으로 결정된다.)
함수 호출 방식 | this 가 가르키는 객체 (this 바인딩) |
---|---|
일반 함수 | 전역 객체 |
메서드 | 메서드를 호출한 객체 |
생성자 함수 | 생성자 함수가 (미래에)생성할 객체 |
Author And Source
이 문제에 관하여(생성자 함수, 프로토타입), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@hyacinta/생성자-함수에-의한-객체-생성저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)