JavaScriptクロスブラウザーアクセサー



私のAtomJSフレームワークでは、アクセッター(ゲッターとセッター)を積極的に使用しています。



Foo = atom.Class({ get bar () { return this._bar; }, set bar (bar) { this._bar = bar; } });
      
      







理論についてはすでに説明しましたが、トピックでは、すべての最新のブラウザーでそれらを動作させる方法、つまりInternet Explorer 9が__defineSetter__



および同様のメソッドについて何も知らないという事実で状況を解決する方法について説明します。







取得 / 設定



幸いなことに、この点で、すべてのブラウザーはほぼ同じです。 IE9 +、Opera10 +、Fx3.5 +、Chrome-すべてこのエントリをサポートし、動作と構文は同じです。 唯一のものは、Internet Explorer 9がポップアップする場合、「 おそらく ':'



'が ':'



"-ブラウザが「Interner Explorer 7」の「Bagodrom」モードに切り替わったかどうかを確認することです。



defineProperty



ここで複雑なことはありません



非標準の場合 __defineSetter__



__defineSetter__



および __defineGetter__



__defineGetter__



は、EcmaScript5- Object.definePropertyの代替です 。 デフォルトでは、オブジェクトのconfigurable



およびenumerable



プロパティはtrueとして宣言されているため、標準の代替を簡単に記述できます。

 instance.__defineSetter__(propertyName, setterFn); instance.__defineGetter__(propertyName, getterFn); // => Object.defineProperty(instance, propertyName, { set: setterFn, get: getterFn, enumerable : true, configurable: true });
      
      







getOwnPropertyDescriptor



非標準の場合 __lookupSetter__



__lookupSetter__



および __lookupGetter__



__lookupGetter__



には、EcmaScript5- Object.getOwnPropertyDescriptorの代替もあります。



すべてが少し喜びではありませんが、重要ではありません。 秘密は、 __lookup*__



はプロトタイプチェーン全体に沿ってアクセサを検索するのに対し、 getOwnPropertyDescriptor



は個人のプロパティでのみ検索することです。

指定されたオブジェクトの独自のプロパティ(つまり、オブジェクトのプロトタイプチェーンに沿って存在するのではなく、オブジェクトに直接存在するプロパティ)のプロパティ記述子を返します。




つまり、 次の状況があります

 var MyClass = function () {}; MyClass.prototype = { get foo() { return 42; } }; var instance = new MyClass(); console.log(instance.__lookupGetter__('foo')); // function foo() { return 42; } console.log(Object.getOwnPropertyDescriptor(instance, 'foo')); // undefined
      
      







ゲッターには実際には次のものがあります:

 console.log(instance.foo); // 42
      
      







1.非標準プロパティのより正確かつ論理的な動作

2.私のフレームワークのアイデアにより適しています。

3.すべてのブラウザが同じように動作することが重要です。 それほど重要ではない



したがって、 null



、または特定のプロパティまたはアクセサに到達するまで、 Object.getPrototypeOfメソッドを使用してプロトタイプチェーン全体を再帰的に処理します。



 function getPropertyDescriptor (from, key) { var descriptor = Object.getOwnPropertyDescriptor(from, key); if (!descriptor) { //     -       var proto = Object.getPrototypeOf(from); if (proto) return getPropertyDescriptor(proto, key); //   , ,       (   ) } else if ( descriptor.set || descriptor.get ) { return { set: descriptor.set, get: descriptor.get }; } //    ,       return null; };
      
      







すべてをライブラリに入れる



これで、取得した知識を適用して、ブラウザ間のアクセサー用のライブラリを作成できます。

私の個人的な観察によると、非標準の方法は少し速く動作し、必要なハックも少ないので、デフォルトとして採用しましょう。

また、私は名前のlookup



define



好きです-それらは簡潔で理解しやすいので、私たちはそれらを使用する理由です。

2つの異なる関数を作成するだけで、毎回余分なチェックを行わないため、各メソッドのルックアップ関数の内容は根本的に異なります。

 (function (Object) { var standard = !!Object.getOwnPropertyDescriptor, nonStandard = !!{}.__defineGetter__; if (!standard && !nonStandard) throw new Error('Accessors are not supported'); var lookup = nonStandard ? function (from, key) { var g = from.__lookupGetter__(key), s = from.__lookupSetter__(key); return ( g || s ) ? { get: g, set: s } : null; } : function (from, key) { var descriptor = Object.getOwnPropertyDescriptor(from, key); if (!descriptor) { var proto = Object.getPrototypeOf(from); if (proto) return accessors.lookup(proto, key); } else if ( descriptor.set || descriptor.get ) { return { set: descriptor.set, get: descriptor.get }; } return null; }; var define = nonStandard ? function (object, prop, descriptor) { if (descriptor) { if (descriptor.get) object.__defineGetter__(prop, descriptor.get); if (descriptor.set) object.__defineSetter__(prop, descriptor.set); } return object; } : function (object, prop, descriptor) { if (descriptor) { var desc = { get: descriptor.get, set: descriptor.set, configurable: true, enumerable: true }; Object.defineProperty(object, prop, desc); } return object; }; this.accessors = { lookup: lookup, define: define }; })(Object);
      
      







これで、オブジェクトでアクセサー宣言できます。

 MyClass = function (param) { var property = param; accessors.define(this, 'property', { set: function (value) { property = value; }, get: function () { return property; } }); }; var instance = new MyClass(42); console.log(instance.property); // 42 console.log(accessors.lookup(instance, 'property')); // getter+setter
      
      







継承



inherit



メソッドを追加して、ライブラリを少し拡張してみましょう。 これは、fromオブジェクトからkey



という名前のプロパティアクセサーを受け取り、toオブジェクトに追加します。 成功した場合はtrue



、そうでない場合はfalse



返しtrue





  this.accessors = { lookup: lookup, define: define, inherit: function (from, to, key) { var a = accessors.lookup(from, key); if ( a ) { accessors.define(to, key, a); return true; } return false; } };
      
      







このメソッドは、 jQuery.extend



をサポートするMooTools



jQuery.extend



またはObject.merge



類似物を作成するのに役立ちますが、すべての通常のフレームワークはそれらについて何も知りません。



 var object = jQuery.extend({}, { get foo(){ return null; } }); console.log( object.__lookupGetter__('foo') ); // undefined console.log( object.foo ); // null
      
      







独自のバージョンを作成します(注意、このオプションは教育目的で作成されたものであり、実際のアプリケーションでは使用しないでください)



 function extend(from, to) { for (var i in to) { //    if (!accessors.inherit(from, to, i)) { //     -    from[i] = to[i]; } } return from; }; var object = extend({}, { get foo(){ return null; } }); console.log( object.__lookupGetter__('foo') ); // getter console.log( object.foo ); // null
      
      







おわりに



とても快適です。 高度に特化された2つの非常に強力なフレームワーク、 AtomJSLibCanvasがあり、アクセサーの使用が完全に成果上げました。 あなたが9番目のバージョン以下のロバを放棄する余裕があるなら-それは価値がある、たくさんの喜びを得る。



このトピックで説明されているソリューションは、わずかに拡張されており当初はAtomJS-Accessorsプラグインとして実装されていました



All Articles