객체지향 프로그래밍

sykim·2020년 4월 6일
0

들어가기 전에

객체지향 언어로서 클래스 기반의 언어프로토타입 기반의 언어로 나뉩니다.
클래스 기반의 언어는 클래스로 객체의 기본적인 형태와 기능을 정의하고, 생성자로 인스턴스를 만들어서 사용합니다.
프로토타입 기반의 언어는 객체의 자료구조와 메서드 등을 동적으로 바꿀 수 있습니다.
그리고 이런 특징들 때문에 장단점도 각각 존재합니다.
정확성, 안전성, 예측성의 관점으로는 클래스 기반의 언어가 더 나은 결과를 보장하고, 자유성의 관점에서는 프로토타입 기반의 언어가 더 좋습니다.
? es6에서 나온 class 문법은 여기서 말하는 클래스 기반의 언어인 걸까?

클래스, 생성자, 메서드

function Person(arg) {
    this.name = arg;
    this.getName = function() {
        return this.name;
    }
    this.setName = function(value) {
        this.name = value;
    }
}
var me = new Person('zoon');
console.log(me.getName()); // zoon
me.setName('zoon2');
console.log(me.getName()); // zoon2

함수 Person이 클래스이자 생성자의 역할을 합니다. 그리고 사용자는 new 키워드를 이용해 인스턴스를 생성해 사용할 수 있습니다.
위 코드의 me는 Person의 인스턴스로 name이라는 변수가 있고, getName, setName 메서드가 있습니다.

var me = new Person('zoon');
var you = new Person('zoon2');

하지만 이 코드의 단점은 여러 개의 객체를 생성할 때마다 메서드들이 불필요하게 중복해서 생성이 됩니다. 즉, 메모리의 낭비로 이어질 수 있는 형태입니다.

function Person(arg) {
    this.name = arg;
}

Person.prototype.getName = function() {
    return this.name;
};

Person.prototype.setName = function(value) {
    this.name = value;
};
var me = new Person('zoon');
var you = new Person('zoon2');

이 문제를 해결하려면 Person 함수 객체의 prototype 프로퍼티에 메서드를 정의하면 됩니다. (prototype은 필요할 때 재정의가 됨)

자바스크립트에서 클래스 안의 메서드를 정의할 때는 프로토타입 객체에 정의한 후, new로 생성한 객체에서 메서드를 호출하는 것이 좋습니다.

상속

부모 생성자의 기능을 물려받으면서 새로운 기능을 추가하는 것을 의미합니다. 상속, 혹은 확장이라고도 합니다.

function Person(arg) {
    this.name = arg;
}

Person.prototype.getName = function() {
    return this.name;
};

Person.prototype.setName = function(value) {
    this.name = value;
};

function Student(arg) { // (0)

}
var you = new Person('amy');
Student.prototype = you; // (1)

var me = new Student('zzon');
console.log(me.getName()); 
// (2) zzon을 기대하지만 amy가 나온다. 즉, 인스턴스 초기화가 제대로 안 이뤄짐.

(0) 위 코드는 Student 함수 객체를 만들어서, (1) 이 Student 함수 객체의 프로토타입으로 Person 함수 객체의 인스턴스(you)를 참조하게 만들었습니다.
이렇게 되면 Student 함수 객체로 생성된 객체 me의 프로토타입 property가 you를 가리키고,
you는 new Person으로 만들어진 인스턴스이기 때문에 프로토타입 property가 Person prototye을 가리킵니다.
이런 프로토타입 체인으로, me 라는 자식 인스턴스가 부모 프로토타입의 메서드인 getName, setName에 접근이 가능합니다.

(2) 그런데 마지막 콘솔을 출력하면 기대 결과값(zzon)과 다른 값(amy)이 나옵니다.

var me = new Student('zzon');
me.setName('zzon');
console.log(me.getName()); // zzon

setName 메서드로 this.name 프로퍼티를 만들고나서야 원하는 결과값이 출력이 됩니다.

function Student(arg) {
    Person.apply(this, arguments); // *해결책
}

이 문제를 해결하려면 위와 같이 수정합니다. 이 코드를 해석하면, Person의 this들을 그대로 받으라는 의미입니다. Student의 매개변수로 Peson 함수의 변수인 name가 그대로 적용이 됩니다.

부모 클래스의 인스턴스와 자식 클래스의 인스턴스 독립시키기

function Person(arg) {
    this.name = arg;
}

Person.prototype.getName = function() {
    return this.name;
};

Person.prototype.setName = function(value) {
    this.name = value;
};
function Student(arg) {
    Person.apply(this, arguments);
}

var you = new Person('amy');
Student.prototype = you; 

var me = new Student('aaa');
console.log('me.getName() :', me.getName()); // aaa
console.log(Person.prototype)
console.log(Student.prototype)

그런데 위 코드는 자식 클래스의 prototype이 부모 클래스의 인스턴스를 참조합니다.

function Person(arg) {
    this.name = arg;
}

Person.prototype.getName = function() {
    return this.name;
};

Person.prototype.setName = function(value) {
    this.name = value;
};

function Student(arg) {
    Person.apply(this, arguments);
}

function F() {};
F.prototype = Person.prototype;
Student.prototype = new F();
Student.prototype.constructor = Student;
Student.super = Person.prototype;

var me = new Student('aaa');
console.log( me.getName());
console.log(Person.prototype)
console.log(Student.prototype)

빈 함수 F()를 생성합니다. 그리고 이 F()의 인스턴스를 Person.prototype과 Student 사이에 둡니다.


위 콘솔 결과와 같이 Person 함수 객체에서 this에 바인딩되는 것은 Student의 인스턴스가 접근할 수 없습니다.

profile
블로그 이전했습니다

0개의 댓글