JavaScript/Vanilla JS

객체 Object - 6 [생성자 함수] (캡슐화, 상속)

유혁스쿨 2022. 1. 23. 23:06
728x90
반응형

캡슐화

사각형을 의미하는 Rectangle 객체를 만든다고 가정했을때, 다음과 같이 Rectangle생성자 함수를 만들 수 있다.

function Rectangle(wth, hgt) {
    this.width = wth;
    this.height = hgt;
}
Rectangle.prototype.getArea = function() {
    return this.width * this.height;
}
var rectangle = new Rectangle(5, 10);
alert('AREA : ' + rectangle.getArea());

width 속성에 5를 입력하고 height 속성에 10을 입력했으므로 getArea( ) 메서드를 호출하면 50을 출력한다.

하지만, width속성이나 height 속성에 음수를 입력하면 어떻게 될까?

function Rectangle(wth, hgt) {/* 생략 */}
Rectangle.prototype.getArea = function() {/* 생략 */}
var rectangle = new Rectangle(5, 10);

rectangle.width = -5;

alert('AREA : ' + rectangle.getArea());

결과적으로 프로그램을 실행하는 데는 아무 문제가 없지만 길이가 음수가 나올수 없는것이 물리적 법칙이고 상식이다.

따라서 길이에 음수를 넣으면 Rectangle 객체는 음수값을 계산해낸다.

음수를 넣지 않으면 되는 당연한 방법이 존재하지만 Rectangle이라는 생성자 함수를 구현한 사람만 알고있는 사항이며,  만약 Rectangle 객체를 다른 사람에게 배포했다면 그사람은 아무 것도 모른 채 음수를 넣어 사용할수도 있다.

 

캡슐화란 이렇게 잘못 사용될 수 있는 객체의 특정 부분을 사용자가 사용할 수 없게 막는 기술이라고 정의할 수 있다.

자바스크립트 에서 캡슐화를 구현할 때에는 클로저를 활용한다.

다음과 같이 캡슐화를 진행한다.

function Rectangle(wth, hgt) {
    var width = wth;
    var height = hgt;
}
Rectangle.prototype.getArea = function() {
    return this.width * this.height;
}
var rectangle = new Rectangle(5, 10);
alert('AREA : ' + rectangle.getArea());

this.getWidth = function() {return width;}
this.getHeight = function() {return height;}
this.setWidth = function(wth) {width = wth;}
this.setHeight = function(hgt) {height = hgt;}

이제 외부에서는 지역변수 width와 height를 사용할 수 없고 오직 get ( ) 메서드와 set___( )메서드 형태의 메서드를 사용하여 width와 height 변수에 접근할 수 있게 되었다.

 

다음은 throw 키워드를 사용하여 웹페이지 오류 문구를 띄워보는 유효성검증 로직을 구현한다.

function Rectangle(wth, hgt) {
    var width = wth;
    var height = hgt;
    this.getWidth = function() {return width;}
    this.getHeight = function() {return height;}
    
    this.setWidth = function(wth) {
        if(wth < 0) {
            throw '길이는 음수일 수 없다.';
        } else {
            width = wth;
        }
    }
    
    this.setHeight = function(hgt) {
        if(hgt < 0) {
            throw '길이는 음수일 수 없다.';
        } else {
            height = hgt;
        }
    }
    
}

Rectangle.prototype.getArea = function() {
    return this.width * this.height;
}
var rectangle = new Rectangle(5, 10);
rectangle.setWidth(-2);
alert('AREA : ' + rectangle.getArea());

 

캡슐화의 개념을 잘못 알고있는 초급 프로그래머는 게터와 세터를 만드는것 자체가 캡슐화라고 생각하는 경우가 있다.

이는 너무나 단순한 생각이다.

캡슐화는 만일의 상황을 대비해서 특정 속성이나 메서드를 사용자가 사용할 수 없게 숨겨놓는 것이다.

 


상속

상속은 기존 생성자 함수나 객체를 기반으로 새로운 생성자 함수느 객체를 쉽게 만드는 것을 뜻한다.

기존의 객체를 깁나으로 생성하므로 상속으로 새로 만들어지는 객체에는 기존 객체의 특성이 모두 있다.

이를 기존의 객체에서 유산(속성 및 메서드)을 물려받는 것과 비슷하다고 하여 상속이라는 이름을 사용한다.

상속을 사용하면 이전에 만들었던 객체와 비슷한 객체를 쉽게 생성할 수 있다.

 

아래 코드는 정사각형 객체를 생성하는 생성자 함수 Square를 만드는 코드이다.

function Square(length) {
    this.width = length;
    this.height = length;
}
Square.prototype.getArea = function () {
    return this.getWidth() * this.getHeight();
}

width속성과 height 속성을 캡슐화 하려 할 때, 이전에 캡슐화를 구현하였던 Rectangle 생성자 함수를 상속받으면 쉽게 캡슐화할 수 있다.

function Rectangle(wth, hgt) {
    /* 코드 생략 */
}

Rectangle.prototype.getArea = function () {
    return this.getWidth() * this.getHeight();
}

function Square(length) {
    this.base = Rectangle;
    this.base(length, length);
}

Square.prototype = Rectangle.prototype;
Square.prototype.constructor = Square;

 

생성자 함수 Square 내부에서 작성한 것은 base 속성에 생성자 함수 Rectangle을 넣고 실행한 것과

생성자 함수 Square의 프로토타입에 Rectangle의 프로토타입을 넣은 것 두 가지이다.

 

function Square(length) {
    this.base = Rectangle;
    this.base(length, length);
}

위와 같은 코드를 구현하여 Rectangle 객체의 속성을 Square 객체에 추가했고,

 

다음 코드를 구현하여 Rectangle 객체의 프로토타입이 가진 속성 또는 메서드를 Square객체의 프로토타입에 복사했다.

Square.prototype = Rectangle.prototype;

 

참고로 this.base속성을 가르키는 일부러 base속성의 이름을 사용할 필요는 없다.

어떠한 속성 이름을 사용해도 상관없다.

 

function Rectangle(wth, hgt) {
    /* 코드 생략 */
}

Rectangle.prototype.getArea = function () {
    return this.getWidth() * this.getHeight();
}

function Square(length) {
    this.base = Rectangle;
    this.base(length, length);
}

Square.prototype = Rectangle.prototype;
Square.prototype.constructor = Square;

var rectangle = new Rectangle(5, 7);
var square = new Square(5);

alert(rectangle.getArea() + ' : ' + square.getArea());

위 코드를 실행하면 부모 객체 rectangle과 자식객체 square 각각의 넓이를 출력한다.

 

참고로 생성자 함수 Square의 프로토타입 constructor( ) 메서드에 Square을 다시 넣는 부분에서 constructor( ) 메서드는 프로토타입 객체를 생성했던 함수에 대한 참조를 나타낸다.

따라서 Square 객체의 생성자함수에 대한 참조에 Square 생성자함수를 다시 넣는 작업으로, 별다른 의미는 없다.

 

console.log(square.constructor);
console.log(typeof square.constructor);

 

직접 Square객체의 constructor( )메서드를 출력하면 생성자함수 Square이 아니라 생성자함수 Rectangle을 가리킨다.

따라서 프로토타입의 생성자 함수를 재정의한 것이다. (? square 함수자체를 출력함)

 

자바스크립트에서 정확한 상속 방법은 없다.

상속을 판단하는 기준을 instanceof 키워드를 사용하여 true를 출력하면 상속돼었다고 판단한다.

 

var square = new Square(5);
alert(square instanceof Rectangle);

square 객체는 생성자 함수 Square로부터 만들어진 객체지만, Rectangle로부터 만들어진 객체임을 확인할 수 있다.

생성자 함수 Square가 Rectangle의 상속을 받았기 때문에 가능한 일이다.

728x90
반응형