
私の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」の
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つの非常に強力なフレームワーク、 AtomJSとLibCanvasがあり、アクセサーの使用が完全に成果を上げました。 あなたが9番目のバージョン以下のロバを放棄する余裕があるなら-それは価値がある、たくさんの喜びを得る。
このトピックで説明されているソリューションは、わずかに拡張されており、当初はAtomJS-Accessorsプラグインとして実装されていました 。