ホームページ ウェブフロントエンド jsチュートリアル JavaScript のプロトタイプと継承についての詳細

JavaScript のプロトタイプと継承についての詳細

Nov 26, 2019 pm 06:11 PM
javascript プロトタイプ 継承する

この記事では主に、JavaScript でプロトタイプがどのように機能するか、すべてのオブジェクトで共有される [プロトタイプ] の非表示プロパティを介してオブジェクトのプロパティとメソッドをリンクする方法、およびカスタム コンストラクターを作成する方法と、プロパティとメソッドの値を渡すためにプロトタイプの継承がどのように機能するかについて説明します。 。

JavaScript のプロトタイプと継承についての詳細

#はじめに

JavaScript はプロトタイプベースの言語です。つまり、オブジェクトのプロパティとメソッドには次の方法でアクセスできます。クローン作成および拡張機能を備えたユニバーサル オブジェクト共有を備えています。これはプロトタイプ継承と呼ばれ、クラス継承とは異なります。 JavaScript は、一般的なオブジェクト指向プログラミング言語の中でも比較的独特です。PHP、Python、Java などの他のよく知られた言語は、オブジェクトの設計図としてクラスを定義するクラスベースの言語であるためです。

[関連コースの推奨事項:

JavaScript ビデオ チュートリアル ]

この記事では、オブジェクト プロトタイプとは何か、コンストラクターを使用してプロトタイプを拡張する方法について学びます。新しいオブジェクト。継承とプロトタイプ チェーンについても学習します。

JavaScript プロトタイプ

JavaScript のすべてのオブジェクトには、[[プロトタイプ]] と呼ばれる内部プロパティがあります。新しい空のオブジェクトを作成することで、これを実証できます。

let x = {};
ログイン後にコピー

これが通常のオブジェクトの作成方法ですが、これを実現する別の方法はオブジェクト コンストラクターを使用することであることに注意してください。

let x = new object()
ログイン後にコピー

[[Prototype]] を囲む角かっこは、次のことを示します。これは内部プロパティであり、コードから直接アクセスできないこと。

この新しく作成されたオブジェクトの [[プロトタイプ]] を見つけるには、getPrototypeOf() メソッドを使用します。


Object.getPrototypeOf(x);
ログイン後にコピー

出力は、いくつかの組み込みプロパティとメソッドで構成されます。

出力:


{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, …}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

[[プロトタイプ]] を検索する別の方法は、__proto__ プロパティを使用することです。 __proto__ は、[[Prototype]] オブジェクトの内部プロパティを公開するプロパティです。

. _proto__ はレガシー機能であり、実稼働コードでは使用しないでください。また、最新のすべてのブラウザーに存在するわけではないことに注意してください。ただし、この記事ではデモの目的でこれを使用できます。

x.__proto__;
ログイン後にコピー

出力は getPrototypeOf() を使用した場合と同じになります。

出力


{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, …}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

JavaScript では 2 つ以上のオブジェクトにリンクされたメソッドが作成されるため、JavaScript のすべてのオブジェクトに [[プロトタイプ]] があることが重要です。

作成するオブジェクトは、日付や配列などの組み込みオブジェクトと同じ [[プロトタイプ]] を持ちます。この内部プロパティは、プロトタイプ属性を介してあるオブジェクトから別のオブジェクトに参照できます。これについては、チュートリアルで後ほど説明します。

#プロトタイプの継承

#オブジェクトのプロパティまたはメソッドにアクセスしようとすると、JavaScript は最初にオブジェクトを検索します。それ自体が見つからない場合は、オブジェクトの [[Prototype]] を検索します。オブジェクトとその [[Prototype]] をクエリした後に一致が見つからない場合、JavaScript はリンクされたオブジェクトのプロトタイプをチェックし、プロトタイプ チェーンの最後に到達するまで検索を続けます。


#プロトタイプ チェーンの最後は Object.prototype です。すべてのオブジェクトは、オブジェクトのプロパティとメソッドを継承します。チェーンの終わりを超えて検索すると、結果は null になります。

この例では、x は object から継承された空のオブジェクトです。 x は、オブジェクトが持つ任意のプロパティまたはメソッド (toString() など) を使用できます。

x.toString();
ログイン後にコピー

出力

[object Object]
ログイン後にコピー

このプロトタイプ チェーンの長さは 1 チェーンのみです。 x - > オブジェクト。 2 つの [[Prototype]] プロパティを連結しようとすると null になるため、これがわかります。

x.__proto__.__proto__;
ログイン後にコピー

出力

null
ログイン後にコピー

別のタイプのオブジェクトを見てみましょう。 JavaScript で配列を操作した経験がある場合は、pop() や Push() など、配列に多くの組み込みメソッドがあることをご存知でしょう。新しい配列の作成時にこれらのメソッドにアクセスできるのは、作成されたすべての配列が array.prototype のプロパティとメソッドにアクセスできるためです。

新しい配列を作成してこれをテストできます。

let y = [];
ログイン後にコピー

これを配列コンストラクターとして記述することもできることを思い出してください。y = new array() とします。

新しい y 配列の [[Prototype]] を見ると、x オブジェクトよりも多くのプロパティとメソッドがあることがわかります。 Array.prototype からすべてを継承します。

y.__proto__;
ログイン後にコピー
[constructor: ƒ, concat: ƒ, pop: ƒ, push: ƒ, …]
ログイン後にコピー

プロトタイプのコンストラクター属性が Array() に設定されていることがわかります。コンストラクター プロパティは、関数からオブジェクトを構築するためのメカニズムであるオブジェクトのコンストラクターを返します。

この場合、プロトタイプ チェーンが長くなるため、2 つのプロトタイプをリンクできるようになりました。 y -> Array -> Object のように見えます。

y.__proto__.__proto__;
ログイン後にコピー
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, …}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

このチェーンは Object.prototype を参照するようになりました。内部の [[Prototype]] をコンストラクターの Prototype 属性に対してテストして、それらが同じものを参照していることを確認できます。

y.__proto__ === Array.prototype;            // true
y.__proto__.__proto__ === Object.prototype; // true
ログイン後にコピー

isPrototypeOf() メソッドを使用してこれを実現することもできます。

Array.prototype.isPrototypeOf(y);      // true
Object.prototype.isPrototypeOf(Array); // true
ログイン後にコピー

我们可以使用instanceof操作符来测试构造函数的prototype属性是否出现在对象原型链中的任何位置。

y instanceof Array; // true
ログイン後にコピー

总而言之,所有JavaScript对象都具有隐藏的内部[[Prototype]]属性(可能__proto__在某些浏览器中公开)。对象可以扩展,并将继承[[Prototype]]其构造函数的属性和方法。

这些原型可以被链接,并且每个额外的对象将继承整个链中的所有内容。链以Object.prototype结束。

构造器函数

构造函数是用来构造新对象的函数。new操作符用于基于构造函数创建新实例。我们已经看到了一些内置的JavaScript构造函数,比如new Array()和new Date(),但是我们也可以创建自己的自定义模板来构建新对象。

例如,我们正在创建一个非常简单的基于文本的角色扮演游戏。用户可以选择一个角色,然后选择他们将拥有的角色类别,例如战士、治疗者、小偷等等。

由于每个字符将共享许多特征,例如具有名称、级别和生命值,因此创建构造函数作为模板是有意义的。然而,由于每个角色类可能有非常不同的能力,我们希望确保每个角色只能访问自己的能力。让我们看看如何使用原型继承和构造函数来实现这一点。

首先,构造函数只是一个普通函数。当使用new关键字的实例调用它时,它将成为一个构造函数。在JavaScript中,我们按照惯例将构造函数的第一个字母大写。

// Initialize a constructor function for a new Hero
function Hero(name, level) {
  this.name = name;  this.level = level;
}
ログイン後にコピー

我们创建了一个名为Hero的构造函数,它有两个参数:name和level。因为每个字符都有一个名称和一个级别,所以每个新字符都有这些属性是有意义的。this关键字将引用创建的新实例,因此将this.name设置为name参数将确保新对象具有name属性集。

现在我们可以用new创建一个新的实例。

let hero1 = new Hero('Bjorn', 1);
ログイン後にコピー

如果我们在控制台输出hero1,我们将看到已经创建了一个新对象,其中新属性按预期设置。

输出

Hero {name: "Bjorn", level: 1}
ログイン後にコピー

现在,如果我们得到hero1的[[Prototype]],我们将能够看到构造函数Hero()。(记住,它的输入与hero1相同。,但这是正确的方法。)

Object.getPrototypeOf(hero1);
ログイン後にコピー

输出

constructor: ƒ Hero(name, level)
ログイン後にコピー

您可能注意到,我们只在构造函数中定义了属性,而没有定义方法。在JavaScript中,为了提高效率和代码可读性,通常在原型上定义方法。

我们可以使用prototype向Hero添加一个方法。我们将创建一个greet()方法。

// Add greet method to the Hero prototype
Hero.prototype.greet = function () {
  return `${this.name} says hello.`;
}
ログイン後にコピー

因为greet()在Hero的原型中,而hero1是Hero的一个实例,所以这个方法对hero1是可用的。

hero1.greet();
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

输出

"Bjorn says hello."
ログイン後にコピー
ログイン後にコピー

如果检查Hero的[[Prototype]],您将看到greet()现在是一个可用选项。

这很好,但是现在我们想要为英雄创建角色类。将每个类的所有功能都放到Hero构造函数中是没有意义的,因为不同的类具有不同的功能。我们希望创建新的构造函数,但也希望它们连接到原始的Hero。

我们可以使用call()方法将属性从一个构造函数复制到另一个构造函数。让我们创建一个战士和一个治疗构造器。

// Initialize Warrior constructor
function Warrior(name, level, weapon) {
  // Chain constructor with call
  Hero.call(this, name, level);  // Add a new property
  this.weapon = weapon;
}// Initialize Healer constructor
function Healer(name, level, spell) {
  Hero.call(this, name, level);  this.spell = spell;
}
ログイン後にコピー

两个新的构造函数现在都具有Hero和unqiue的属性。我们将把attack()方法添加到Warrior中,而heal()方法添加到Healer中。

Warrior.prototype.attack = function () {
  return `${this.name} attacks with the ${this.weapon}.`;
}

Healer.prototype.heal = function () {
  return `${this.name} casts ${this.spell}.`;
}
ログイン後にコピー

此时,我们将使用两个可用的新字符类创建字符。

const hero1 = new Warrior('Bjorn', 1, 'axe');
const hero2 = new Healer('Kanin', 1, 'cure');
ログイン後にコピー

hero1现在被认为是拥有新属性的战士。

输出

Warrior {name: "Bjorn", level: 1, weapon: "axe"}
ログイン後にコピー

我们可以使用我们在战士原型上设置的新方法。

hero1.attack();
ログイン後にコピー
Console
"Bjorn attacks with the axe."
ログイン後にコピー

但是如果我们尝试使用原型链下面的方法会发生什么呢?

hero1.greet();
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

输出

Uncaught TypeError: hero1.greet is not a function
ログイン後にコピー

使用call()链接构造函数时,原型属性和方法不会自动链接。我们将使用Object.create()来链接原型,确保在创建并添加到原型的任何其他方法之前将其放置。

Warrior.prototype = Object.create(Hero.prototype);
Healer.prototype = Object.create(Hero.prototype);
// All other prototype methods added below…
ログイン後にコピー

现在我们可以在一个战士或治疗者的实例上成功地使用Hero的原型方法。

hero1.greet();
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

输出

"Bjorn says hello."
ログイン後にコピー
ログイン後にコピー

这里是我们的角色创建页面的完整代码。

// Initialize constructor functions
function Hero(name, level) {
  this.name = name;
  this.level = level;
}
 
function Warrior(name, level, weapon) {
  Hero.call(this, name, level);
 
  this.weapon = weapon;
}
 
function Healer(name, level, spell) {
  Hero.call(this, name, level);
 
  this.spell = spell;
}
 
// Link prototypes and add prototype methods
Warrior.prototype = Object.create(Hero.prototype);
Healer.prototype = Object.create(Hero.prototype);
 
Hero.prototype.greet = function () {
  return `${this.name} says hello.`;
}
 
Warrior.prototype.attack = function () {
  return `${this.name} attacks with the ${this.weapon}.`;
}
 
Healer.prototype.heal = function () {
  return `${this.name} casts ${this.spell}.`;
}
 
// Initialize individual character instances
const hero1 = new Warrior('Bjorn', 1, 'axe');
const hero2 = new Healer('Kanin', 1, 'cure');
ログイン後にコピー

使用这段代码,我们已经用基本属性创建了Hero类,从原始构造函数创建了两个名为Warrior和Healer的字符类,向原型添加了方法,并创建了单独的字符实例。

この記事は js チュートリアル 列からのものです。ぜひ学習してください。

以上がJavaScript のプロトタイプと継承についての詳細の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

C++関数継承の詳しい解説:継承で「基底クラスポインタ」と「派生クラスポインタ」を使うには? C++関数継承の詳しい解説:継承で「基底クラスポインタ」と「派生クラスポインタ」を使うには? May 01, 2024 pm 10:27 PM

関数の継承では、「基底クラス ポインター」と「派生クラス ポインター」を使用して継承メカニズムを理解します。基底クラス ポインターが派生クラス オブジェクトを指す場合、上方変換が実行され、基底クラスのメンバーのみにアクセスされます。派生クラス ポインターが基本クラス オブジェクトを指す場合、下向きキャストが実行される (安全ではない) ため、注意して使用する必要があります。

継承とポリモーフィズムは C++ のクラス結合にどのような影響を与えますか? 継承とポリモーフィズムは C++ のクラス結合にどのような影響を与えますか? Jun 05, 2024 pm 02:33 PM

継承とポリモーフィズムはクラスの結合に影響します。派生クラスは基本クラスに依存するため、継承により結合が増加します。ポリモーフィズムにより、オブジェクトは仮想関数と基本クラス ポインターを通じて一貫した方法でメッセージに応答できるため、結合が軽減されます。ベスト プラクティスには、継承を控えめに使用すること、パブリック インターフェイスを定義すること、基本クラスへのデータ メンバーの追加を回避すること、依存関係の注入を通じてクラスを分離することが含まれます。ポリモーフィズムと依存性注入を使用して銀行口座アプリケーションの結合を軽減する方法を示す実践的な例。

C++ 関数の継承の詳細な説明: 継承のエラーをデバッグするには? C++ 関数の継承の詳細な説明: 継承のエラーをデバッグするには? May 02, 2024 am 09:54 AM

継承エラーのデバッグのヒント: 正しい継承関係を確認します。デバッガーを使用してコードをステップ実行し、変数値を調べます。仮想修飾子を正しく使用してください。隠れた相続によって引き起こされる相続ダイアモンド問題を調べてください。抽象クラスに実装されていない純粋仮想関数がないか確認します。

C++ 関数の継承の詳細な説明: 継承における 'is-a' と 'has-a' の関係を理解するには? C++ 関数の継承の詳細な説明: 継承における 'is-a' と 'has-a' の関係を理解するには? May 02, 2024 am 08:18 AM

C++の関数継承を詳しく解説:「is-a」と「has-a」の関係をマスターしよう 関数継承とは?関数の継承は、派生クラスで定義されたメソッドを基本クラスで定義されたメソッドに関連付ける C++ の手法です。これにより、派生クラスが基本クラスのメソッドにアクセスしてオーバーライドできるようになり、基本クラスの機能が拡張されます。 「is-a」および「has-a」関係 関数継承では、「is-a」関係は、派生クラスが基本クラスのサブタイプであること、つまり、派生クラスが基本クラスの特性と動作を「継承」することを意味します。基本クラス。 「has-a」関係は、派生クラスに基本クラス オブジェクトへの参照またはポインターが含まれていること、つまり、派生クラスが基本クラス オブジェクトを「所有」していることを意味します。構文関数継承を実装する方法の構文は次のとおりです: classDerivedClass:pu

原神バージョン 4.4 の新しいマップの紹介 原神バージョン 4.4 の新しいマップの紹介 Jan 31, 2024 pm 06:36 PM

原神 バージョン 4.4 の新しいマップの紹介. 皆さん、原神 4.4 バージョンでは、立月のシー ランタン フェスティバルも始まりました. 同時に、バージョン 4.4 では神羽渓谷と呼ばれる新しいマップ エリアが開始されます。提供された情報によると、沈雨谷は実際には喬営村の一部ですが、プレイヤーはそれを沈雨谷と呼ぶことに慣れています。それでは、新しい地図をご紹介します。原神バージョン 4.4 の新マップのご紹介です。バージョン 4.4 では、立月北部に「陳兪渓谷・上谷」、「陳兪渓谷・南嶺」、「来新山」がオープンします。谷・上谷」。 ※魔神クエスト・第3幕「ドラゴンと自由の歌」プロローグをクリアすると、テレポートアンカーポイントが自動で解放されます。 2. 喬営荘 暖かい春風が再び陳嶼の山野を撫でるとき、香りのよい

「PHP によるオブジェクト指向プログラミング入門: 概念から実践まで」 「PHP によるオブジェクト指向プログラミング入門: 概念から実践まで」 Feb 25, 2024 pm 09:04 PM

オブジェクト指向プログラミングとは何ですか?オブジェクト指向プログラミング (OOP) は、現実世界のエンティティをクラスに抽象化し、オブジェクトを使用してこれらのエンティティを表すプログラミング パラダイムです。クラスはオブジェクトのプロパティと動作を定義し、オブジェクトはクラスをインスタンス化します。 OOP の主な利点は、コードの理解、保守、再利用が容易になることです。 OOP の基本概念 OOP の主な概念には、クラス、オブジェクト、プロパティ、メソッドが含まれます。クラスはオブジェクトの設計図であり、オブジェクトのプロパティと動作を定義します。オブジェクトはクラスのインスタンスであり、クラスのすべてのプロパティと動作を備えています。プロパティは、データを保存できるオブジェクトの特性です。メソッドは、オブジェクトのデータを操作できるオブジェクトの関数です。 OOP の利点 OOP の主な利点は次のとおりです。 再利用性: OOP はコードをより高度なものにすることができます。

C++ 関数の継承の説明: 継承を使用すべきでないのはどのような場合ですか? C++ 関数の継承の説明: 継承を使用すべきでないのはどのような場合ですか? May 04, 2024 pm 12:18 PM

C++ 関数の継承は、次の状況では使用しないでください。 派生クラスが異なる実装を必要とする場合、異なる実装を持つ新しい関数を作成する必要があります。派生クラスが関数を必要としない場合は、空のクラスとして宣言するか、プライベートの未実装の基本クラス メンバー関数を使用して関数の継承を無効にする必要があります。関数が継承を必要としない場合は、コードの再利用を実現するために他のメカニズム (テンプレートなど) を使用する必要があります。

Java インターフェイスと抽象クラス: プログラミング天国への道 Java インターフェイスと抽象クラス: プログラミング天国への道 Mar 04, 2024 am 09:13 AM

インターフェイス: 実装のないコントラクト インターフェイスは、Java でメソッド シグネチャのセットを定義しますが、具体的な実装は提供しません。これは、インターフェイスを実装するクラスに、その指定されたメソッドを強制的に実装するコントラクトとして機能します。インターフェイス内のメソッドは抽象メソッドであり、メソッド本体はありません。コード例: publicinterfaceAnimal{voideat();voidsleep();} 抽象クラス: 部分的に実装されたブループリント 抽象クラスは、そのサブクラスによって継承できる部分的な実装を提供する親クラスです。インターフェイスとは異なり、抽象クラスには具体的な実装と抽象メソッドを含めることができます。抽象メソッドは、abstract キーワードを使用して宣言され、サブクラスによってオーバーライドされる必要があります。コード例: publicabstractcla

See all articles