JavaScript革命。 文字通り

今日は2017年4月22日、百年前の出来事が起こらなかった人の誕生日です。 だから、革命の歴史について話す理由があります。 しかし、Habrはどこにありますか? -この世界のすべてが最も予期せぬつながりを持っていることがわかった。



ウラジミール・イリイチ・レーニン






私たちが知っているように、JavaScriptは「すべてがオブジェクトに帰着する」という意味でオブジェクト指向言語と見なすことができます(実際、オブジェクトのプロトタイプ)。 一方、広範な哲学的質問では、オブジェクトとプロトタイプも扱っています。 たとえば、革命の目的と資本で説明されたプロトタイプを検討するとき。 それでは、これらのイベントについて現代言語で話しましょう!



すべての革命的な直接性で、すぐにテーブルの上のカードを切ります!

この記事は、基本的にJavaScriptの継承メカニズム、つまりOOPの側面の簡単な説明です。 高度なプロレタリアの開発者は、まったく新しいものを見つけることはありません。 しかし、この資料が、JSでの開発に関心を持つ幅広い人々にとって、記憶に残る比、的なメモとして役立つことを願っています。 さらに、おそらく誰かがロシアの歴史についての知識を強化するでしょう



2つのオブジェクトを作成します。



var stalin = { gulag: true, mustache: true, hero: 1 } var lenin = { baldHead: true, armand: false, criticalImperialism: true }
      
      





__proto__プロパティを使用して、あるオブジェクトが別のオブジェクトの後継であることを示します(この形式の記述はIE10を除くすべてのブラウザーで利用可能であり、ES2015に含まれています )。 1つの相続人と他のプロトタイプ。 後続オブジェクトのプロパティを確認し、そこに__proto__が現れました:



 stalin.__proto__ = lenin; console.log(stalin);
      
      





プロパティがオブジェクトで直接検出されない場合、親オブジェクト(プロトタイプ)で検索されます。 試してみましょう:



 console.log(stalin.baldHead); // true
      
      





はい、プロパティは利用可能ですが、その値は私たちには合いません。 親オブジェクトのプロパティは変更されませんが、書き直します。



 stalin.baldHead = false; console.log(lenin.baldHead); // true -     
      
      





ところで、プロトタイプのプロトタイプは何ですか?



JSでは、1つのケース(以下で詳しく説明します)を除き、オブジェクトはObject .__ proto__(コンソールで確認)を継承します。 デフォルトで使用可能な標準メソッドを含める:たとえば、Object.toString()、Object.valueOf()など。



しかし、不必要な操作を実行しないように、親のプロパティなしでオブジェクト自体のプロパティをリストするにはどうすればよいですか? -これにはhasOwnPropertyがあります。



 for (var key in stalin) { if (stalin.hasOwnProperty(key)) console.log(key + ": " + stalin[key]) }
      
      





ところで、オブジェクトがすでに独自のプロパティを持っている場合、プロトタイプを割り当てた後、プロトタイプの値で上書きされることはありませんが、そのまま残ります:



 var dzerjinskiy = { mustache: true, baldHead: false } dzerjinskiy.__proto__ = lenin; console.log(dzerjinskiy.baldHead); // false -      
      
      





最後に、プロパティを持たない単純なダミーオブジェクトが必要になる場合があります。これは、値を書き込むためにのみ必要です。 その場合、プロパティをリストするときにhasOwnPropertyを確認する必要はありません。



 var zyuganov = Object.create(null); zyuganov.experience = 25; console.log(zyuganov.toString); // undefined
      
      





チェックすると、空のオブジェクトにはtoString()などの標準メソッドさえないことがわかります。 ところで、Object.createメソッド(プロトタイプ[、{}])が上記で使用されました-必要なプロトタイプ(nullを含む)とプロパティ(オプション)でオブジェクトを作成できるメソッドです。



F.prototypeおよびnew



コンストラクターを作成して、親が単一のオブジェクトであるインスタンスを簡単に作成できます。



 var marks = { marxism: true, engels: "friend", beard: 80 } function Marksist(name) { this._name = name; this.__proto__ = marks; } var kamenev = new Marksist("kamenev"); console.log(kamenev);
      
      





私たちはその仲間を見る カメネフにもひげがある



レフ・ボリソヴィッチ・カメネフ



しかし、革命はどうですか? プロトタイプに新しい値とメソッドを追加すると、子孫はこのメソッドを使用できます。



 marks.revolution = "future"; marks.deal = function() {return this.revolution}; // this    marks
      
      





新しい値が子孫に現れました:



 console.log(kamenev.revolution); // "future"
      
      





プロトタイプにプロパティまたはメソッドを追加すると、それらを再定義する必要なく子孫に表示されます。 プロトタイプ継承の力!



当然、子孫の値は変更できますが、これはプロトタイプの残りの子孫には影響しません。



 kamenev.revolution = "now"; console.log(kamenev.deal()); // "now"
      
      





ご覧のとおり、オブジェクトには最初はメソッドがありませんでしたが、プロトタイプにメソッドを追加した後、さらに子孫で値を変更して呼び出すことができます。



すべてのブラウザでのサポートについては、 古い、別の方法があります:



 function Marksist(name) { this._name = name; } Marksist.prototype = marks; var kamenev = new Marksist(""); console.log(kamenev); //         marks  __proto__
      
      





プロトタイプは、コンストラクター(JSで大文字で記述されている)でのみ意味を持ち、本質的に1つのアクションのみを実行します。つまり、コンストラクター関数を初期化するときに__proto__プロパティを参照する場所を示します。



最初のオブジェクトを作成した後、別のプロトタイプを表示して2番目のオブジェクトを作成する場合、最初のオブジェクトのプロトタイプは変更されません。



 Marksist.prototype = {}; var chicherin = new Marksist(""); console.log(chicherin.marxism); // undefined,   __proto__    Object console.log(kamenev.marxism); // - true,   __proto__   marks
      
      





空のプロトタイプを持つ新しいオブジェクトには、最初のオブジェクトのような継承されたプロパティがないことがわかります。 ただし、その場ですべてを再生できます。



 Marksist.prototype = marks; var zinovev = new Marksist(""); console.log(zinovev.marksizm); // true console.log(zinovev.deal()); // future
      
      





プロトタイプの変更は非常に高価な操作と見なされるため、プロトタイプをその場でプレイすることはお勧めできません。



プロトタイプでは、すべての子孫が使用するメソッドも指定できます。



 var marks = { marxism: true, engels: "friend", beard: 80, shout: function(){ alert("   " + this._name + "!") } } function Marksist(name) { this._name = name; } Marksist.prototype = marks; var dzerjinskiy = new Marksist(""); dzerjinskiy.shout(); //    !
      
      





ここで、これはプロトタイプの関数が呼び出されるオブジェクトであり、この場合はDzerzhinskyです。



フェリックス・エドムンドビッチ・ジェルジンスキー

正確には、Felix Edmundovichは私たちに警告しています:JavaScriptでは、このキーワードが現在指している場所について常に注意する必要があります


instanceof演算子を使用して、オブジェクトがコンストラクターの子孫であるかどうかを確認できます。



 var zemlyachka = function(tov) { var res = false; if (tov instanceof Marksist) res = true; return res; } console.log(zemlyachka(zinovev)); // true
      
      





以下は、通常のマルクス主義者と同じ特性と方法を実際に持っている日和見主義者です。



 var opportunist = { name: "", marxism: true, engels: "friend", beard: 80, shout: function(){ alert("   " + this.name + "!") } }; opportunist.shout();
      
      





私たちは彼に彼自身の同じユニークな特性を伝えて、彼のプロトタイプの残りを決定することさえできます、すなわち、前のオブジェクトとまったく同じ構造を保存します:



 var plehanov = { marxism: true, engels: "friend", beard: 80, shout: function(){ alert("   " + this._name + "!") } } function Socialist (name){ this._name = name; } Socialist.prototype = plehanov; var opportunist = new Socialist(""); console.log(opportunist); //        var zinovev = new Marksist,    
      
      





ただし、チェックでは失敗します。



 console.log(zemlyachka(opportunist)); // false
      
      





ロザリア・サモイロフナ・ゼムリャチカ

ロザリア・サモイロフナは対象物を見通しています。 オブジェクトをチェックする別のアプローチがあります-
ダックタイピング
カモのように見え、カモのように泳ぎ、カモのように鳴く場合、これはおそらくカモです。

サブジェクト、オブジェクトが必要に応じて動作する場合、その起源にもかかわらず、考慮されるべき共産主義者であると考えます




しかし、実績のある共産主義者は時々間違えられることがあります。 instanceof演算子はオブジェクトとコンストラクターのプロトタイプのみを比較するため、次のような衝突が発生する可能性があります。



 var troczkiy = new Marksist(""); Marksist.prototype = {}; console.log(troczkiy instanceof Marksist); // , 1940-   !
      
      





もちろん、JSのすべてがオブジェクトである(より正確には、プロトタイプチェーンでは、特別な空のオブジェクトを除くすべてのオブジェクトがObject.prototypeに来る)ことを忘れ ないでください。



 var john_reed = [10]; console.log(john_reed instanceof Array); // true console.log(john_reed instanceof Object); // true
      
      





コンストラクタープロパティ



コンストラクター関数(および実際にはすべての関数)には、 コンストラクターが記述されているprototypeプロパティがあります。これは、インスタンスプロトタイプを作成した関数への参照を返します。 次のように、さらに変換すると簡単に失われる可能性があります JSはこのリンクを保持する必要はありません。 Lev Davydovichの政治的ルーツを見つけることにしたと仮定します。



 var marks = { marxism: true, engels: "friend", beard: 80 } function Marksist(name) { this._name = name; } Marksist.prototype = marks; var troczkiy = new Marksist(""); var Congress = troczkiy.constructor; var retrospective = new Congress("My life"); console.log(retrospective); //  ,     ,     !
      
      





レフ・ダヴィドヴィッチ・トロツキー



明らかに、最初のオブジェクトと同じ新しいオブジェクトを作成する同じコンストラクター関数を呼び出すことはできません(ただし、 コンストラクターは理論的にはそれを指すはずです!)。 目的の結果を得るには、Marksistプロトタイプにコンストラクタープロパティを保存するだけです。



 var marks = { marxism: true, engels: "friend", beard: 80 } function Marksist(name) { this._name = name; } Marksist.prototype = marks; Marksist.prototype.constructor = Marksist; var troczkiy = new Marksist(""); var Congress = troczkiy.constructor; var retrospective = new Congress("My life"); console.log(retrospective); //     !
      
      





したがって、どのコンストラクターがインスタンスを作成したかを知る必要はなく、そこから新しいインスタンスを作成しています。 この情報はインスタンス自体に記録されます。



これらの変換を見ると、埋め込みJavaScriptプロトタイプのプロパティとメソッドをオーバーライドすることを考えるかもしれません。 革新的な言語で話す、グローバルなグローバルレベルに移動する



ソ連1924年の最初の紋章



たとえば、Object.prototypeメソッドの変更を止めることはできません。



 Object.prototype.toString = function(){ alert(" !") }
      
      





またはそのような過激派の例ではありません:



 Array.prototype.manifest= function(){ return "    -  "; }
      
      





クラスを変更するこのスタイル(ここではプロトタイプと言う方が正確です)は、モンキーパッチングと呼ばれます。



2つの危険があります。 まず、組み込みプロトタイプを展開または変更することにより、プロパティ継承チェーンの下位にあるすべてのオブジェクトの変更を利用できるようにします(Object.prototypeの場合、これらは通常すべてJSエンティティです)。 次に、このメソッドを使用して、新しいプロパティを古い名前で呼び出して上書きする危険があります。 同じプロトタイプチェーン内で、他のスクリプトを接続できる大規模プロジェクトの一部として、プロパティの名前を覚えている場合、グローバルオブジェクトなどの基本エンティティのプロトタイプの内容はそのまま残しておくのが最善です。そうしないと、結果は予測できません。



そして次に、プログラム実行中の再定義は、明白でない結果につながる可能性があります。



 var pss = { name: "  ", author: "", length: 20 } var Books = function(){}; Books.prototype = pss; var firstEdition = new Books; console.log(firstEdition.length); // 20 var extendLenin = function(year){ if (!year) var year = new Date().getFullYear(); if (year > 1925 && year < 1932) pss.length = 30; if (year > 1941 && year < 1966) pss.length = 35; if (year > 1966) pss.length = 55; } extendLenin(); var fourthEdition = new Books; console.log(fourthEdition.length); // ??
      
      





プロトタイプから継承したオブジェクトのプロパティの動作(いわゆる「クラスインスタンス」)は、想定したものとは異なる場合があります。



機能的継承



プロトタイプの継承パラダイムに加えて、JavaScriptも機能を使用します。 革命の英雄の1人を例として、それがどのように実現されるかを見てみましょう。



次のコンストラクタを作成します。



a)パラメータを受け入れる

b)コンストラクターとその派生物の外部から使用されることになっているパブリックメソッドを所有します。

c)コンストラクターとその派生物の内部でのみ使用されることになっているプラ​​イベートメソッドがあります。



私たちの前に典型的な共産主義者がいます:



 var Kommunist = function(principles) { if (!principles) principles = {}; this._start = 1902; if (principles && principles.start) this._start = principles.start; //  ,     this.response = function() { alert("  !") } //  ,         this._experience = function() { return (this._start); } this.principles = (Object.keys(principles).length > 0 ? principles : {fidelity: 100}); this._getPrinciples = function() { return this.principles } }
      
      





プライベートメソッドは、アンダースコアで始まる慣習です。



したがって、引数を受け入れて処理する準備が整った一連のメソッドを持つコンストラクターがあります。 クラスのインスタンスを作成します。



クリメント・エフレモビッチ・ボロシロフ



 function Voroshilov(principles) { Kommunist.apply(this, arguments); //    var parentExperience = this._experience; this._experience = function() { return ("  ()  " + parentExperience.apply(this, arguments)); } //  ,     //  this.getPrinciples = function() { var p = this._getPrinciples(); var char = { fidelity: p.fidelity, persistence: p.persistence || "!" } console.log(": " + char.fidelity + ", : " + char.persistence) } this.getExperience = function() { console.log(this._experience()); alert(" -!"); } //  this.setExperience = function() { this._experience = function() { return ("  ()   "); } } } var ke = {fidelity: 101, persistence: 100, start: 1903} var voroshilov = new Voroshilov(ke);
      
      





注:コンストラクターは、 これに関連し呼び出され、そのすべてのメソッドを呼び出します。 引数配列には、呼び出し中に指定されたすべての引数( keオブジェクト)が含まれます。



さらに、ゲッターとセッターの動作方法、およびその他のパブリックメソッドを確認できます。



 voroshilov.getExperience(); //   voroshilov.setExperience(); //       voroshilov.getExperience(); //    voroshilov.getPrinciples(); //        
      
      





変更のために、パラメーターなしでコンストラクターを呼び出すことができます。



クラスエンティティ



最後に、ES6(ES2015)のリリースにより、JavaScriptでクラスステートメントを直接使用できるようになりました。 実際、プロトタイプ継承デバイスでは何も変わっていませんが、JSは構文シュガーをサポートしています。これは、他の言語から来た多くのプログラマーにとってより馴染みのあるものです。



 class Marksist { constructor(name) { this._name = name } enemy() { return "capitalism" } static revolution(){ return " " } }
      
      





JSクラスには3種類のメソッドがあります。



-コンストラクター(クラスインスタンスが初期化されるときに実行されます);

-静的(静的メソッドは、クラスが呼び出されたときに使用可能ですが、インスタンスでは使用できません);

-従来の方法。



ここで、非常に重要な日付を定数(ES6ではこのタイプの変数も許可します)で記憶させてから、マルクス主義の相続人であるメンシェビキを定義します。



 const cleavage = 1903; class Menshevik extends Marksist { constructor(name) { super(); this._name = name; } static revolution() { var r = super.revolution(); return r + ",  "; } ["che" + cleavage]() { alert(" !") } hurt() { alert("  ") } } let chkheidze = new Menshevik("");
      
      





画像



ここには2つの革新があります。



-最初のケースではsuper()は基本クラスのコンストラクタを初期化し、2番目のケースでは子孫に新しい動作を追加するメソッドを呼び出します。

-計算されたメソッド名( ["che" + cleavage] )、メソッド名をすぐに知る必要はありません。



静的メソッドは、クラスが呼び出されたときに使用できますが、インスタンスが呼び出されたときは使用できません。



 console.log(Menshevik.revolution()); //  console.log(chkheidze.revolution()); // is not a function,    
      
      





次のコードの結果はすでに明らかです。



 chkheidze.hurt(); //    console.log(chkheidze.enemy()); //     chkheidze.che1903(); //     
      
      





JavaScriptのクラスを介した継承の最も基本的な機能を上に示しました。 革命的な忍耐により、意識のあるプロレタリアートは、ネットワーク上でES6のイノベーションの問題をより完全にカバーする多くの記事を見つけます。



All Articles