ホームページ  >  記事  >  ウェブフロントエンド  >  John Resig の単純な JavaScript 継承コードの解析_JavaScript のヒント

John Resig の単純な JavaScript 継承コードの解析_JavaScript のヒント

WBOY
WBOYオリジナル
2016-05-16 17:47:36997ブラウズ

著者が自身の学習と使用のために翻訳に独自の理解を加えますので、英語が得意な学生は以下を読んでください。記事に翻訳ミスがある場合は、メッセージを残して修正してください。 (:
====== ===============エネイン翻訳===================== ====

John Resig は、base2 と PrototypeJS に触発されて、JavaScript の「継承」に関する記事を書きました。彼は、非常に巧妙なテクニックを使用して、スーパー メソッドを実装しました。
詳細については、「JavaScript Ninja の秘密」でも紹介されているので、原文を参照してください。この方法では、グローバル変数を作成する代わりに、サブクラスを追加します。 .
オリジナル スクリプト - John Resig シンプルな JavaScript の継承
以下は赦免コードです。わかりやすくするためにいくつかのコメントを削除しました。

コードをコピー コードは次のとおりです。

(function(){
varInitializing = false, fnTest = /xyz/.test( function(){xyz;} ) ? /b_superb/ : /.*/;
this.Class = function(){};
Class.extend = function(prop) {
var _super = this .prototype;
初期化中= true;
var プロトタイプ = new this();
初期化 = false;
for (prop の var 名) {
prototype[name] = typeof prop[name] == "関数" &&
typeof _super[name] == "関数" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
this._super = _super[名前];
var ret = fn.apply(this, argument);
this._super = tmp;
return ret;
} ;
})(name, prop[name]) :
prop[name];
}
function Class() {
if ( !initializing && this.init )
this.init.apply(this, argument);
}
Class.prototype = プロトタイプ;
Class.constructor = Class;
Class.extend = argument.callee ;
return Class;
};
})();

単純な継承スクリプトの内訳
それがどのように実装され、どのようなテクノロジーが使用されているかを分析してみましょう。

コードをコピー コードは次のとおりです。

(function(){ // .. . }) ();

まず、コードのスコープを作成するための自己実行匿名関数を作成します。

コードをコピー コードは次のとおりです:

varInitializing = false

この初期化変数の意味は非常に単純です。インスタンスの作成時に初期化を true/false に設定するか、現在のプロトタイプを指すオブジェクトを返すだけで、クラス関数 (後述) が呼び出されたときにチェックするブール値です。 「継承の目的」を達成するための連鎖。

インスタンスを作成(初期化 == false)すると、Classにはinitメソッドがあるので、自動的にinitが実行されます。 または、単にプロトタイプに割り当てた場合 (== true の初期化)、何も起こらず、init メソッドは実行されません。これは、コンストラクターが呼び出されるたびに init メソッドを実行する必要がないようにするために行われます (varprototype = new this());.

コードをコピーします コードは次のとおりです。

fnTest = /xyz/.test(function (){xyz;}) /b_superb/ : /.*/;

この fnTest の目的は、クラスメソッド内で「_super()」呼び出しが使用されているかどうかを検証することです。この技術は「関数逆コンパイル (関数逆コンパイル)」または「関数シリアル化 (関数シリアル化)」と呼ばれます。関数のシリアル化 これは、関数が文字列に変換されるときに発生します。現在、多くのブラウザーが toString メソッドをサポートしています。

関数のシリアル化をテストするために、fnTest は匿名関数 funciton(){xyz;} を使用して内容を "xyz" に設定し、文字列に変換した後、正規表現を使用して "xyz" を検索します。 true (ブラウザが関数のシリアル化をサポートしている場合) 関数はそれを文字列に変換するため、「xyz」も文字列の一部になります。この場合、fnTest は「/b_superb/」を返し、他の場合は「/.*/」を返します。 " ブラウザが関数のシリアル化をサポートしていない場合は、常に true を返します。 (これは元のコードの fnTest.test を指します) fnTest の正規化と関数のシリアル化テクノロジを使用すると、メソッド内で「_super」が使用されているかどうかを簡単に判断できます。使用されている場合は、いくつかの特別なメソッドが実行されます。この特別な方法は、親クラスと子クラスに同じメソッドが同時に出現することを避けるためです。

ブラウザが関数のシリアル化をサポートしていない場合は、常に true を返すため、追加の操作は常に _super で実行され、これらの新しいメソッドは _super で使用されなくなりますが、これにより多少のパフォーマンスが消費されます。すべてのブラウザで正常に実行されることが保証されています。

コードをコピーします コードは次のとおりです:

this .Class = function(){};

空のコンストラクターを作成し、それをグローバル変数に置きます。これは、定義されたコンテンツを持たない、またはウィンドウ オブジェクトを参照します。クラス変数はグローバル オブジェクトです。

コードをコピー コードは次のとおりです:

クラス。 extend = function( prop) { // ...}

extends メソッドと単純な prop (オブジェクト) パラメーターを追加すると、新しいコンストラクターのプロトタイプと親オブジェクトのプロトタイプが返されます。

コードをコピーします コードは次のとおりです:
var _super = this.prototype;

現在のオブジェクトのプロトタイプ オブジェクトを _super に格納します。 this.prototype は、必要なときに親メソッドにアクセスできます。 super は予約語なので、この変数は何ですか。まだ適用されていません。

コードをコピー コードは次のとおりです:
initializing = true;varprototype = new this (); 初期化中 = false;

インスタンス クラスのオブジェクトはプロトタイプ変数に格納されますが、init メソッドは実行されません。初期化が以前に true に設定されていたため、プロトタイプ変数が割り当てられた後、init メソッドは起動されません。次のステップでは初期化が false に戻されます (例: 実際のインスタンスを作成する場合)


コードをコピーします コードは次のとおりです。
for (var name in prop) { // ...}

for ループを使用して、prop の属性とメソッドを反復処理します。属性は、_super の特別な処理に加えて、プロトタイプ属性に割り当てられます。


コードをコピーします コードは次のとおりです:
prototype[name] = typeof prop[name] == " function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() { // _super の特別な処理 }; })(name, prop[名前] ) : prop[名前];

prop 内の各オブジェクトを反復処理すると、 (typeof prop[name] == "function") (typeof _super[name] == "function") (fnTest.test(prop[name]) = = true)
新しいメソッドと親クラスにバインドされた元のメソッドを処理するための新しいメソッドを追加します。
上記のコードは少しわかりにくいかもしれません。もっと明確な方法を使用してみましょう。

コードをコピーします コードは次のとおりです:

if (typeof prop[name] == "function " && typeof _super[name] == "function" && fnTest.test(prop[name])) {prototype[name] = (function(name, fn){ return function() { // _super の特別な処理 }; }) (name, prop[name]);} else { // プロパティをコピーするだけですprototype[name] = prop[name];}

スーパーで名前 prop[name] を処理するときに使用される、別の自己実行匿名関数。このようなクロージャはありません (たとえば、この関数を返すときは、常に最後に戻ります)。ループの a)
すべてを繰り返して、新しい関数を返します。この関数はネイティブ メソッド (スーパー経由) と新しいメソッドを処理します。

コードをコピーします コードは次のとおりです。

// supervar の特別な処理 tmp = this._super;this._super = _super[name];var ret = fn.apply(this , argument);this._super = tmp;return ret;

super の特殊な処理では、まず既存の _super 属性とクラスのいくつかのパラメーターを一時的な tmp に保存する必要があります。これは、_super 内の既存のメソッドが上書きされるのを防ぐためです。
その後、割り当てます。
次に、_super[name] メソッドを現在のオブジェクトの this._super に割り当てます。これにより、apply を通じて fn が実行されたときに、この ._Super () がポイントされるようになります。
親メソッドの this も現在のオブジェクトにアクセスできます。
最後に、戻り値をオブジェクトに格納します。
以下に簡単な例を示します。シンプルな Foo を作成し、継承オブジェクト Bar:

コードをコピー コードは次のようになります:

var Foo = Class.extend({ qux: function() { return "Foo.qux"; }});var Bar = Foo.extend({ qux: function() { return "Bar.qux, " this._super(); }});

Foo.extends が実行されると、qux メソッドに this._super が存在するため、Bar プロトタイプの qux は実際には次のようになります:

コードをコピーします コードは次のとおりです。

Bar.prototype.qux = function () { var tmp = this._super this._super = Foo.prototype; .qux; var ret = (function() { return "Bar.qux, " this._super(); }).apply(this, 引数); }

スクリプトのこのステップが完了すると、コンストラクターが呼び出されます。

コードをコピーします コードは次のとおりです。

function Class() { if ( !initializing && this.init ) this.init.apply(this, argument);}

このコードは Class を呼び出して、以前に作成した this.Class とは異なる、ローカル Class.extend としての新しいコンストラクターを作成します。このコンストラクターは、Class.extend (前の Foo.extends など) への呼び出しを返します。 new このコンストラクターは、Foo() がインスタンス化された後に実行されます。
コンストラクターは、init() メソッド (存在する場合) を自動的に実行します。前述したように、この初期化変数は、init が実行されるかどうかを制御します。

コードをコピー コードは次のとおりです:
 
Class.prototype = prototype;

The last prototype returns a mixed prototype object of the parent class from the constructor method of the parent class. (e.g var prototype = new this()), this result is passed through the for loop in the extend function.

Class.constructor = Class;
Because we have rewritten the entire prototype object, store this native constructor in this type, so that it can maintain the default shape in the constructor of an instance.

Copy code The code is as follows:
 
Class.extend = arguments.callee;

will assign itself, through arguments.callee, which in this case means "self". In fact, we can avoid using arguments.callee here, if we modify my native method (e.g Class.extend = function extend(prop) ) Then we can copy the code

by using . The code is as follows:
;.return Class;

The instance will then return, a prototype object, a constructor property, an extend method and a self-executable method init.!!!

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