1. オブジェクト継承メカニズム
この例では、UML を使用して継承メカニズムをわかりやすく説明しています。
継承メカニズムを説明する最も簡単な方法は、幾何学的形状の古典的な例を使用することです。実際、幾何学的形状は、楕円 (円形) と多角形 (特定の数の辺を持つ) の 2 種類しかありません。円は、焦点が 1 つだけある楕円の一種です。三角形、長方形、五角形はすべて、さまざまな数の辺を持つ多角形の一種です。正方形は、すべての辺が同じ長さの長方形の一種です。これは完全な継承関係を構成し、オブジェクト指向の継承メカニズムをよく説明します。
この例では、シェイプは楕円と多角形の基本クラスです (通常、これを親クラスと呼ぶこともでき、すべてのクラスはそれを継承します)。楕円には焦点があり、これは楕円が持つ焦点の数を示します。 Circle は ellipse を継承するため、circle は ellipse のサブクラスであり、ellipse はcircle のスーパークラスです。同様に、三角形、長方形、および五角形はすべて、そのスーパークラスであるポリゴンのサブクラスです。最後に、正方形は長方形を継承します。
この継承関係を説明するには図を使用するのが最善であり、そこで UML (統一モデリング言語) が登場します。 UML の主な用途の 1 つは、継承などの複雑なオブジェクトの関係を視覚的に表現することです。次の図は、形状とそのサブクラスの関係を説明する UML 図です。
UML では、各ボックスはクラス名で記述されたクラスを表します。三角形、長方形、五角形の上部の線分が集まって図形を指しており、これらのクラスが図形を継承していることを示しています。同様に、正方形から長方形へ向かう矢印は、それらの間の継承関係を示しています。
2. ECMAScript 継承メカニズムの実装
ECMAScript を使用して継承メカニズムを実装するには、継承元の基本クラスから始めることができます。開発者が定義したすべてのクラスは基本クラスとして機能できます。セキュリティ上の理由から、ネイティブ クラスとホスト クラスは基本クラスとして機能できません。これにより、悪意のある攻撃に使用される可能性のある、コンパイルされたブラウザー レベルのコードへのパブリック アクセスが防止されます。
基本クラスを選択した後、そのサブクラスを作成できます。基本クラスを使用するかどうかは完全にあなた次第です。場合によっては、直接使用できないが、サブクラスに共通の関数を提供するためにのみ使用される基本クラスを作成したい場合があります。この場合、基本クラスは抽象クラスとみなされます。 ECMAScript は他の言語ほど厳密に抽象クラスを定義しませんが、使用が許可されていないクラスを作成することがあります。通常、このクラスを抽象クラスと呼びます。
作成されたサブクラスは、コンストラクターとメソッドの実装を含む、スーパークラスのすべてのプロパティとメソッドを継承します。すべてのプロパティとメソッドはパブリックであるため、サブクラスはこれらのメソッドに直接アクセスできることに注意してください。サブクラスは、スーパークラスにない新しいプロパティやメソッドを追加したり、スーパークラスのプロパティやメソッドをオーバーライドしたりすることもできます。 JS は本格的なオブジェクト指向言語ではないため、一部の名詞も変更する必要があります。
3. ECMAScript の継承メソッド
ECMAScript 言語では、継承されたクラス (基本クラス) をスーパータイプ、サブクラス (または派生クラス) をサブタイプと呼びます。他の機能と同様に、ECMAScript は複数の方法で継承を実装します。これは、JavaScript の継承メカニズムが明示的に規定されておらず、模倣によって実装されているためです。これは、継承の詳細すべてがインタープリターによって完全に処理されるわけではないことを意味します。開発者には、自分にとって最適な継承方法を決定する権利があります。ここでは、具体的な継承方法をいくつか紹介します。
(1) プロトタイプチェーン方式
この形式の継承は、もともと ECMAScript のプロトタイプ チェーンに使用されていました。前回のブログ投稿では、オブジェクトを作成するプロトタイプ方法を紹介しました。プロトタイプ チェーンは、このアプローチを拡張して、興味深い方法で継承メカニズムを実装します。プロトタイプ オブジェクトはテンプレートであり、インスタンス化されるオブジェクトはこのテンプレートに基づいています。要約すると、プロトタイプ オブジェクトのプロパティとメソッドはすべて、そのクラスのすべてのインスタンスに渡されます。プロトタイプ チェーンはこの機能を利用して継承メカニズムを実装します。例を見てみましょう:
function A() {//超类型A中必须没有参数 this.color = "red"; this.showColor = function () { return this.color; }; }; function B() {//子类型B this.name = "John"; this.showName = function () { return this.name; }; }; B.prototype = new A();//子类型B继承了超类型A,通过原型,形成链条 var a = new A(); var b = new B(); document.write(a.showColor());//输出:blue document.write(b.showColor());//输出:red document.write(b.showName());//输出:John
var b = new B(); document.write(b instanceof A);//输出:true document.write(b instanceof B);//输出:true
使用原型链方式实现了继承,但是这种方式无法共享和子类型给超类型传递参数。我们可以借用构造函数方式(也就是对像冒充)的方式来解决这两个问题。
(2)对象冒充方式
对象冒充方式的其原理如下:构造函数使用this关键字给所有属性和方法赋值(即采用对象声明的构造函数方式)。因为构造函数只是一个函数,所以可使A构造函数成为B的方法,然后调用它。B就会收到A的构造函数中定义的属性和方法。例如,用下面的方式改写上面的例子创建对象A和B:
call()方法
function A(Color) {//创建超类型A this.color = Color; this.showColor = function () { return this.color; }; }; function B(Color,Name) {//创建子类型B A.call(this, Color);//对象冒充,给超类型传参 this.name = Name;//新添加的属性 this.showName = }; var a = new A("blue"); var b = new B("red", "John"); document.write(a.showColor());//输出:blue document.write(b.showColor());//输出:red document.write(b.showName());//输出:John
apply()方法
和上面call()方法唯一的区别就是在子类型B中的代码:
A.call(this,arguments);//对象冒充,给超类型传参
当然,只有超类型中的参数顺序与子类型中的参数顺序完全一致时才可以传递参数对象。如果不是,就必须创建一个单独的数组,按照正确的顺序放置参数。
使用对象冒充方式虽然解决了共享和传参的问题,但是没有原型,复用就更不可能了,所以我们组合上述的两种方式,即原型链方式和对象冒充的方式实现JS的继承。
(3)混合方式
这种继承方式使用构造函数定义类,并非使用任何原型。对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。开发者如何选择呢?答案很简单,两者都用。由于这种混合方式使用了原型链,所以instanceof运算符仍能正确运行。
在上一篇文章,创建对象的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法。用这两种方式重写前面的例子,代码如下:
function A(Color) { this.color = Color; }; A.prototype.showColor = function () { return this.color; }; function B(Color, Name) { A.call(this, Color);//对象冒充 this.name = Name; }; B.prototype = new A();//使用原型链继承 B.prototype.showName = function () { return this.name; }; var a = new A("blue"); var b = new B("red", "John"); document.write(a.showColor());//输出:blue document.write(b.showColor());//输出:red document.write(b.showName());//输出:John
继承的方式和创建对象的方式有一定的联系,推荐使用的继承方式还时原型链和对象冒充的混合方式。使用这种混合方式可以避免一些不必要的问题。
看这篇文章的时候,必须看一下前面的创建对象的方式:详解JavaScript基于面向对象之创建对象(1)和详解JavaScript基于面向对象之创建对象(2)。
以上就是本文的全部内容,希望对大家的学习有所帮助。