序文継承は JS の 3 つの山の 1 つとして知られ、JS の世界に欠かせない要素です。この方法を使用すると、以前の開発コードをより適切に再利用し、開発サイクルを短縮し、開発効率を向上させることができます。 ES6 以前は、JS のクラスはコンストラクターによってシミュレートされており、実際のクラスはありませんでした。ES6 のクラスはシンタックスシュガーですが、この時期のクラスは関数として直接使用できます。ES6 以降、クラスは関数として使用できなくなりました。 継承について話す前に、クラスにはインスタンス属性とパブリック属性という 2 種類の属性があることを明確にする必要があります。以下で説明する継承方法はすべて、この 2 つの点を中心に展開されます。 関数 Animal(名前) { // インスタンスの属性 this.name = name; } // パブリックプロパティ Animal.prototype.eat = function() { // やること... } ES6 以前のコンストラクターを関数として直接呼び出すことを回避するにはどうすればよいですか? ES5 ソリューション: 関数 Animal() { // new を使用せずに直接呼び出された場合は、例外をスローします if (!(this instanceof Animal)) { // new の原則。new を使用すると、これは Animal のインスタンスであり、この instanceof Animal は true になります。 throw new Error("コンストラクターを直接呼び出さないでください"); } } ES6 以降のソリューション: 関数 Animal() { // new が使用される場合、new.target はそれ自身を指します。それ以外の場合は未定義です。ただし、継承時には使用できません。インスタンスのプロパティを継承する場合、元の es5 では Animal.call(this) if (!new.target) { が使用されるためです。 throw new Error("コンストラクターを直接呼び出さないでください"); } } 上記の両方の解決策は、関数として直接呼び出すことで解決できます。コンソールを直接呼び出すと、エラーが報告されます: キャッチされないエラー: コンストラクターを直接呼び出さないでください 次に、JSの継承メソッドを見てみましょう。 プロトタイプチェーン継承プロトタイプ チェーン継承は、より一般的な継承方法の 1 つです。関係するコンストラクター、プロトタイプ、インスタンスの間には特定の関係があります。つまり、各コンストラクターにはプロトタイプ オブジェクトがあり、プロトタイプ オブジェクトにはコンストラクターへのポインターが含まれ、インスタンスにはプロトタイプ オブジェクトへのポインターが含まれます。 関数 Person(名前) { this.name = 名前; this.permission = ["ユーザー", "給与", "休暇"]; } Person.prototype.say = 関数 () { console.log(`${this.name} スポーク`); }; 関数 スタッフ(年齢) { this.age = 年齢; } Staff.prototype = new Person("張三"); const zs = new Staff(12); console.log(zs.name); // Zhang Sanzs.say(); // Zhang Sanが話しました この時点で、コードは期待どおりになっています。次に、インスタンスを作成し、名前と権限を変更します。 const zs = new Staff(12); const zs2 = 新しいスタッフ(18); zs.permission.pop() zs.name = '李斯'; コンソールログ(zs.name); コンソールログ(zs2.name); コンソールにログ出力します。 コンソールログ(zs2.permission); 最初の 2 つの出力は Li Si と Zhang San ですが、最後の 2 つの出力は両方とも ["user"、"salary"] で同じです。なぜこのようなことが起こるのでしょうか? zs2.nameはプロトタイプチェーンを検索し続けるので、最初の2つの出力はLi SiとZhang Sanです。 console.log(zs.__proto__ === zs2.__proto__); を通じて true を出力することで、2 つのインスタンスが同じプロトタイプ オブジェクト Person を使用し、メモリ空間を共有していることがわかります。一方が変更されると、もう一方もそれに応じて変更されます。 上記から、プロトタイプチェーン継承にはいくつかの欠点があることがわかります。 コンストラクタの継承コンストラクタは通常、継承を完了するためにcallとapplyを使用します。 関数 Person(名前) { this.name = 名前; this.permission = ["ユーザー", "給与", "休暇"]; } Person.prototype.say = 関数 () { console.log(`${this.name} スポーク`); }; 関数 スタッフ(名前, 年齢) { Person.call(これ、名前); this.age = 年齢; } Staff.prototype.eat = 関数(){ console.log('食べようよ~~~'); } const zs = new Staff("张三", 12); コンソールログ(zs); 上記のコードのコンソール出力: Staff のプロパティとメソッドを持っているだけでなく、インスタンス化されるたびに Person.call(this, name); が呼び出されるため、Person のプロパティも継承していることがわかります。これにより、プロトタイプ チェーン継承の問題を解決できます。 このとき、Personプロトタイプのメソッドを呼び出します。 zs.say() このとき、コンソールに次のエラーが表示されます: Uncaught TypeError: zs.say is not a function 組み合わせ継承(プロトタイプチェーン継承とコンストラクタ継承の組み合わせ)プロトタイプ チェーン継承とコンストラクター継承には、それぞれ独自の問題と利点があります。 2 つの継承方法を組み合わせると、複合継承が生成されます。 関数 Person(名前) { this.name = 名前; this.permission = ["ユーザー", "給与", "休暇"]; } Person.prototype.say = 関数 () { console.log(`${this.name} スポーク`); }; 関数 スタッフ(名前, 年齢) { // Personの2回目の実行 Person.call(これ、名前); this.age = 年齢; } Staff.prototype.eat = 関数(){ console.log("食べようよ〜〜〜"); }; // Personの最初の実行 Staff.prototype = 新しい Person(); // StaffコンストラクタをStaffにポイントしない場合、Staffインスタンスzs.constructorはPersonにポイントします Staff.prototype.constructor = スタッフ; const zs = new Staff("张三", 12); const ls = new Staff("Li Si", 12); zs.permission.pop(); コンソールにログ出力します。 コンソールにログ出力します。 zs.say(); ls.say(); とりあえず、コンソール出力は正常で、上記の 2 つの継承の欠点は解決されましたが、次の 2 つの新たな問題が発生します。
寄生遺伝Object.create を使用して対象オブジェクトの浅いコピーを取得し、基本クラスの汚染を回避するためにいくつかのメソッドを追加することで、主に複合継承の 2 番目の問題を解決します。 主に次の2行のコードを置き換えます Staff.prototype = 新しい Person(); Staff.prototype.constructor = スタッフ; 次と置き換えます: Staff.prototype = Object.create(Person.prototype, { コンストラクタ: { // StaffコンストラクタをStaffにポイントしない場合、Staffインスタンスzs.constructorはPersonにポイントします 値: スタッフ、 }, }); 組み合わせ寄生遺伝今のところ、Person を 2 回インスタンス化するという問題は解決されていません。次の組み合わせた寄生継承は、上記の問題を完璧に解決できます。これは、ES6 以前のすべての継承方法の中でも最良の継承方法でもあります。 完全なコードは次のとおりです。 関数 Person(名前) { this.name = 名前; this.permission = ["ユーザー", "給与", "休暇"]; } Person.prototype.say = 関数 () { console.log(`${this.name} スポーク`); }; 関数 スタッフ(名前, 年齢) { Person.call(これ、名前); this.age = 年齢; } Staff.prototype = Object.create(Person.prototype, { コンストラクタ: { // StaffコンストラクタをStaffにポイントしない場合、Staffインスタンスzs.constructorはPersonにポイントします 値: スタッフ、 }, }); Staff.prototype.eat = 関数(){ console.log("食べようよ〜〜〜"); }; 実際、継承する場合、Staff.prototypeを変更する方法は上記以外にもたくさんあり、他にもいくつかの方法があります。
Staff.prototype.__proto__ = Person.prototype prototype.__proto__ には互換性の問題があります。それ自体では見つけることができません。プロトタイプ チェーンを上方向に検索し続けます。この時点で、Animal と Tiger は同じアドレスを共有しなくなり、お互いに影響を与えなくなります。
Object.setPrototypeOf(スタッフプロトタイプ、人物プロトタイプ) es6構文は互換性があり、原則はprototype.__proto__メソッドです 拡張するES6 以降では継承に extends を使用できます。これは、現在の開発で最も一般的に使用されている方法でもあります。ブラウザのサポートは理想的ではありませんが、今日のエンジニアリングの向上により、これらは使用を制限する理由ではなくなりました。 クラス Person { コンストラクタ(名前) { this.name = 名前; this.permission = ["ユーザー", "給与", "休暇"]; } 言う() { console.log(`${this.name} スポーク`); } } クラスStaffはPersonを拡張します{ コンストラクタ(名前, 年齢) { super(名前); this.age = 年齢; } 食べる() { console.log("食べようよ〜〜〜"); } } 実際、ES6 継承は babel によってコンパイルされた後、複合寄生継承も採用されるため、その継承原則を習得することに重点を置く必要があります。 要約する以上で、JS における 6 つの継承方法とそのメリット・デメリットについての説明は終了です。JS の継承方法とそのメリット・デメリットについてさらに詳しく知りたい方は、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続きご覧ください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: docker インストール後に hello-world を実行する問題を解決する
この記事では、MySQL 8.0.12のインストール方法に関する詳細なチュートリアルを参考までに紹介...
目次1. 変数意味のある名前を使う不必要なコンテキストを追加しないようにするハードコードされた値を避...
これは見落とされがちな問題かもしれません。まず、次の点を明確にする必要があります。 MySQL では...
まず、MySQL とは何かを簡単に紹介します。簡単に言えば、データベースはデータを格納するための倉庫...
目次1. 4つのコンセプト1. JavaScriptはシングルスレッドです2. タスクキュー3. 同...
この記事で説明する等高レイアウトでは、純粋な CSS を使用して、要素の高さを手動で設定することなく...
目次序文axiosカプセル化の利点パッケージのアイデア設定の優先順位axiosインスタンス構成1. ...
最近、会社でたまたま生放送をしていたのですが、今日は私が遭遇した落とし穴を記録します。会社のサーバー...
[LeetCode] 183.注文しない顧客Web サイトに、Customers テーブルと Or...
1. ElasticSearch 6.4.1 インストール パッケージを次の場所からダウンロードしま...
目次序文antd はどのようにしてコンポーネントをカプセル化するのでしょうか?ディバイダーコンポーネ...
先日、外国人の方がHTML+CSSを使ってHamburgerMenuを実装している動画を見ました。最...
1. package.jsonに追加する "メイン": "electr...
tar バックアップ システム sudo tar cvpzf backup.tgz --exclud...
多くのウェブサイトでは、ユーザーが簡単に検索したり他のページに移動したりできるように、上部にナビゲー...