쑤쑤_CS 기록장
20. 자바스크립트 객체지향 프로그래밍 본문
#1. 객체지향 프로그래밍 개요
객체지향 프로그래밍
: 실세계에 존재하고 인지하고 있는 객체(Object)를 소프트웨어의 세계에서 표현하기 위해 객체의 핵심적인 개념 또는 기능만을 추출하는 추상화(abstraction)를 통해 모델링하려는 프로그래밍 패러다임
관계성있는 객체들의 집합이라는 관점으로 접근하는 소프트웨어 디자인
#2. 클래스 기반 vs. 프로토타입 기반
#2.1 클래스 기반 언어
-
Java C++ C# Python PHP Ruby Object-C
- 클래스란
- 같은 종류의 집단에 속하는 속성(attribute)과 행위(behavior)를 정의한 것
- 객체지향 프로그램의 기본적인 사용자 정의 데이터형(user define data type)
- 객체 생성에 사용되는 패턴 혹은 청사진(blueprint)일 뿐
- new 연산자를 통한 인스턴스화 과정이 필요하다.
- 인스턴스란
- 오직 클래스에서 정의된 범위 내에서만 작동하며
- 런타임에 그 구조를 변경할 수 없다.
- 정확성, 안정성, 예측성 측면에서 클래스 기반 언어가 프로토타입 기반 언어보다 좀 더 나은 결과를 보장한다.
#2.2 프로토타입 기반 언어
-
멀티-패러다임 언어
-
명령형(imperative), 함수형(functional), 프로토타입 기반(prototype-based) 객체지향 언어
객체 생성 방법 (3)
- 객체 리터럴
- Object() 생성자 함수
- 생성자 함수
// 객체 리터럴
var obj1 = {};
obj1.name = 'Lee';
// Object() 생성자 함수
var obj2 = new Object();
obj2.name = 'Lee';
// 생성자 함수
function F() {}
var obj3 = new F();
obj3.name = 'Lee';
#3. 생성자 함수와 인스턴스의 생성
자바스크립트는 생성자 함수와 new 연산자를 통해 인스턴스를 생성할 수 있다. 이때 생성자 함수는 클래스이자 생성자의 역할을 한다.
// 생성자 함수(Constructor)
function Person(name) {
// 프로퍼티
this.name = name;
// 메소드
this.setName = function (name) {
this.name = name;
};
// 메소드
this.getName = function () {
return this.name;
};
}
// 인스턴스의 생성
var me = new Person('Lee');
console.log(me.getName()); // Lee
// 메소드 호출
me.setName('Kim');
console.log(me.getName()); // Kim
각 인스턴스가 내용이 동일한 메소드를 각자 소유한다. 이는 메모리 낭비인데 생성되는 인스턴스가 많아지거나 메소드가 크거나 많다면 무시할 수 없는 문제이다.
문제를 해결하려면 다른 접근 방식이 필요한데 그 해답은 프로토타입이다.
#4. 프로토타입 체인과 메소드의 정의
프로토타입
: 다른 객체를 가리키는 내부 링크
-
프로토타입 체인 : 프로토타입을 통해 직접 객체를 연결
- 프로토타입을 이용
- 생성자 함수 내부의 메소드를 생성자 함수의 prototype 프로퍼티가 가리키는 프로토타입 객체로 이동
- 생성자 함수에 의해 생성된 모든 인스턴스는 프로토타입 체인을 통해 프로토타입 객체의 메소드를 참조할 수 있다.
/**
* 모든 생성자 함수의 프로토타입은 Function.prototype이다. 따라서 모든 생성자 함수는 Function.prototype.method()에 접근할 수 있다.
* @method Function.prototype.method
* @param ({string}) (name) - (메소드 이름)
* @param ({function}) (func) - (추가할 메소드 본체)
*/
Function.prototype.method = function (name, func) {
// 생성자함수의 프로토타입에 동일한 이름의 메소드가 없으면 생성자함수의 프로토타입에 메소드를 추가
// this: 생성자함수
if (!this.prototype[name]) {
this.prototype[name] = func;
}
};
/**
* 생성자 함수
*/
function Person(name) {
this.name = name;
}
/**
* 생성자함수 Person의 프로토타입에 메소드 setName을 추가
*/
Person.method('setName', function (name) {
this.name = name;
});
/**
* 생성자함수 Person의 프로토타입에 메소드 getName을 추가
*/
Person.method('getName', function () {
return this.name;
});
var me = new Person('Lee');
var you = new Person('Kim');
var him = new Person('choi');
console.log(Person.prototype);
// Person { setName: [Function], getName: [Function] }
console.log(me); // Person { name: 'Lee' }
console.log(you); // Person { name: 'Kim' }
console.log(him); // Person { name: 'choi' }
#5. 상속 (Inheritance)
프로토타입을 통해 객체가 다른 객체로 직접 상속된다.
- 의사 클래스 패턴 상속 : 클래스 기반 언어의 상속 방식과 유사하다.
- 프로토타입 패턴 상속 : 프로토타입으로 상속을 구현한다.
#5.1 의사 클래스 패턴 상속
- 자식 생성자 함수의 prototype 프로퍼티를 부모 생성자 함수의 인스턴스로 교체하여 상속을 구현한다.
- 부모와 자식 모두 생성자 함수를 정의하여야 한다
// 부모 생성자 함수
var Parent = (function () {
// Constructor
function Parent(name) {
this.name = name;
}
// method
Parent.prototype.sayHi = function () {
console.log('Hi! ' + this.name);
};
// return constructor
return Parent;
}());
// 자식 생성자 함수
var Child = (function () {
// Constructor
function Child(name) {
this.name = name;
}
// 자식 생성자 함수의 프로토타입 객체를 부모 생성자 함수의 인스턴스로 교체.
Child.prototype = new Parent(); // ②
// 메소드 오버라이드
Child.prototype.sayHi = function () {
console.log('안녕하세요! ' + this.name);
};
// sayBye 메소드는 Parent 생성자함수의 인스턴스에 위치된다
Child.prototype.sayBye = function () {
console.log('안녕히가세요! ' + this.name);
};
// return constructor
return Child;
}());
var child = new Child('child'); // ①
console.log(child); // Parent { name: 'child' }
console.log(Child.prototype); // Parent { name: undefined, sayHi: [Function], sayBye: [Function] }
child.sayHi(); // 안녕하세요! child
child.sayBye(); // 안녕히가세요! child
console.log(child instanceof Parent); // true
console.log(child instanceof Child); // true
Child 생성자 함수가 생성한 인스턴스 child(①)의 프로토타입 객체는 Parent 생성자 함수가 생성한 인스턴스(②)이다.
Parent 생성자 함수가 생성한 인스턴스의 프로토타입 객체는 Parent.prototype이다.
child는 프로토타입 체인에 의해 parent인스턴스와 Parent.prototype의 모든 프로퍼티에 접근 가능하다.
< 의사 클래스 패턴의 문제점 >
- new 연산자를 통해 인스턴스를 생성한다
- 생성자 링크의 파괴
- 객체 리터럴
#5.2 프로토타입 패턴 상속
Object.create 함수를 사용하여 객체에서 다른 객체로 직접 상속을 구현하는 방식
-> new 연산자가 필요없으며, 생성자 링크도 파괴되지 않으며, 객체리터럴에도 사용할 수 있다.
-
Object.create 함수 : 매개변수에 프로토타입으로 설정할 객체 또는 인스턴스를 전달하고 이를 상속하는 새로운 객체를 생성
- 비어있는 생성자 함수 F를 생성한다.
- 생성자 함수 F의 prototype 프로퍼티에 매개변수로 전달받은 객체를 할당한다.
- 생성자 함수 F를 생성자로 하여 새로운 객체를 생성하고 반환한다.
#6. 캡슐화와 모듈 패턴
캡슐화
: 관련있는 멤버 변수와 메소드를 클래스와 같은 하나의 틀 안에 담고 외부에 공개될 필요가 없는 정보를 숨기는 것
= 정보 은닉 (information hiding)
var Person = function(arg) {
var name = arg ? arg : ''; // ①
this.getName = function() {
return name;
};
this.setName = function(arg) {
name = arg;
};
}
var me = new Person('Lee');
var name = me.getName();
console.log(name);
me.setName('Kim');
name = me.getName();
console.log(name);
①의 name 변수는 private 변수가 된다. 자바스크립트는 function-level scope를 제공하므로 함수 내의 변수는 외부에서 참조할 수 없다.
public 메소드 getName, setName은 클로저로서 private 변수(자유 변수)에 접근할 수 있다. 이것이 기본적인 정보 은닉 방법이다.
person 함수는 객체를 반환한다. 이 객체 내의 메소드 getName, setName은 클로저로서 private 변수 name에 접근할 수 있다. 이러한 방식을 모듈 패턴이라 하며 캡슐화와 정보 은닉를 제공한다. 많은 라이브러리에서 사용되는 유용한 패턴이다.
< 주의할 점 >
-
private 멤버가 객체나 배열일 경우, 반환된 해당 멤버의 변경이 가능하다
: 얕은 복사가 일어나 private 멤버의 참조값을 반환하기 때문 -
person 함수가 반환한 객체는 person 함수 객체의 프로토타입에 접근할 수 없다 = 상속을 구현할 수 없다.
→ 모듈 패턴은 생성자 함수가 아니며 단순히 메소드를 담은 객체를 반환
→ 객체 리터럴 방식으로 생성된 객체로 person의 프로토타입에 접근 (X)
-
이문제를 해결하기 위해서는 반환형을 객체가 아닌 함수로 해야 한다.
'IT 지식 기록 > JavaScript 정리' 카테고리의 다른 글
22. 전역 객체 (0) | 2020.12.24 |
---|---|
21. 빌트인 객체 (0) | 2020.12.24 |
19. 클로저 (0) | 2020.12.24 |
18. 실행 컨텍스트와 자바스크립트의 동작 원리 (0) | 2020.12.24 |
17. 함수 호출 방식에 의해 결정되는 this (0) | 2020.12.22 |