JavaScriptのプロトタイプチェーンと継承を分かりやすく詳しく解説
JavaScript言語の継承メカニズムには、「サブクラス」と「親クラス」の概念がなく、「クラス」と「インスタンス」の区別もなく、すべて非常に独特な「プロトタイプ」「プロトタイプチェーン」に依存しています。継承を実装するパターン。
この部分の知識はJavaScriptの核心部分の一つでもあり、難しい点でもあります。みんなの勉強がしやすくなるように、そして私自身の感想を深めるために、勉強ノートを整理しました。コードのこの部分には多くの詳細があり、繰り返し検討する必要があります。それでは始めましょう。
シリーズ目次
JavaScriptのクロージャ(Closure)を簡単に説明
JavaScriptのこれを簡単に説明
JavaScriptのプロトタイプチェーンと継承を簡単に説明
ちょっと試してみる
プロトタイプチェーンの例(書き方のポイント) コメントでは、コードをブラウザにコピーしてテストすることができます、以下同様)
function foo(){} //通过function foo(){}定义一个函数对象 foo.prototype.z = 3; //函数默认带个prototype对象属性 (typeof foo.prototype;//"object") var obj =new foo(); //我们通过new foo()构造器的方式构造了一个新的对象 obj.y = 2; //通过赋值添加两个属性给obj obj.x = 1; //通过这种方式构造对象,对象的原型会指向构造函数的prototype属性,也就是foo.prototype obj.x; // 1 //当访问obj.x时,发现obj上有x属性,所以返回1 obj.y; // 2 //当访问obj.y时,发现obj上有y属性,所以返回2 obj.z; // 3 //当访问obj.z时,发现obj上没有z属性,那怎么办呢?它不会停止查找,它会查找它的原型,也就是foo.prototype,这时找到z了,所以返回3 //我们用字面量创建的对象或者函数的默认prototype对象,实际上它也是有原型的,它的原型指向Object.prototype,然后Object.prototype也是有原型的,它的原型指向null。 //那这里的Object.prototype有什么作用呢? typeof obj.toString; // ‘function' //我们发现typeof obj.toString是一个函数,但是不管在对象上还是对象的原型上都没有toString方法,因为在它原型链的末端null之前都有个Object.prototype方法, //而toString正是Object.prototype上面的方法。这也解释了为什么JS基本上所有对象都有toString方法 'z' in obj; // true //obj.z是从foo.prototype继承而来的,所以'z' in obj返回了true obj.hasOwnProperty('z'); // false //但是obj.hasOwnProperty('z')返回了false,表示z不是obj直接对象上的,而是对象的原型链上面的属性。(hsaOwnProperty也是Object.prototype上的方法)
今、x、y、zにアクセスして、プロトタイプを検索しましたオブジェクトの特定の属性にアクセスし、そのオブジェクトに対応する属性がない場合は、プロトタイプ チェーンを上方向に検索し、それでも null が見つかった場合は、unknown が返されます。
プロトタイプベースの継承
function Foo(){ this.y = 2; } Foo.prototype.x = 1; var obj3 = new Foo(); //①当使用new去调用的时候,函数会作为构造器去调用②this会指向一个对象(这里是obj3),而这个对象的原型会指向构造器的prototype属性(这里是Foo.prototype) obj3.y; //2 obj3.x; //1 //可以看到y是对象上的,x是原型链上的原型(也就是Foo.prototype上)
プロトタイプの属性とプロトタイプ
関数宣言を使用して空の関数を作成する場合、Foo.prototypeがどのような構造であるかを見てみましょう。この関数にはプロトタイプ属性があり、デフォルトでコンストラクターと __proto__ という 2 つの属性があります。コンストラクター属性はそれ自体 Foo を指します。__proto__ は chrome で公開されます (標準属性ではありません。知っておいてください)。次に Foo のプロトタイプです。プロトタイプは Object.prototype を指します。したがって、Object.prototype の一部のメソッド toString および valueOf は、すべての一般オブジェクトで使用されます。
function Foo(){} typeof Foo.prototype; // "object" Foo.prototype.x = 1; var obj3 = new Foo();
要約すると、ここには Foo 関数があります。この関数にはプロトタイプ オブジェクト属性があります。その機能は
new Foo() を使用してインスタンスを構築する場合、このコンストラクターのプロトタイプ属性が新しいプロトタイプとして使用されます。これらのオブジェクトの。したがって、プロトタイプとプロトタイプは別のものであることを理解する必要があります。プロトタイプは関数オブジェクトのデフォルト属性であり、プロトタイプは通常、コンストラクターのプロトタイプ属性です。 あるクラスが別のクラスを継承することを実現
function Person(name, age) { this.name = name; //直接调用的话,this指向全局对象(this知识点整理) this.age = age; //使用new调用Peoson的话,this会指向原型为Person.prototype的空对象,通过this.name给空对象赋值,最后this作为return值 } Person.prototype.hi = function() { //通过Person.prototype.hi创建所有Person实例共享的方法,(可以参考上节的左图:对象的原型会指向构造器的prototype属性,所以想让obj1,obj2,obj3共享一些方法的话,只需在原型对象上一次性地添加属性和方法就可以了); console.log('Hi, my name is ' + this.name + ',I am ' + this.age + ' years old now.')//这里的this是全局对象 }; Person.prototype.LEGS_NUM = 2; //再设置一些对Person类的所有实例共享的数据 Person.prototype.ARMS_NUM = 2; Person.prototype.walk = function() { console.log(this.name + ' is walking...'); }; function Student(name, age, className) { //每个学生都属于人 Person.call(this, name, age); //在Student这个子类里面先调用一下父类 this.className = className; } //下一步就是我们怎么去把Student的实例继承Person.prototype的一些方法 Student.prototype = Object.create(Person.prototype); //Object.create():创建一个空对象,并且这个对象的原型指向它的参数 //这样子我们可以在访问Student.prototype的时候可以向上查找到Person.prototype,又可以在不影响Person的情况下,创建自己的方法 Student.prototype.constructor = Student; //保持一致性,不设置的话constructor会指向Person Student.prototype.hi = function() { //通过Student.prototype.hi这样子的赋值可以覆盖我们基类Person.prototype.hi console.log('Hi, my name is ' + this.name + ',I am ' + this.age + ' years old now, and from ' + this.className + '.'); } Student.prototype.learn = function(subject) { //同时,我们又有自己的learn方法 console.log(this.name + 'is learning ' + subject + ' at' + this.className + '.'); }; //test var yun = new Student('Yunyun', 22, 'Class 3,Grade 2'); yun.hi(); //Hi,my name is Yunyun,I'm 22 years old now,and from Class 3, Grade 2. console.log(yun.ARMS_NUM); // 2 //我们本身对象是没有的,对象的原型也就是Student.prototype也没有,但是我们用了继承,继续向上查找,找到了Person.prototype.ARMS_NUM,所以返回2 yun.walk(); //Yunyun is walking... yun.learn('math'); //Yunyun is learning math at Class 3,Grade 2.
プロトタイプチェーンに基づく継承が実装されます。 では、hi、walk、Learn メソッドを呼び出すとどうなるでしょうか?たとえば、hi メソッドを呼び出す場合、最初にオブジェクト yun に hi メソッドがあるかどうかを確認しますが、このインスタンスには hi メソッドがないため、上方向に検索して、hi メソッドである yun のプロトタイプを見つけます。 Student.protoype にあるので、最終的にそれを呼び出します。その 1 つは Student.prototype.hi であり、他のメソッドの呼び出しも同様です。
プロトタイプを変更する JavaScript のプロトタイプが Java のクラスとは異なることはわかっていますが、Java のクラスを一度作成すると、動的に変更するのは困難ですが、
JavaScript のプロトタイプは実際には通常のオブジェクトですつまり、プログラムの実行段階で、プロトタイプにいくつかの属性を動的に追加または削除することもできます。
上記のコードに基づいて、yun のインスタンスがすでにあるので、実験を進めましょう:
Student.prototype.x = 101; //通过Student.prototype.x把yun的原型动态地添加一个属性x yun.x; //101 //那我们发现所有的实例都会受到影响 //接着我们做个有趣的实验 Student.prototype = {y:2}; //我们直接修改构造器的prototype属性,把它赋值为一个新的对象 yun.y; //undefined yun.x; //101 //所以我们得出:当我们修改Student.prototype值的时候,并不能修改已经实例化的对象 var Tom = new Student('Tom',3,'Class LOL KengB'); Tom.x; //undefined //但当我们创建一个新的实例时,这一次x就不见了, Tom.y; //2 //并且y是新的值

实现继承的方式
实现继承有多种方式,下面我们还是以Person和Student来分析
function Person() { } function Student() { } Student.prototype = Person.prototype; // 我们可不可用这种方式呢?这种方法是错误的:因为子类Student有自己的一些方法 //,如果通过这样子赋值,改变Student的同时也改变了Person。 Student.prototype = new Person(); //这种方式是可以实现的,但是调用构造函数有时候也是有问题的,比如要传进Person一个name和age //,这里的Student是个类,还没实例化,这时候有些奇怪了,传什么都不是。 Student.prototype = Object.create(Person.prototype); //相对来说这中方式是比较理想的,这里我们创建了一个空的对象 //,并且对象的原型指向Person.prototype,这样我们既保证了继承了Person.prototype上的方法,并且Student.prototype又有自己空的对象。 //但是Object.create是ES5以后才有的
以上就是深入浅出JavaScript之原型链和继承的详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!

ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

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

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

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

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

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

ホットトピック









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

JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

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

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

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

JavaScript で HTTP ステータス コードを取得する方法の紹介: フロントエンド開発では、バックエンド インターフェイスとの対話を処理する必要があることが多く、HTTP ステータス コードはその非常に重要な部分です。 HTTP ステータス コードを理解して取得すると、インターフェイスから返されたデータをより適切に処理できるようになります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法と、具体的なコード例を紹介します。 1. HTTP ステータス コードとは何ですか? HTTP ステータス コードとは、ブラウザがサーバーへのリクエストを開始したときに、サービスが

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

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