8

オブジェクトを取得して、そこからいくつかのメソッドを削除したいと思います。

つまり、ゲッター/セッターを含むオブジェクトを内部に持っており、外部ユーザーにアクセスを許可したいと考えています。彼らがセッター関数にアクセスできるようにしたくありません。

メソッドを削除して元のオブジェクト参照を変更したくはありませんが、同じオブジェクトを指すがメソッドが少ない新しいオブジェクト参照を作成します。

  • どうすればこれを行うことができますか?
  • これはデザインパターンですか?
  • この種の問題に対するよく知られた解決策はありますか?

私はこの機能の実装を持っています

var readOnly = function(obj, publicData) {
    // create a new object so that obj isn't effected
    var object = new obj.constructor;
    // remove all its public keys
    _.each(object, function(val, key) {
        delete object[key];    
    });
    // bind all references to obj
    _.bindAll(obj);
    // for each public method give access to it
    _.each(publicData, function(val) {
        object[val] = obj[val];    
    });
    return object;
};

実際の例を 参照してください。_.each _.bindAll

意図されたすべての目的のために、返されるオブジェクトは元のオブジェクトと同じでなければなりませんが、一部のメソッドはもう存在しません。内部this参照は、どの関数でも壊れてはなりません。プロトタイプ チェーンは壊れてはなりません。

  • このような関数の直感的な名前は何でしょうか?
  • 現在の実装に注意すべき落とし穴はありますか?
4

9 に答える 9

5

すべきことは、保持したいメソッドのみを持つFacade パターンとDelegation パターンを使用して、それらのメソッドを元のオブジェクトのインスタンスに委任することです。Javascript で利用できる豊富なリフレクションを使用すると、これらのファサードをプログラムでかなり簡単に生成できるはずです。

于 2011-02-23T17:38:06.217 に答える
2

実装に関する少なくとも1つの問題は、への依存obj.constructorです。このプロパティは読み取り専用ではないconstructorことで有名であり、簡単に混乱する可能性があります。次のパターンを検討してください。これは、Javascriptでクラスを定義するための非常に一般的な方法です。

function Foo() {};
Foo.prototype = {
    myProperty: 1,
    myFunction: function() {
        return 2;
    }
};
// make an instance
var foo = new Foo();
foo instanceof Foo; // true
foo.constructor == Foo;  // false! The constructor is now Object
(new foo.constructor()) instanceof Foo; // false

objこれを行う方法は、インスタンスをプロトタイプとして新しいクラスを作成することだと思います。次に、新しいクラスのオブジェクトに空のキーを追加することで、古い関数へのアクセスをブロックできます。

function getRestricted(obj, publicProperties) {
    function RestrictedObject() {};
    RestrictedObject.prototype = obj;
    var ro = new RestrictedObject();
    // add undefined keys to block access
    for (var key in obj) {
        // note: use _.indexOf instead -- this was just easier to test
        if (publicProperties.indexOf(key) < 0) {
            ro[key] = null;
        } else {
            // again, use _.isFunction if you prefer
            if (typeof obj[key]=='function') {
                (function(key) {
                    // wrap functions to use original scope
                    ro[key] = function() {
                        // basically the same as _.bind
                        return obj[key].apply(obj, arguments);
                    }
                })(key);
            }
        }
    }
    return ro;
}

function Foo() {
    var a=0;
    this.getA = function() {
        this.setA(a+1);
        return a;
    };
    this.setA = function(newa) {
        a = newa;
    }
};

// make an instance
var foo = new Foo();
foo.setA(1);
foo.getA(); // 2

// make a restricted instance
var restrictedFoo = getRestricted(foo, ['getA']);
restrictedFoo.getA(); // 3
restrictedFoo instanceof Foo; // true
try {
    restrictedFoo.setA(2); // TypeError: Property 'setA' is not a function
} catch(e) {
    "not a function";
}
// one bump here:
"setA" in restrictedFoo; // true - just set to undefined

// foo is unaffected
foo.setA(4);
foo.getA(); // 5

(これは、ここで説明するCrockfordのパワーコンストラクター関数に部分的に基づいています。)


編集:私はあなたのコメントに対処するために上記のコードを更新しました。これで、実装に多少似ていますが、より単純になり、constructor問題が回避されます。ご覧のとおり、パブリック関数での参照は古いオブジェクトを参照するようになりました。

于 2011-02-25T05:31:36.453 に答える
1

動作が必要な場合instanceofは、@nrabinowitzのソリューションのように継承を使用する必要があります。そのソリューションでは、不要なメソッドはキーをnullに設定して非表示にし、それらのキーはユーザーがアクセスできるため、ユーザーがリセットする可能性があります。これらのキーを中間オブジェクトに非表示にすることでこれを防ぐことができます。また、中間クラスからインスタンス化されるため、継承が壊れることはありません。

function restrict(original, whitelist) {
    /* create intermediate class and instantiate */
    var intermediateClass = function() {};
    intermediateClass.prototype = original;
    var intermediateObject = new intermediateClass();

    /* create restricted class and fix constructor reference after prototype replacement */
    var restrictedClass = function() {};
    restrictedClass.prototype = intermediateObject;
    restrictedClass.prototype.constructor = original.constructor;
    if (restrictedClass.prototype.constructor == Object.prototype.constructor) {
        restrictedClass.prototype.constructor = restrictedClass;
    }

    for (var key in original) {
        var found = false;
        for (var i = 0; i < whitelist.length; i++) {
            if (key == whitelist[i]) {
                if (original[key] instanceof Function) {
                    /* bind intermediate method to original method */
                    (function(key) {
                        intermediateObject[key] = function() {
                            return original[key].apply(original, arguments);
                        }
                    })(key);
                }
                found = true;
                break;
            }
        }
        if (!found) {
            /* black out key not in the whitelist */
            intermediateObject[key] = undefined;
        }
    }

    return new restrictedClass();
}

次の例では、メンバー値を実装する2つの方法を表していますij1つは閉鎖中のプライベートメンバーで、もう1つはクラスのパブリックメンバーです。

var originalClass = function() {
    var i = 0;
    this.j = 0;

    this.getI = function() {
        return i;
    };
    this.setI = function(val) {
        i = val;
    };
}
originalClass.prototype.increaseI = function() {
    this.setI(this.getI() + 1);
};
originalClass.prototype.decreaseI = function() {
    this.setI(this.getI() - 1);
};
originalClass.prototype.getJ = function() {
    return this.j;
};
originalClass.prototype.setJ = function(val) {
    this.j = val;
};
originalClass.prototype.increaseJ = function() {
    this.setJ(this.getJ() + 1);
};
originalClass.prototype.decreaseJ = function() {
    this.setJ(this.getJ() - 1);
};

var originalObject = new originalClass();
var restrictedObject = restrict(originalObject, ["getI", "increaseI", "getJ", "increaseJ"]);

restrictedObject.increaseI();
restrictedObject.increaseJ();
console.log(originalObject.getI()); // 1
console.log(originalObject.getJ()); // 1
console.log(restrictedObject instanceof originalClass); // true

ご覧のとおり、すべてのセッターと減少メソッドは制限付きオブジェクトに隠されています。iユーザーは、getterを使用するか、およびの値を増やすことしかできませんj

于 2011-02-26T06:45:52.397 に答える
0

オブジェクト内の特定のメソッドを public として公開し、残りを非公開にすることだけが必要な場合は、次のようにすることができます (以下の例では、privateMethod2 は、ユーザーに返される新しい「読み取り専用」オブジェクトで決して公開されません)。

function MyObject() {

  // Private member
  var name = "Bob"; 

  // Private methods
  function setName(n) { name = n; }
  function privateMethod2() { ... }
  function privateMethod3() { ... }

  // Expose certain methods in a new object
  this.readOnly() { 
    return {
      publicMethod1: setName,
      publicMethod2: privateMethod3
    }; 
  }

}
于 2011-02-20T17:13:49.077 に答える
0

@nrabinowitz のgetRestricted(リンク) 関数は、あなたが探していた答えのほとんどだと思いました。

私は、JavaScript に GOF パターンを適用することはあまり好きではありません (それ自体に議論があります)。しかし、これは特定のオブジェクトの実行時の動作を変更しているため、私にはデコレータのような匂いがしますが、逆に、デデコレータのように思えます :)

于 2011-02-25T07:28:40.057 に答える
0

あなたは非常に複雑なアプローチを選択しています。最初に詳細を入力してください:

  • 元のオブジェクト プロトタイプを拡張する必要は本当にありますか (つまり、実行時にオブジェクト プロトタイプを何回変更するか)。
  • 何種類のオブジェクトを作成しますか? 異なるということは、つまり、同じオブジェクト タイプから作成されますが、パブリック メソッドが異なるということです。
  • いくつのオブジェクト タイプをロックする予定ですか?
  • どのくらいの頻度でコードを更新する予定ですか?
  • 何らかの種類のミニファイヤ/コンパイラを使用していますか?

コメント 1 への応答:

  • すべてのオブジェクトをロックしている場合、パフォーマンスの問題 (特に IE の場合) に対処するのは時間の問題です。
  • これまでに、ロックしたいオブジェクト タイプが 2 ~ 3 あります。これらの 2 ~ 3 個のオブジェクトをDelegation パターンで準備する方が簡単ではないでしょうか? 柔軟性には代償を払わなければならないことを忘れないでください。
  • 高度なモードでClosure コンパイラを使用してみてください。このツールを活用すると、オブジェクト プロパティを難読化し、パブリック インターフェイスを残す方法を学習できます。さらに、コードを変更して再コンパイルするたびに、新しい隠し変数/関数名が作成されるため、setA または getA 関数であると推測してみてください。
  • 最後になりましたが、重要なことです。パブリック インターフェイスをできるだけすっきりと小さく保つようにしてください。これは、すべてのコードを匿名関数でまとめ、できるだけ少ないオブジェクト/メソッド/プロパティを開示することを意味します。

Closure コンパイラの難読化の仕組みについてもう少し詳しく説明します。

  1. コンパイルは、すべてのオブジェクト プロパティの名前を aa、ab、ac などに変更します。
  2. 公開メソッド/プロパティを開示します: a.prototype.getA = a.prototype.aa; (内部的には a.prototype.aa メソッドを使用します。これは、パブリック メソッドを任意の値に置き換えることができることを意味します。これはコードには影響しません);
  3. そして最後に、オブジェクトを公開します: window['foo'] = new a;

次の方法は、Google (GMaps、GMail など) で使用されます。

于 2011-02-24T06:24:47.000 に答える
0

あなたが探しているのは、JavaScript ではモジュール パターンと呼ばれます。Gobhi の記述にかなり近いですが、一般的には自己実行機能です。

詳細はこちら:

http://yuiblog.com/blog/2007/06/12/module-pattern/

と :

http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth

于 2011-02-23T17:28:09.413 に答える
-1

多分:

var yourfunction = function() {
   var that = {};

   var constructor = function() {
   };

   //private methods
   var privateMethod = function() {
   };

   constructor();

   //public methods   
   that.publicMethod = function() {
   };

   return that;
}
于 2011-02-23T10:20:29.297 に答える
-1

あなたの状況に非常によく一致するパターンはProxyだと思います。

更新: コメントが示すように、プロキシは実際のオブジェクトと同じインターフェイスをサポートする必要があるため、質問に記載されている問題とはうまく一致しません。Proxy に関するウィキペディアの記事の最後に、 Proxy、Adapter、Facadeのパターンを比較した興味深い記事へのリンクを見つけました。

于 2011-02-23T14:41:01.863 に答える