9.5 슈퍼 클래스와 서브 클래스
- 자바 스크립트는 클래스 기반의 상속 대신 프로토 타입 상속을 지원한다.
하지만 자바스크립트를 사용하여도 생각할 수 있는 클래스 계층을
유사하게 만들 수 있다
- 프토토 타입 객체 역시 하나의 객체이다.
프로토 타입 객체는 Object() 생성자를 사용하여 만들어진다.
이 말은 프로토 타입 객체도 Object.prototype의 프로퍼티들을 상속 받는다는 것을
의미한다. 프로토 타입 기반의 상속은 한 개의 프로토 타입에 제한되지 않는다.
대신 프로토 타입 객체의 체인을 사용한다.
/* * Complex.js: * This file defines a Complex class to represent complex numbers. * Recall that a complex number is the sum of a real number and an * imaginary number and that the imaginary number i is the * square root of -1. */
/* * The first step in defining a class is defining the constructor * function of the class. This constructor should initialize any * instance properties of the object. These are the essential * "state variables" that make each instance of the class different. */ function Complex(real, imaginary) { this.x = real; // The real part of the number this.y = imaginary; // The imaginary part of the number }
/* * The second step in defining a class is defining its instance * methods (and possibly other properties) in the prototype object * of the constructor. Any properties defined in this object will * be inherited by all instances of the class. Note that instance * methods operate implicitly on the this keyword. For many methods, * no other arguments are needed. */
// Return the magnitude of a complex number. This is defined // as its distance from the origin (0,0) of the complex plane. Complex.prototype.magnitude = function() { return Math.sqrt(this.x*this.x + this.y*this.y); };
// Return a complex number that is the negative of this one. Complex.prototype.negative = function() { return new Complex(-this.x, -this.y); };
// Add a complex number to this one and return the sum in a new object. Complex.prototype.add = function(that) { return new Complex(this.x + that.x, this.y + that.y); }
// Multiply this complex number by another and return the product as a // new Complex object. Complex.prototype.multiply = function(that) { return new Complex(this.x * that.x - this.y * that.y, this.x * that.y + this.y * that.x); }
// Convert a Complex object to a string in a useful way. // This is invoked when a Complex object is used as a string. Complex.prototype.toString = function() { return "{" + this.x + "," + this.y + "}"; };
// Test whether this Complex object has the same value as another. Complex.prototype.equals = function(that) { return this.x == that.x && this.y == that.y; }
// Return the real portion of a complex number. This function // is invoked when a Complex object is treated as a primitive value. Complex.prototype.valueOf = function() { return this.x; }
/* * The third step in defining a class is to define class methods, * constants, and any needed class properties as properties of the * constructor function itself (instead of as properties of the * prototype object of the constructor). Note that class methods * do not use the this keyword: they operate only on their arguments. */ // Add two complex numbers and return the result. // Contrast this with the instance method add() Complex.sum = function (a, b) { return new Complex(a.x + b.x, a.y + b.y); };
// Multiply two complex numbers and return the product. // Contrast with the instance method multiply() Complex.product = function(a, b) { return new Complex(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); };
// Here are some useful predefined complex numbers. // They are defined as class properties, and their names are in uppercase // to indicate that they are intended to be constants (although it is not // possible to make JavaScript properties read-only). Complex.ZERO = new Complex(0,0); Complex.ONE = new Complex(1,0); Complex.I = new Complex(0,1);
|
위의 예제에서 Object.prototype 보다 Complex.prototype 객체를 먼저 검색하기 때문에
Object.prototype에 있는 프로퍼티들 중에서 Complex.prototype에 있는 프로퍼티와
이름이 같은 프로퍼티들은 감춰진다.
또 다른 예를 보자. 아래의 예제는 위의 경우를 응용해서 Rectangle 클래스를
똑같이 정의하고, 이를 확장하여 PositionedRectangle 클래스를 정의한 예제다.
// Here is a simple Rectangle class. // It has a width and height and can compute its own area function Rectangle(w, h) { this.width = w; this.height = h; } Rectangle.prototype.area = function() { return this.width * this.height; }
// Here is how we might subclass it
function PositionedRectangle(x, y, w, h) { // First, invoke the superclass constructor on the new object // so that it can initialize the width and height. // We use the call method so that we invoke the constructor as a // method of the object to be initialized. // This is called constructor chaining. Rectangle.call(this, w, h);
// Now store the position of the upper-left corner of the rectangle this.x = x; this.y = y; }
// If we use the default prototype object that is created when we // define the PositionedRectangle() constructor, we'd get a subclass of Object. // To subclass, rectangle, we must explicitly create our prototype object. PositionedRectangle.prototype = new Rectangle();
// We create this prototype object for inheritance purposes, but we // don't actually want to inherit the width and height properties that // each Rectangle object has, so delete them from the prototype. delete PositionedRectangle.prototype.width; delete PositionedRectangle.prototype.height;
// Since the prototype object was created with the Rectangle() constructor, // it has a constructor property that refers to that constructor. But // we want PositionedRectangle objects to have a different constructor // property, so we've got to reassign this default constructor property. PositionedRectangle.prototype.constructor = PositionedRectangle;
// Now that we've configured the prototype object for our subclass, // we can add instance methods to it. PositionedRectangle.prototype.contains = function(x,y) { return (x > this.x && x < this.x + this.width && y > this.y && y < this.y + this.height); }
|
- 서브 클래스를 만드는 순서
① 서브 클래스의 생성자에서 슈퍼 클래스의 생성자를 호출한다.
② 서브 클래스 생성자의 프로토 타입 객체를 설정하기 위한 몇 가지 기술이 필요하다. 반드시 프로토 타입 객체를 명시적으로 슈퍼 클래스의 인스턴스로 만들어야 하고, 그 후에 프로토 타입 객체의 constructor 프로퍼티를
- PositionedRectangle 클래스 예제를 이용에서 다음과 같은 코드를
|
9.5.1 생성자 체이닝
: 서브 클래스 생성자 함수가 상위 클래스의 생성자 함수를 명시적으로 호출하는 방식.
이는 하위 클래스의 프로토 타입 객체에 “superclass”라는 프로퍼티를 추가하는 방법을 통해
생성자 체이닝을 위한 문법을 간소화 할 수 있다.
// 상위 클래스 생성자에 대한 참조를 저장한다. PositionedRectangle.prototype.superclass = Rectangle;
여기서 정의된 프로퍼티를 사용하여 생성자 체이닝을 더욱 간단히 만들 수 있다.
function PositionedRectangle (x, y, w, h)) { this.superclass(w, h); this.x = x; this.y = y; }
이는 상위 클래스 생성자를 호출하는데 더 이상 call() 이나 apply()를 사용할 필요가 없다는 것이다.
|
9.5.2 재정의 된 메서드 호출하기
Rectangle.prototype.toString() = function() { return “[” + this.width + “, ” + this.height + “]”; }
☞ Rectangle.prototype.toString() = function() { return “[” + this.width + “, ” + this.height + “]” }
상위 클래스에 구현된 toString() 메서드는 하위 클래스에 있는 프로토 타입 객체의 프로퍼티다. 이 메서드를 직접 호출할 수 없다는데 주목하라! 어떤 객체에서 이 메서드를 호출해야 하는지를 지정하기 위해 apply() 메서드와 함께 호출하라.
만약 PositionedRectanglr.prototype에 superclass 프로퍼티를 추가했다면 다음과 같이 이 코드를 상위 클래스에 독립적인 방식으로 재지정 할 수 있다.
Rectangle.prototype.toString() = function() { return “[” + this.width + “, ” + this.height + “]”
}
|
'IT_Programming > JavaScript' 카테고리의 다른 글
[펌_번역] Private Members in JavaScript -Douglas Crockford (0) | 2011.05.31 |
---|---|
자바스크립트 완벽사이드 - 9.6 상속 없이 확장하기 (0) | 2011.04.29 |
[JQuery] Ajax IE 캐싱 문제 (0) | 2011.04.03 |
IE 에서 location.href 를 쓰는 경우 referer 값을 제대로 받아오는 방법 (0) | 2011.04.03 |
가우시안 (정규분포) 난수 생성 예제 (0) | 2010.12.14 |