이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

게시글

강좌16 - JavaScript - 2년 전 등록 / 11일 전 수정

객체 지향 프로그래밍(생성자와 프로토타입)

조회수:
0
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

이번 시간에는 자바스크립트식 객체 지향 프로그래밍(OOP, Object Oriented Programming)에 대해 알아보겠습니다.

생성자

지난 시간에 Date 객체를 new Date()로 만들었던 것 기억하시나요? Date는 분명 객체라고 했는데 new를 붙이고 함수처럼 호출했습니다. 바로 자바스크립트 생성자(constructor) 함수입니다. 객체를 생성하는 함수를 생성자 함수라고 부릅니다. 다른 언어에서는 class가 있지만 자바스크립트에서는 없습니다. 생성자 함수가 그 역할을 대신합니다. 예를 들면 사람 생성자를 만들면 다음과 같습니다.

function Person(name, gender) {
  this.name = name;
  this.gender = gender;
  this.sayHello = function() {
    alert(this.name + ' said "hello"');
  }
  this.... // 사람의 속성과 메소드를 더 정의할 수 있습니다.
}

함수를 만들 때처럼 function을 쓰긴 했는데 함수와는 달리 대문자로 시작하게 만듭니다. 이게 규칙입니다. 생성자를 바탕으로 실제 사람 객체를 만들 수 있습니다. new라는 키워드를 사용해서 호출합니다. new 생성자(인자); 이렇게 하면 됩니다. Zero와 그의 친구 Hero를 만들어보죠!

var zero = new Person('Zero', 'm'); // Person {name: 'Zero', gender: 'm'}
var hero = new Person('Hero', 'f'); // Person {name: 'Hero', gender: 'f'}
zero.sayHello(); // 'Zero said "hello"'
hero.sayHello(); // 'Hero said "hello"'

하나의 Person 생성자를 바탕으로 zero와 hero 두 사람 객체를 만들었습니다. 그리고 이 객체들은 공통적으로 sayHello라는 메소드를 갖고 있습니다.

function Person(name, gender) {
  this.name = name;
  this.gender = gender;
  this.sayHello = function() {
    alert(this.name + ' said "hello"');
  }
}

다시 보면요. Person 옆에 (name, gender)는 처음 만들 때 매개변수를 받는 부분임을 알 수 있습니다. 그렇게 받은 매개변수들을 this.name, this.gender에 저장합니다. this는 바로 생성자 함수 자신을 가리킵니다. 이렇게 this에 저장된 것들은 new를 통해 객체를 만들 때 그 객체에 적용됩니다.

프로토타입

function Person(name, gender) {
  this.name = name;
  this.gender = gender;
}
Person.prototype.sayHello = function() {
  alert(this.name + ' said "hello"');
};

이번에는 다른 건 위의 코드와 같은데 this.sayHello 대신에 Person.prototype에 sayHello를 넣었습니다. prototype은 처음보죠? prototype 객체는 사전 그대로 원형을 뜻합니다. 원래의 모습이죠. 같은 생성자로부터 만들어진 객체들은 모두 이 원형 객체를 공유합니다. 따라서 Person의 prototype 객체에 sayHello라는 메소드를 넣으면 Person 생성자로 만든 모든 객체는 이 메소드 사용이 가능합니다. 공유하고 있기 때문이죠.

그런데 this.sayHello보다 prototype에 Person.prototype.sayHello로 넣는 게 더 효율적입니다. prototype은 모든 객체가 공유하고 있어서 한 번만 만들어지지만, this에 넣은 것은 객체 하나를 만들 때마다 메소드도 하나씩 만들어지기 때문에 불필요한 메모리 낭비가 발생합니다.

아예 메소드뿐만 아니라 속성까지 다 prototype에 넣기도 합니다. 어떤 때 그러는지는 링크 를 참조하세요.

prototype과 __proto__

new Person('Nero', 'm'); // Person {name: "Nero", gender: "m", __proto__: Object}

Nero라는 새 사람 객체를 만들었더니 그 안에 처음보는 __proto__라는 객체가 있습니다. 눌러보니

{
  constructor: function Person(name, gender),
  sayHello: function() {},
  __proto__: Object
}

constructor과 우리가 추가한 sayHello 그리고 또다시 __proto__가 있네요.

undefined

__proto__가 바로 실제 객체를 만들 때 생성자의 prototype이 참조된 모습입니다. 생성자의 prototype을 참조하기 때문에 __proto__와 prototype은 같습니다. 아까 Person.prototype.sayHello를 했던 것이 들어있죠. 추가로 constructor(생성자)에 대한 정보까지 들어있습니다. __proto__ 안에 들어있는 __proto__는 지금은 생각하지 않으셔도 됩니다. 이 강좌 에 나와있습니다.

정리하면

  • constructor는 생성자 함수 그 자체를 가리킴
  • prototype은 생성자 함수에 정의한 모든 객체가 공유할 원형
  • __proto__는 생성자 함수를 new로 호출할 때, 정의해두었던 prototype을 참조한 객체
  • prototype은 생성자 함수에 사용자가 직접 넣는 거고, __proto__는 new를 호출할 때 prototype을 참조하여 자동으로 만들어짐
  • 생성자에는 prototype, 생성자로부터 만들어진 객체에는 __proto__
  • 따라서 사용자는 prototype만 신경쓰면 된다. __proto__는 prototype이 제대로 구현되었는지 확인용으로 사용한다.

prototype, __proto__와 constructor의 관계

prototype과 constructor는 부모자식 관계라고 생각하면 됩니다. Person.prototype.constructor === Person; 입니다. 또한 Person.prototype === (Person생성자로 만들어진 객체).__proto__; 이기 때문에 (Person생성자로 만들어진 객체).__proto__.constructor === Person; 도 성립합니다.

상속의 필요성

위와 같이 생성자 함수를 만들고, new 키워드를 통해 객체도 만들었습니다. Person 생성자 외에도 Vehicle이나 Machine 같은 생성자를 만들 수도 있겠죠?

function Vehicle(name, speed) {
  this.name = name;
  this.speed = speed;
}
Vehicle.prototype.drive = function () {
  console.log(this.name + ' runs at ' + this.speed)
};

이렇게 Vehicle 생성자를 만들었습니다. drive 메소드로 달릴 수 있네요! 근데 drive 메소드 하나밖에 없으니 좀 심심하죠? boost라는 메소드를 만들어서 최고 속도로 달릴 수 있는 기능을 추가하면 괜찮을 거 같은데요. 그래서 Vehicle.prototype.boost = function () {...} 이렇게 추가하자니 한 가지가 걸립니다. Vehicle 안에는 트럭, SUV, 스포츠카, 세단 등 많은데 트럭이 boost하는 건 좀 이상하지 않나요?

이럴 때 상속이 필요합니다. 상속은 외국에서 확장(extend)이라고도 표현합니다. 즉 부모 생성자의 기능을 물려받는 동시에 새로운 기능을 추가할 수도 있는거죠.

다음 시간에는 객체를 상속하는 방법에 대해 알아보겠습니다. Vehicle 생성자의 기능을 상속하고 확장한 Sedan 객체를 만들어봅시다!

투표로 게시글에 관해 피드백을 해주시면 많은 도움이 됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright © 2016- 무단 전재 및 재배포 금지

댓글

8개의 댓글이 있습니다.
2달 전
prototype에 대해 의문점과 헷갈리는 부분이 많았는데 도움이 많이 되었습니다
다만 궁금한게 있는데 Person.prototype.sayHello()인 부분에 관한 질문인데요
Person을 상속하는 두 객체에서 한 객체가 sayHello()를 재정의할때 메모리 할당을 새로 하게 되는 건가요?
한 달 전
상속한 객체가 sayHello()를 "재정의". 재정의하는 순간 메모리 할당이 새로 됩니다. 기존에 있던 객체의 값을 바꾸는 게 아니라 새로운 클래스에 sayHello를 추가하는 것이니까요.
2달 전
질문 있는데요 그럼 매개변수를 안에 입력 안할때는 Object.create(vehicle.prototype) 인데 그냥 뉴 안에 값 입력 안하고 new Vehicle() 이런 식으로 쓰면 안되나요?
2달 전
그러셔도 되는데 버그가 좀 있긴 합니다. Object.create를 권장합니다.
3달 전
자주 들어와서 필요한 자료를 보는데 이번에도 역시 많은 도움이됐어요 항상 감사드립니다 *^^*
4달 전
언제나 좋은글 감사합니다, 좋은 글 잘 읽고 갑니다. 좋은 하루 되세요.
4달 전
감사합니다. 좋은 하루 되세요~
5달 전
많은 자바스크립트 서적과 포스팅을 봤지만 가장 이해가 잘되는거 같습니다.
5달 전
감사합니다! 저도 이렇게 정리할 수 있을 때까지 정말 많은 시행착오를 거쳤습니다. ㅠㅠ
7달 전
너 사람아니지! 인공지능이 대답 해 준거지! 농담입니다.. 상당히 빠른답변 고맙습니다 저도 현재 vue를 사용해서 앱을 만들고있는데 정적 파일의 문제인지 아니면 cookie에 관한 문제인지 모르겠는데 가끔 제로초님 블로그 보다가 답글 왔다고 이메일 옴 -> 클릭해서 들어가면 제가 올린댓글도 사라지고 답변도 없는 경우가 있더라구요 한 3번??정도 그런 경험이 있네욥
7달 전
쿠키의 문제입니다 ㅠㅠ 새로고침 하셔야 보여요
7달 전
와 상당히 이해를 쉽게하게 알려주시네요! 아 궁금증이 하나 생겨서요
함수를 생성할때 해당 객체 내부에 하기보단 바깥에 person.prototype.say.....이런식으로 해주는게 좋다고 하셨는데요
궁금한게

var a = function(name){
this.name = name; <<단순히 변수
this.sayName = function(){ <<객체
consoel.log('name')
}
}

새로 컨스트럭트를 생성하면 각 변수??라고해야하나요 거기에 해당 객체가 또 생겨서 그렇게 만드는게 메모리를 많이 먹는다는 말씀이신가요?

protype을 해서 새로운 객체를 생성해주면 한번만 생성한다는 얘기가.. 조금 이해가 안됩니다

만약 저 내부안에 list : {name : 'kimchi',name:'man'} 이라는 리스트가 존재한다면 이 또한 객체를 생성했기 때문에 만약 다시또 new를 사용해서 생성하면 새로 만들어진다는 말씀이신지요..

프로그래밍을 c#으로 입문을 해서 상당히 헷갈리네요
7달 전
일단 객체가 생기면 메모리를 잡아먹기 때문에 객체가 공통적으로 사용하는 프로토타입 메소드들은 중복 생성되지 않게 해서 메모리 양을 줄이자는 게 주요 골자입니다.
네 리스트를 생성자 함수 안에서 만들면 객체가 새로 만들어집니다(메모리 추가 사용).
프로토타입 메소드들은 Person.prototype 객체 안에 한 번만 들어가있고, 인스턴스들이 메모리 주소를 통해 접근하는 것이기 때문에 낭비가 없습니다.
8달 전
프로토타입이 이해가 될듯하면서 안되네요.ㅜ

Person 생성자함수로 만든 객체 안에 프로토타입객체가 들어있는 형태인가요?

프로토타입의 예제를 예로 들어서

var zero = new Person("zero", m);

이런식으로 zero 변수안에 생성자함수를 통해 객체를 생성하고 sayHello를 호출하려면

zero.prototype.sayHello();

이런식으로 호출하면 되는건가요?

객체안에 또 다른객체가 있는형태로 보면 되는건가요??



제가 이해하고 있는게 잘 이해한건지 모르겠네요ㅜㅜ
8달 전
sayHello는 Person.prototype.sayHello로 등록해두고, 생성자로 만든 객체에서는 zero.sayHello() 이렇게 씁니다.