Thực hiện cơ chế thừa kế ECMAScript
- 이전 페이지 thừa kế 메커니즘 예제
- 다음 페이지 Hướng dẫn nâng cao JavaScript
상속 메커니즘 구현
ECMAScript에서 상속 메커니즘을 구현하려면, 상속할 기본 클래스에서 시작할 수 있습니다. 모든 개발자가 정의한 클래스는 기본 클래스로 사용될 수 있습니다. 보안 이유로 로컬 클래스와 호스트 클래스는 기본 클래스로 사용할 수 없으며, 이는 공용으로 접근할 수 있는 컴파일된 브라우저 수준의 코드를 방지할 수 있습니다. 이러한 코드는 악의적인 공격에 사용될 수 있습니다.
기본 클래스를 선택한 후에는 그 서브 클래스를 생성할 수 있습니다. 기본 클래스의 사용 여부는 전적으로 여러분의 결정에 달려 있습니다. 때로는 직접 사용할 수 없는 기본 클래스를 만들고자 할 수 있습니다. 이 기본 클래스는 서브 클래스에 일반적인 함수를 제공하기 위해만 사용됩니다. 이 경우 기본 클래스는 추상 클래스로 간주됩니다.
ECMAScript는 다른 언어와는 달리 추상 클래스를 엄격하게 정의하지 않지만, 때로는 사용할 수 없는 클래스를 생성합니다. 일반적으로 이러한 클래스를 추상 클래스라고 합니다.
생성된 서브 클래스는 상위 클래스의 모든 속성과 메서드를 继承하며, 생성자 및 메서드 구현도 포함됩니다. 기억하십시오, 모든 속성과 메서드는 공용입니다، 따라서 서브 클래스는 이 메서드를 직접 접근할 수 있습니다. 서브 클래스는 상위 클래스에 없는 새로운 속성과 메서드를 추가할 수 있으며, 또한 상위 클래스의 속성과 메서드를 덮을 수 있습니다.
상속 방법
기타 기능과 마찬가지로 ECMAScript는 상속을 구현하는 방법이 여러 가지가 있습니다. 이는 JavaScript의 상속 메커니즘이 명확하게 규정되어 있지 않고, 모방을 통해 구현되기 때문입니다. 이는 모든 상속 세부 사항이 완전히 설명자가 처리되지 않는다는 것을 의미합니다. 개발자로서, 가장 적합한 상속 방법을 선택할 권리가 있습니다.
다음에서는 여러 가지 구체적인 상속 방법을 소개하겠습니다.
객체 가장하기
ECMAScript를 원래 설계할 때는 객체 가장하기(object masquerading)를 설계할 계획이 없었습니다. 이는 개발자들이 함수의 작동 방식을 이해하기 시작하고, 특히 함수 환경에서 this 키워드를 사용하는 방식을 이해하기 시작한 후에 발전했습니다.
이 원리는 다음과 같습니다: 생성자는 this 키워드를 사용하여 모든 속성과 메서드에 값을 할당합니다(즉, 클래스 선언의 생성자 방식을 사용합니다). 생성자는 함수일 뿐이므로, ClassA 생성자를 ClassB의 메서드로 사용할 수 있으며, 이를 호출하면 ClassB는 ClassA 생성자에서 정의된 속성과 메서드를 받게 됩니다. 예를 들어, 다음과 같이 ClassA와 ClassB를 정의할 수 있습니다:
function ClassA(sColor) { this.color = sColor; this.sayColor = function () { alert(this.color); }; } function ClassB(sColor) { }
기억하시나요? 키워드 this는 현재 생성자가 생성하는 객체를 가리킵니다. 그러나 이 메서드에서는 this가 가리키는 객체가 다릅니다. 이 원리는 ClassA를 상속 메커니즘을 구축하는 데 일반 함수로 사용하는 것보다 생성자로 사용하는 것입니다. 다음과 같이 ClassB 생성자를 사용하여 상속 메커니즘을 구현할 수 있습니다:
function ClassB(sColor) { this.newMethod = ClassA; this.newMethod(sColor); delete this.newMethod; }
이 코드에서는 ClassA에 newMethod(기억해 주세요, 함수 이름은 그것을 가리키는 포인터에 불과합니다)을 할당했습니다. 그런 다음 이 메서드를 호출하여 ClassB의 생성자 매개변수 sColor을 전달했습니다. 마지막 코드 행은 ClassA에 대한 참조를 제거하여 이후로는 더 이상 호출할 수 없게 했습니다.
모든 새로운 속성과 메서드는 새로운 메서드 코드 행을 지우고 정의되어야 합니다. 그렇지 않으면, 상속 클래스의 관련 속성과 메서드를 덮어쓸 수 있습니다:
function ClassB(sColor, sName) { this.newMethod = ClassA; this.newMethod(sColor); delete this.newMethod; this.name = sName; this.sayName = function () { alert(this.name); }; }
먼저 설명한 코드가 유효하다는 것을 증명하기 위해 다음 예제를 실행할 수 있습니다:
var objA = new ClassA("blue"); var objB = new ClassB("red", "John"); objA.sayColor(); //출력 "blue" objB.sayColor(); //출력 "red" objB.sayName(); // "John" 출력
객체 가장하기는 다중 상속을 구현할 수 있습니다
그리고, 객체 가장하기는 다중 상속을 지원합니다. 이는 한 클래스가 여러 상속 클래스를 상속할 수 있다는 것을 의미합니다. 다중 상속 메커니즘은 다음과 같은 UML을 통해 표현됩니다:

예를 들어, ClassX와 ClassY 두 개의 클래스가 존재하는 경우, ClassZ가 이 두 클래스를 상속받으려면 다음과 같은 코드를 사용할 수 있습니다:
function ClassZ() { this.newMethod = ClassX; this.newMethod(); delete this.newMethod; this.newMethod = ClassY; this.newMethod(); delete this.newMethod; }
이러한 상속 메서드의 인기로 인해 ECMAScript의 제3판은 Function 객체에 두 개의 메서드를 추가했습니다. 즉, call()과 apply() 메서드입니다.
이러한 상속 메서드의 인기로 인해 ECMAScript의 제3판은 Function 객체에 두 개의 메서드를 추가했습니다. 즉, call()과 apply() 메서드입니다.
call() 메서드
call() 메서드는 클래식 객체 가장자리 메서드와 가장 유사한 메서드입니다. 첫 번째 매개변수는 this의 객체로 사용됩니다. 다른 매개변수들은 함수 자체에 직접 전달됩니다. 예를 들어:
function sayColor(sPrefix,sSuffix) { alert(sPrefix + this.color + sSuffix); }; var obj = new Object(); obj.color = "blue"; sayColor.call(obj, "The color is ", "a very nice color indeed.");
이 예제에서, sayColor() 함수는 객체 외에서 정의되었으며, 어떤 객체에도 속하지 않아도 키워드 this를 참조할 수 있습니다. 객체 obj의 color 속성은 blue입니다. call() 메서드를 호출할 때 첫 번째 매개변수는 obj입니다. 이는 sayColor() 함수 내의 this 키워드에 obj 값을 할당하는 것을 의미합니다. 두 번째와 세 번째 매개변수는 문자열입니다. 이들은 sayColor() 함수의 매개변수 sPrefix와 sSuffix와 일치하며, 마지막으로 생성된 메시지 "The color is blue, a very nice color indeed."가 표시됩니다.
이 메서드를 상속 기제의 객체 가장자리 메서드와 함께 사용하려면, 세 번째 줄의 할당, 호출 및 제거 코드를 대체하면 됩니다:
function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.call(this, sColor); this.name = sName; this.sayName = function () { alert(this.name); }; }
여기서, ClassA의 키워드 this를 새로 생성된 ClassB 객체로 설정해야 하므로 this는 첫 번째 매개변수입니다. 두 번째 매개변수 sColor은 두 개의 클래스에서 모두 독특한 매개변수입니다.
apply() 메서드
apply() 메서드는 두 개의 매개변수를 가지며, this의 객체와 함수에 전달할 매개변수 배열로 사용됩니다. 예를 들어:
function sayColor(sPrefix,sSuffix) { alert(sPrefix + this.color + sSuffix); }; var obj = new Object(); obj.color = "blue"; sayColor.apply(obj, new Array("The color is ", "a very nice color indeed."));
这个例子与前面的例子相同,只是现在调用的是 apply() 方法。调用 apply() 方法时,第一个参数仍是 obj,说明应该赋予 sayColor() 函数中的 this 关键字值是 obj。第二个参数是由两个字符串构成的数组,与 sayColor() 函数中的参数 sPrefix 和 sSuffix 匹配,最后生成的消息仍是 "The color is blue, a very nice color indeed.",将被显示出来。
该方法也用于替换前三行的赋值、调用和删除新方法的代码:
function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.apply(this, new Array(sColor)); this.name = sName; this.sayName = function () { alert(this.name); }; }
同样的,第一个参数仍是 this,第二个参数是只有一个值 color 的数组。可以把 ClassB 的整个 arguments 对象作为第二个参数传递给 apply() 方法:
function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.apply(this, arguments); this.name = sName; this.sayName = function () { alert(this.name); }; }
当然,只有超类中的参数顺序与子类中的参数顺序完全一致时才可以传递参数对象。如果不是,就必须创建一个单独的数组,按照正确的顺序放置参数。此外,还可使用 call() 方法。
原型链(prototype chaining)
继承这种形式在 ECMAScript 中原本是用于原型链的。上一章介绍了定义类的原型方式。原型链扩展了这种方式,以一种有趣的方式实现继承机制。
在上一章学过,prototype 对象是个模板,要实例化的对象都以这个模板为基础。总而言之,prototype 对象的任何属性和方法都被传递给那个类的所有实例。原型链利用这种功能来实现继承机制。
如果用原型方式重定义前面例子中的类,它们将变为下列形式:
function ClassA() { } ClassA.prototype.color = "blue"; ClassA.prototype.sayColor = function () { alert(this.color); }; function ClassB() { } ClassB.prototype = new ClassA();
프로토타입 방식의 기적적인 점은 강조된 파란 코드 행입니다. 여기서, ClassB의 프로토타입 속성을 ClassA의 인스턴스로 설정합니다. 이는 매우 흥미롭습니다. 왜냐하면 ClassA의 모든 속성과 메서드를 원하지만, ClassB의 프로토타입 속성에 하나씩 추가하지 않고도 원하기 때문입니다. ClassA의 인스턴스를 프로토타입 속성에 할당하는 것보다 더 좋은 방법이 있을까요?
주의:ClassA의 생성자 함수를 호출하며, 어떤 파라미터도 전달하지 않습니다. 이는 프로토타입 체인에서의 표준 방식입니다. 생성자 함수가 어떤 파라미터도 가지지 않도록 보장해야 합니다.
객체 가장장과 유사하게, 자식 클래스의 모든 속성과 메서드는 프로토타입 속성에 값을 할당한 후에만 나타나야 합니다. 왜 그런가요? 프로토타입 속성이 새로운 객체로 대체되기 때문입니다. 새로운 메서드를 추가한 원래 객체는 제거됩니다. 따라서, ClassB 클래스에 name 속성과 sayName() 메서드를 추가하는 코드는 다음과 같습니다:
function ClassB() { } ClassB.prototype = new ClassA(); ClassB.prototype.name = ""; ClassB.prototype.sayName = function () { alert(this.name); };
이 코드를 테스트하기 위해 아래의 예제를 실행할 수 있습니다:
var objA = new ClassA(); var objB = new ClassB(); objA.color = "blue"; objB.color = "red"; objB.name = "John"; objA.sayColor(); objB.sayColor(); objB.sayName();
또한, 프로토타입 체인에서 instanceof 연산자의 동작 방식도 매우 독특합니다. ClassB의 모든 인스턴스에서 instanceof는 ClassA와 ClassB 모두에 대해 true를 반환합니다. 예를 들어:
var objB = new ClassB(); alert(objB instanceof ClassA); //출력 "true" alert(objB instanceof ClassB); //출력 "true"
ECMAScript의 弱형 타입 세계에서는 매우 유용한 도구이지만, 객체 가장장을 사용할 때는 이를 사용할 수 없습니다.
프로토타입 체인의 단점은 다중 상속을 지원하지 않는다는 점입니다. 기억해야 할 것은 프로토타입 체인은 다른 유형의 객체로 클래스의 프로토타입 속성을 다시 쓰는 것입니다.
혼합 방식
이 상속 방식은 클래스를 정의하는 데 사용되는 생성자 함수가 아니라 어떤 프로토타입도 사용하지 않습니다. 객체 가장장의 주요 문제는 생성자 함수 방식을 사용해야 하며, 이는 최선의 선택이 아닙니다. 그러나 프로토타입 체인을 사용하면 파라미터를 가진 생성자 함수를 사용할 수 없습니다. 개발자는 어떻게 선택할까요? 답은 간단합니다. 둘 다 사용합니다.
이전 장에서는 클래스를 생성하는 가장 좋은 방법으로 구조 함수를 사용하여 속성을 정의하고, 프로토타입을 사용하여 메서드를 정의하는 방법을 설명했습니다. 이 방법은 thừa kê계 메커니즘에도 적용됩니다. 객체 대체를 사용하여 생성자의 속성을 thừa kê계하고, 프로토타입 라인을 사용하여 프로토타입 객체의 메서드를 thừa kê계합니다. 이 두 가지 방법으로 이전 예제를 다시 작성하면 다음과 같습니다:
function ClassA(sColor) { this.color = sColor; } ClassA.prototype.sayColor = function () { alert(this.color); }; function ClassB(sColor, sName) { ClassA.call(this, sColor); this.name = sName; } ClassB.prototype = new ClassA(); ClassB.prototype.sayName = function () { alert(this.name); };
이 예제에서, thừa kế 메커니즘은 두 줄의 강조된 파란 코드로 구현됩니다. 첫 번째 줄의 강조된 코드에서는 ClassB 생성자에서 ClassA 클래스의 sColor 속성을 객체 대체로 thừa kế합니다. 두 번째 줄의 강조된 코드에서는 프로토타입 라인을 통해 ClassA 클래스의 메서드를 thừa kế합니다. 이러한 혼합 방식에서 프로토타입 라인이 사용되었기 때문에 instanceof 연산자가 정확하게 작동합니다.
아래의 예제는 이 코드를 테스트합니다:
var objA = new ClassA("blue"); var objB = new ClassB("red", "John"); objA.sayColor(); //출력 "blue" objB.sayColor(); //출력 "red" objB.sayName(); //출력 "John"
- 이전 페이지 thừa kế 메커니즘 예제
- 다음 페이지 Hướng dẫn nâng cao JavaScript