12

JavaScript のプライベート関数をハッキングすることに意味があるかどうかについて、仲間の開発者と話し合っていました。

代替手段は次のとおりです。

  1. すべての関数を含むコンストラクターとプロトタイプ、非 API メソッド (プライベート) はアンダースコアで名前が付けられる_function_nameため、開発者は何を呼び出すことができ、何を呼び出すことができないかがわかります。
  2. API 関数のコンストラクターとプロトタイプ、およびこの名前空間以外の残りの名前空間からそれらを隠すプライベート名前空間内のプライベート関数としてのフリー関数。

var private_var= function(){}オブジェクトがインスタンス化されるたびにこれらすべての関数の作成がトリガーされ、各オブジェクトが独自のセットを持つため、フォームのコンストラクターでプライベート関数を作成するなどの他のアプローチは考慮しません。

その理由は次のとおりです。

1

  • Javascript はプライベート関数自体をサポートしていません。実際、プライベート/保護/パブリックの可視性の概念がないため、これは基本的にハックです。
  • メソッド名にアンダースコアを使用すると、特定のクラス/プロトタイプの境界が明確に示されます。それを「強制」する必要はありません。実際、Python などの言語にはプライベート メソッドがなく、Python ユーザーはそれを別に気にすることはありませんでした。アンダースコアの使用から
  • プライベートが強制されたとしても、パブリック メソッドを動的に置き換えることができる言語では、どのような意味があるでしょうか?
  • 可読性に大きく影響します。プライベート関数は、スコープのために別のブレースのセットに分けられます。使用できないか、またはthisで呼び出す必要があります。function.call(object)function.apply(object)

2

  • クラス/プロトタイプのユーザーの手から離れたプライベート メソッドをカプセル化することで、明確な制限をもたらします。
  • これは多かれ少なかれ業界標準であり、多くの JavaScript 開発者がそのように使用しています。

多くの開発者が使用しているため、パフォーマンスなどの別の理由でそのように使用される可能性があると思われます。

Javascript に関する私たちの知識は非常に限られているため、これを stackoverflow に投稿して、どのアプローチが優れているか、またその理由を確認することにしました。

4

10 に答える 10

6

JavaScript のプライベート関数をどのようにハッキングしますか?また、その目的は何ですか?

クラス、プライベート関数などを作成するさまざまな方法は、動機に依存すると思います。

アンダースコアの規則は、単体テストを試みて、外から見ると巨大なメソッドのように見えるものを実際に分割したい場合に役立ちます。

それ以外の場合は、物事を真にプライベートにするように努めるべきだと思います。他の人が操作できるように、きれいできれいな API を公開しようとしている場合、背後にあるものを見る必要はありません。なぜそれを公開するのですか?これは、プライベートとパブリックに関する一般的な会話につながります:オブジェクト指向でなぜ「プライベート」メソッドなのか?

メソッドのプライベート化にはいくつかの選択肢があり、これらの選択肢のいくつかはパフォーマンスに影響します。

下線規則:

function Pizza() {
   this._pepperoni = function () {};
}

また

function Pizza() {
}

Pizza.prototype._pepperoni = function () {};

スコーピング

function Pizza() {
    function pepperoni() {};
}

名前空間/モジュール

var pizza = pizza || {};
(function() {
    function pepperoni() {};
    function create() {
        pepperoni();
    }
    window.pizza.create = create; // or module.export = pizza or both
}());

モジュールパターン

(function(){
    function pepperoni() {};
    function Pizza() {
        pepperoni();
    }

    window.Pizza = Pizza;
}());

関数の再作成と一度の定義について。まず、内部のプライベート メンバーを使用し、「this」を引き続き使用する場合は、という名前の新しい変数を作成してself割り当てthisます。

function Pizza() {
    var self = this;
    function pep() {
       self.x = 1;
    }
}

次に、事前に関数の再定義と書き込みのパフォーマンスの違いをテストしようとしました: http://jsperf.com/private-methods時間。

どのアプローチもお勧めしません。それらはすべて有効であり、さまざまな場面で役立ちます。セマンティクスが重要な場合もあれば、パフォーマンスが重要な場合もあれば、単体テストなどの目的を達成するための場合もあります。

于 2012-10-18T05:09:09.627 に答える
4
  • それはハックではありません:私はこの考えを克服することが基本だと思います。オブジェクトの詳細を非表示にするという目標を達成するために、言語のコアとなる正当な機能であるクロージャを使用している場合、ハッキングは発生しません。

    クラスベース言語でprivateを使用する場合は、オブジェクトの実装の詳細を非表示にするという、より高いレベルの概念を実装しているだけであることに注意することが重要です。そのことを念頭に置いて、JavaScriptでプライベート修飾子を模倣しようとしているのではなく、概念を実装しているだけです。それを実装する方法はそれほど関連性がなく、ツール/言語に依存します。

    ちなみに、私はC#のバックグラウンドを持っており、JavaScriptを使い始めたとき、この「ハック」の障壁を克服するのは非常に難しいと感じました。最近まで、クロージャ、オブジェクトリテラル、コンストラクター関数、コールバック関数を検討していました...それらを検討しましたすべてのハック。その精神的な障壁を乗り越えて初めて、JavaScriptを高く評価し、JavaScriptの長所を生かした優れたコードを書き始めました。

  • 「プライベート」関数を非表示にする方法については、提案したように名前空間内に非表示にするか、モジュールパターン またはそのバリエーションの1つを使用して変数として初期化することができます。これは、これらの変数が新しいインスタンスごとに再作成されることを意味するとおっしゃいましたが、クラスベース言語のプライベート変数についても同じです。staticに相当するものを探している場合は、MyClass.StaticVariable = "myStaticVariable"を実行するだけで、それで十分です。

  • パブリックAPIを変更できる場合、動的言語で詳細を非表示にするとどのような違いがありますか?さて、これは私の最初のポイントに戻ります-クラスベース言語の間違った目でJavaScriptを調べます-動的言語の観点から、その動的な性質のために得られるすべての柔軟性を想像してください、私はちょうどSinonライブラリを見ていましたこれは、タイマーのようなものをモックする方法を提供します(基本的に、ほとんどすべてのパブリックapiをハイジャックできるため)、非動的言語ではそれがどれほど難しいかを想像し、モックフレームワーク、IoCコンテナー、プロキシジェネレーター、リフレクションのすべての必要性を参照してください、JavaScriptのような動的言語でこれらすべてを無料で入手できます。したがって、あなたの議論は技術的には正しいものですが、完全な文脈に置くと、それは無関係になります。

    繰り返しますが、実装の詳細を隠すときは、C#/ Java / C ++機能を模倣するためにそれを行っているのではなく、最初は再利用性と保守のための優れた設計と優れた概念であるためですが、次に、私の見解では重要です。これは言語パターンであり、JavaScriptで行われる方法であるためです。それを過小評価しないでください。今日は明日誰かが読むコードを書くので、一般的なパターン(詳細やカプセル化を隠すようなオブジェクト指向デザイン、モジュールパターンなどの言語固有のパターン)に従うと、コミュニケーションが大幅に向上します。 -それ自体で-私の意見ではそれを受け入れるのに十分です。これを「JavaScriptの方法」で行うと、他のチームメンバーと適切に通信していることになります。

  • 読みやすさに関する限り、パターンと一般的な規則に従うことで、コードの読者に有利に働きます。C ++で同じことを行おうとすると、読みやすさに影響します。

  • アンダースコアを付けてください。特にチームで作業する場合は規則が適しています。jQueryUIの最大のライブラリの1つはこの規則を使用しており、機能して物事をより明確にします。したがって、変数が実際に非表示になっていない場合でも(jQuery UIでは、下線付きの変数にアクセスして変更できます)、コードベースにもたらす明確さと一貫性は非常に重要です。

最後に、「JavaScriptの方法で実行する」と言い続けていますが、そのステートメント自体は紛らわしいものです。あなたが言及している問題にはJavaScriptの方法があると思いますが、より大規模な場合、特にフレームワークやライブラリのレベルでは、物事を行う方法は1つではありません。しかし、それはJavaScriptの興奮と美しさの一部です。それを受け入れます。

于 2012-10-23T23:19:21.190 に答える
3

はい、誤解に基づく悪い考えです。Java や C# などの言語にはプライバシーが強制されている、または回避できないという誤解がよくあります。しかし実際には、これは完全に誤りであり、privateJava や C# などのその他のアクセス修飾子は、強制ではなく、アドバイスとコミュニケーションに帰着します。

マーティン・ファウラー著:

アクセス制御のポイントは、アクセスを防ぐことではなく、クラスがいくつかのものを自分自身に保持することを好むことを知らせることです。プログラミングの多くのことと同様に、アクセス修飾子を使用することは、主に通信に関するものです。

これはまさにアンダースコアプレフィックスが行うことです。また、アンダースコアのプレフィックスを使用すると、言語オブジェクト モデルと戦うことはありません。

何らかの理由で閉鎖を回避することはできませんが、これは他のすべての言語で可能です. 皮肉なことに、Javascript を最も柔軟性のない言語にしただけです。

オブジェクトのプロパティではなく変数を使用するため、

基本的に、クロージャーで得られる OOP 機能は、一連の関数をグループ化するだけにかなり制限されています。これは、最初のおもちゃの例にのみ適しています。また、プライバシーはマシンによって強制されなければならないという常識に反して主張する場合、「パッケージのプライベート」、「保護」、または多くの種類のより洗練された「アクセス制御」レベルを持つことさえできません。

ところで、各インスタンスごとに各メソッドごとに一意の関数オブジェクトを作成する必要もあります (それらには観察可能な ID があるため)。

于 2013-08-08T10:31:48.360 に答える
2

どちらのアプローチが優れているかは、達成しようとしていることに大きく依存します。

あなたの選択肢1は、特定の命名規則に関する開発者の合意です。このアプローチは、この契約にコミットする意思のある開発者のチームがあり、ソフトウェアの一部を使用しているのは彼らだけである場合に適しています。あなたが正しく述べているように、このアプローチはコードの可読性とより重要なテスト容易性を非常によくサポートします。また、選択肢 2 よりも、コードを理解して保守できる開発者がはるかに多くなります。

あなたの選択肢 2、およびクロージャーを使用してオブジェクトの実際のプライベート メンバーを実装するためのパターンは、ハックではなく、javascript の動作方法です。オブジェクト内の属性を本当に保護する必要がある場合は、このアプローチが適しています。これは主に、チームのメンバーではない開発者がコードを利用できるようにしたり、公開したりする場合に当てはまります。しかし、コードの可読性とテスト容易性がいくらか失われ、コードを理解して維持できる開発者が少なくなります。

また、あなたの側でjavascriptについて混乱しているようです。プライベート属性を(まだ)サポートしていないjavascriptについては正しいです。しかし、javascript は言語構造として「クラス」をサポートしておらず、「クラス メソッド」もサポートしていません。JavaScript のすべては (単なる) オブジェクトです。クラス コンストラクターは、オブジェクトを作成するための単なるパターンであり、メソッドは、"関数" 型のオブジェクトの (単なる) 属性です。従来のオブジェクト指向言語のように、javascript のオブジェクトはクラスのインスタンスではありません。

したがって、あなたが正しく述べているように、JavaScript でオブジェクトを作成した後、メソッド属性とも呼ばれる関数属性を含む、そのオブジェクトのすべての属性を変更できます (クロージャーにカプセル化された属性を除く)。これは JavaScript の能力ですが、オブジェクトを変更から保護する場合には不利であり、JavaScript が (まだ) 設計されているものではありません。ユーザーがオブジェクトを書き換えるのを防ぐことはできませんが、クロージャーを使用すると、より困難にすることができます。

これがあなたの決定に役立つことを願っています。

于 2012-10-18T21:18:06.950 に答える
1

JavaScript の次のバージョンである ES6: WeakMap のツールを使用することをお勧めします。私はこれを IE8+ に実装し、それについて書いて広範囲に使用しています。

function createStorage(creator){
  creator = creator || Object.create.bind(null, null, {});
  var map = new WeakMap;
  return function storage(o, v){
    if (1 in arguments) {
      map.set(o, v);
    } else {
      v = map.get(o);
      if (v == null) {
        v = creator(o);
        map.set(o, v);
      }
    }
    return v;
  };
}


var _ = createStorage(function(o){ return new Backing(o) });

function Backing(o){
  this.facade = o;
}
Backing.prototype.doesStuff = function(){
  return 'real value';
}

function Facade(){
  _(this);
}
Facade.prototype.doSomething = function doSomething(){
  return _(this).doesStuff();
}

詳しくは:

  1. http://bbenvie.com/articles/2012-07-25/JavaScript-Classes-with-private-protected-and-super
  2. http://benvie.github.com/WeakMap/
  3. http://benvie.github.com/harmony-collections/
于 2012-10-18T12:48:07.580 に答える
0

これは実際には天気の問題に対する答えではありませんが、クロージャーを使用してそうすることが可能であることを指摘したいのですが、プライベートメソッドを厳密に適用することをお勧めします。

これは基本的にはvar foo=functionトリックですが、オブジェクトのインスタンスごとに新しい関数を作成するわけではありません。それを行う方法はこれです:

// Create a closure that we can share with our constructor
var MyConstructor = (function(){

    // Within this closure you can declare private functions
    // and variables:
    var private_function = function () {};
    var private_var = {};        

    // Declare the actual constructor below:
    return function () {

    }
})()

個人的に、私はこれについて2つの心を持っています。多くの人が同時に同じファイルのセットで作業している作業環境から来て、オブジェクトを誤って使用しようとする人によってコードが壊れないように、ものを本当にプライベートにする機能に本当に感謝しています。

一方、Perlのバックグラウンドから来た私は、オブジェクトを設計した元のプログラマーが全能ではなく、コードが将来使用されるすべての方法を予測できないという事実にも感謝しています。一部のコーナーケースでは、オブジェクトの内部を実際に変更する必要がある場合があります。これは、経験豊富なPerlプログラマーの標準として受け入れられている知恵です。

結局、両方の方法が機能します。PerlのCPANリポジトリ内のほとんどのモジュールは非常に安定しており、実際のプライバシーがなくても箱から出してすぐに機能します。一方で、バグが原因でバグが発生するプロジェクトにも取り組んできました。私はそれがあなたのチームがどのように協力するかに要約されると思います。コーディングガイドラインと文化がすべての人に尊重されている場合、Perlスタイルの「_」トリックはうまく機能します。一方、将来のメンテナがどのようにコードを壊してしまうかを信頼できない場合は、プライバシーをより厳密に適用する方がおそらく良いでしょう。

于 2012-10-18T06:05:35.587 に答える
0

私が働いているところでは、推奨されるパターンは「名前空間パターン」を使用し、他のオブジェクト指向言語と同じように名前空間を扱うことです。

あなたはこの答えでどのように見ることができます

于 2012-10-15T20:48:29.280 に答える
0

問題にはいくつかのアプローチがあり、どのアプローチを選択するかは、(1) ターゲット ブラウザーのサポート、(2) プロジェクトの目標、(3) 個人の好みなど、いくつかの要因によって異なります。特に私が意見を持っているいくつかのアプローチを強調します。

シンボル

これは最も堅牢なソリューションであり、JavaScript の未来であるため、最初に強調します。これは、ECMAScript 標準の次のバージョンに含まれる予定です。現在、ES5 準拠のブラウザーでシムを使用することで可能です。つまり、基本的に過去 2 年間にリリースされたすべての A グレードのブラウザーで動作します。主な欠点は、IE 8 ではサポートされていないことですが、IE 9 および 10 (もちろん、FF、Chrome、および Safari のすべての最新バージョン) ではサポートされています。IE8 がまだ重要な場合、これはまだ選択肢ではありません。

シムはここからダウンロードできます: SymbolsForES5。このライブラリを使用すると、この機能を使い始めることができます。将来、Symbol がブラウザーにネイティブに組み込まれると、コードはうまく移行できるはずです。

プライベート メンバーにシンボルを使用する例を次に示します。

var x = { };
var a = new Symbol();
x[a] = 5;
console.log(x[a]); // => 5

aSymbol オブジェクトにアクセスできる限り、xオブジェクトからプロパティを読み取ることができますが、アクセス権のない人aは読み取ることができません。これにより、実際にプライベートメンバーを持つことができます:

var Person = (function() {

    var firstName = new Symbol(),
        lastName = new Symbol();

    function Person(first, last) {
        this[firstName] = first;
        this[lastName] = last;
    }

    Person.prototype.getFullName = function() {
        return this[firstName] + ' ' + this[lastName];
    };

    return Person;

})();

var john = new Person('John', 'Smith');
john.getFullName(); // => 'John Smith'

Object.getOwnPropertyNames(john); // => [ ]

DON'T ACCESS文字の使用

あなたが言及したように、いつでもプロパティの前に文字 (アンダースコアなど) を付けて、外部コードからアクセスしてはならないことを示すことができます。主な欠点は、プロパティがまだアクセス可能であることです。ただし、いくつかの利点があります。(1)効率的なメモリ(2)プロトタイプの継承を使用する完全な機能(3)使いやすい(4)デバッグが容易 (プロパティがデバッガーに表示されるため) (5)すべてで動作するブラウザ。

私は過去にこの方法を広範囲に使用して大きな成功を収めました。アンダースコアの代わりに、私が開発した 1 つのフレームワーク (joi)で、シンボルを使用しまし#た。これは、プロパティにアクセスするのが難しくなるためです (代わりに角括弧表記でアクセスする必要があります)。アクセスして、おそらくそのままにしておく必要があります。

function Person(first, last) {
    this['#firstName'] = first;
    this['#lastName'] = last;
}

Person.prototype.getFullName = function() {
    return this['#firstName'] + ' ' + this['#lastName'];
};

var john = new Person('John', 'Smith');
john.getFullName(); // => 'John Smith'

私はこの手法を約 7 年間使用して大きな成功を収めてきました。シンボルがニーズに合わない場合は、この手法を強くお勧めします。

コンストラクター内のプライベート

メモリ/パフォーマンス上の理由から、この手法を使用しないことにしたとおっしゃっていましたが、真のプライベートに近いものが必要で、レガシー ブラウザのサポートが必要なためにシンボルを使用できない場合は、良いオプションです。私が勤務している会社では、大規模なアプリケーションでこのモデルを使用していますが、消費されるメモリは実際には問題になりません。さらに、このモデルでメモリとパフォーマンスを最適化する方法を発見した研究について聞いたことがあります。最新のブラウザー (他のブラウザーではないにしても、少なくとも V8/Chrome) はこれらの最適化を実装し始めていると思います。(この情報は、ブレンダン・アイクから聞いた話から得たものです。申し訳ありませんが、どの話が私の頭から離れたものだったのかわかりません。)

最終的に、この手法の使用を支持する専門家がいます。私の好みではありませんが、私の会社はこの手法で成功しており、信頼できるモデルであることを保証できます。

完全を期すために、次のようになります。

function Person(first, last) {
    this.getFullName = function() {
        return first + ' ' + last;
    };
}

var john = new Person('John', 'Smith');
john.getFullName(); // => 'John Smith'

JavaScript エンジンは、メソッドごとに実際には新しい関数を作成しないことでこれを最適化することができますgetFullName(本によると、作成することになっていますが)。この場合、毎回新しい関数オブジェクトを実際に作成するかどうかを判断する方法がないためです。厄介な部分は、新しい関数オブジェクトが作成されるたびに決定できるコードが導入されると、実際にそれを行うためにエンジンを切り替える必要があることです。

ウィークマップ

別の回答では、ベンビー( WeakMapsについて言及)[http://stackoverflow.com/a/12955077/1662998]。IE8 のサポートが必要で、プロパティで実際にプライベート メンバーをシミュレートする場合は、この代替手段も検討します。

于 2012-10-24T05:19:35.733 に答える
0

プライベート メソッドは、他の開発者がアクセスすべきでないとわかっていても、これらのメソッドにアクセスできないようにする場合に適していると思います。

この Crockfordの記事、特に OOP とプライベート メンバーに関する部分をお読みください。

この助けを願っています

于 2012-10-15T21:11:54.377 に答える
0

モジュールパターンを明らかにすることを好みます。ここをチェック

于 2012-10-15T21:55:45.870 に答える