JavaScriptプライベートメソッド


482

パブリックメソッドでJavaScriptクラスを作成するには、次のようにします。

function Restaurant() {}

Restaurant.prototype.buy_food = function(){
   // something here
}

Restaurant.prototype.use_restroom = function(){
   // something here
}

そうすれば、私のクラスのユーザーは次のことができます。

var restaurant = new Restaurant();
restaurant.buy_food();
restaurant.use_restroom();

buy_foodおよびuse_restroomメソッドによって呼び出すことができるが、クラスのユーザーによって外部からは呼び出せないプライベートメソッドを作成するにはどうすればよいですか?

言い換えれば、私は私のメソッド実装ができるようにしたいです:

Restaurant.prototype.use_restroom = function() {
   this.private_stuff();
}

しかし、これはうまくいかないはずです:

var r = new Restaurant();
r.private_stuff();

private_stuffこれらの両方が成り立つようにプライベートメソッドとして定義するにはどうすればよいですか?

Doug Crockfordの記事を何度か読んだことがありますが、「プライベート」メソッドをパブリックメソッドから呼び出したり、「特権」メソッドを外部から呼び出したりすることはできないようです。

回答:


403

あなたはそれを行うことができますが、欠点はそれがプロトタイプの一部になることができないことです:

function Restaurant() {
    var myPrivateVar;

    var private_stuff = function() {  // Only visible inside Restaurant()
        myPrivateVar = "I can set this here!";
    }

    this.use_restroom = function() {  // use_restroom is visible to all
        private_stuff();
    }

    this.buy_food = function() {   // buy_food is visible to all
        private_stuff();
    }
}

9
クロージャーの内側に薄い部分を隠しても、すべての通訳者のプライバシーは保証されません。code.google.com/p/google-caja/wiki/…を
マイクサミュエル

51
@mikesamuel-真、ただしこれらの通訳にバグがある場合のみ:)
jvenema

133
これはプライベートメソッドで十分ですが、特にこれらのオブジェクトを多数作成する場合は、通常のプロトタイプメソッドよりも多くのメモリを消費する傾向があります。すべてのオブジェクトインスタンスに対して、クラスではなくオブジェクトにバインドされた個別の関数を作成します。また、オブジェクト自体が破棄されるまで、ガベージコレクションは行われません。
アリンダム

4
オブジェクトをこのように宣言すると、McDonalds()がRestaurant()から継承するようにした場合、McDonaldsはプライベートメソッドをオーバーライドできません。まあ、実際にはできますが、他のメソッドがプライベートを呼び出す場合は機能せず、メソッドの元のバージョンが呼び出され、親メソッドを呼び出すこともできません。これまでのところ、継承でうまく機能するプライベートメソッドを宣言するための良い方法を見つけていません。これとパフォーマンスの影響により、これは非常に良い設計パターンにはなりません。下線で始まるようなプライベートメソッドを示すために、ある種の接頭辞を付けることをお勧めします。
ホフマン2013年

68
プライベートメソッドはオーバーライドされるべきではありません-それらはプライベートです。

163

自己呼び出し関数と呼び出しの使用

JavaScriptはプロトタイプを使用し、オブジェクト指向言語のようなクラス(またはメソッド)はありません。JavaScript開発者はJavaScriptで考える必要があります。

ウィキペディアの引用:

多くのオブジェクト指向言語とは異なり、関数定義とメソッド定義の間に違いはありません。むしろ、区別は関数呼び出し中に発生します。関数がオブジェクトのメソッドとして呼び出されると、関数のローカルthisキーワードがその呼び出しのオブジェクトにバインドされます。

使用したソリューションの自己起動機能通話機能をプライベート「メソッド」を呼び出すために:

var MyObject = (function () {

    // Constructor
    function MyObject (foo) {
        this._foo = foo;
    }

    function privateFun (prefix) {
        return prefix + this._foo;
    }

    MyObject.prototype.publicFun = function () {
        return privateFun.call(this, '>>');
    }

    return MyObject;
})();


var myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // ReferenceError: private is not defined

通話機能は、私たちは適切なコンテキストでプライベート関数を呼び出すことができます(this)。


Node.jsでよりシンプルに

node.jsを使用している場合、モジュール読み込みシステムを利用できるため、IIFEは必要ありません。

function MyObject (foo) {
    this._foo = foo;
}

function privateFun (prefix) {
    return prefix + this._foo;
}

MyObject.prototype.publicFun = function () {
    return privateFun.call(this, '>>');
}

exports.MyObject = MyObject;

ファイルをロードします。

var MyObject = require('./MyObject').MyObject;

var myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // ReferenceError: private is not defined


(実験的)Bind演算子を使用したES7

bindオペレーター::はECMAScriptの提案であり、Babelステージ0)で実装されています。

export default class MyObject {
  constructor (foo) {
    this._foo = foo;
  }

  publicFun () {
    return this::privateFun('>>');
  }
}

function privateFun (prefix) {
  return prefix + this._foo;
}

ファイルをロードします。

import MyObject from './MyObject';

let myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // TypeError: myObject.privateFun is not a function

34
これが最良の答えです。プライベートな方法の利点は、ジャンクではありません。
TaylorMac、2014年

1
@テイラーマックパーツを除く.call
ピシュピッシュ

1
@janje Huh?それが問題のポイントですf()。JavaScript にはプライベートはありません(現時点ではありません)。
Yves M.

2
@YvesM。問題のポイントは、それをシミュレートする最適なパターンを選択することです。
ピッシュピッシュ

1
@changed隠された関数(外部から呼び出せない)、素敵なsintax(no .call)、小さなメモリ(インスタンスに関数がない)を持つための最良の妥協点は何ですか?それも存在しますか?
CiprianTomoiagă2016年

161

次のようなプライベートメソッドをシミュレートできます。

function Restaurant() {
}

Restaurant.prototype = (function() {
    var private_stuff = function() {
        // Private code here
    };

    return {

        constructor:Restaurant,

        use_restroom:function() {
            private_stuff();
        }

    };
})();

var r = new Restaurant();

// This will work:
r.use_restroom();

// This will cause an error:
r.private_stuff();

この手法の詳細については、http//webreflection.blogspot.com/2008/04/natural-javascript-private-methods.htmlをご覧ください。


7
また、私はDouglas Crockfordのサイトをプライベート/パブリックメソッドおよびメンバーjavascript.crockford.com/private.htmlの
Jared

10
彼はそのリンクを質問で述べました。
Gulzar Nazim

8
このメソッドの欠点は、private_stuff()がRestaurant内の他のプライベートデータにアクセスできず、他のRestaurantメソッドがprivate_stuff()を呼び出せないことです。良い点は、今述べたいずれの条件も必要ない場合は、プロトタイプでuse_restroom()を保持できることです。

6
著者は明らかにプロトタイププロパティを使用しているため、これは解決策であり、受け入れられた答えになるはずです。
Gabriel Llamas 2012年

23
@georgebrockによって提案されたパターンにより、すべてのプライベートデータはすべてのレストランオブジェクト間で共有されます。これは、クラスベースのOOPの静的プライベート変数および関数に似ています。OPはこれを望んでいないと思います。意味を説明するために、私はjsFiddleを作成しました
feklee

35

パブリックAPIがあり、プライベートおよびパブリックのメソッド/プロパティが必要なこれらの状況では、常にモジュールパターンを使用します。このパターンはYUIライブラリ内で人気があり、詳細はここにあります:

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

それは本当に簡単で、他の開発者が理解するのは簡単です。簡単な例:

var MYLIB = function() {  
    var aPrivateProperty = true;
    var aPrivateMethod = function() {
        // some code here...
    };
    return {
        aPublicMethod : function() {
            aPrivateMethod(); // okay
            // some code here...
        },
        aPublicProperty : true
    };  
}();

MYLIB.aPrivateMethod() // not okay
MYLIB.aPublicMethod() // okay

この種のものは、IDEのオートコンプリートでは検出されません:(
クリック投票

19
ただし、これはクラスではないため、状態が異なる2つの「インスタンス」を持つことはできません。
DevAntoine 2012

()の部分を削除すると、「クラス」が作成されます。少なくとも、さまざまな状態のさまざまなインスタンスをインスタンス化できる場所です。モジュールのパターンはかなりメモリを
消費し

@DevAntoine 26の答えの17に対するコメントを見てください。JavaScriptでは、拡張可能なクラスとプライベートメソッドを簡単に連携させることはできません。この場合の私の提案は、継承よりも構成を使用することです。囲まれたコンクリートオブジェクトと同じメソッドで拡張可能なプロトタイプを作成します。次に、プロトタイプの内部で、具象オブジェクトのメソッドを呼び出すタイミングを決定できます。

次のようにプライベート関数からパブリック変数を呼び出すことの欠点はありますaPrivateMethod = function() { MYLIB.aPublicProperty}か?
Hanna

21

これは、ダグラス・クロックフォードが彼のサイト「JavaScriptのプライベートメンバー」で提案したことを理解するために私が作成したクラスです。

function Employee(id, name) { //Constructor
    //Public member variables
    this.id = id;
    this.name = name;
    //Private member variables
    var fName;
    var lName;
    var that = this;
    //By convention, we create a private variable 'that'. This is used to     
    //make the object available to the private methods. 

    //Private function
    function setFName(pfname) {
        fName = pfname;
        alert('setFName called');
    }
    //Privileged function
    this.setLName = function (plName, pfname) {
        lName = plName;  //Has access to private variables
        setFName(pfname); //Has access to private function
        alert('setLName called ' + this.id); //Has access to member variables
    }
    //Another privileged member has access to both member variables and private variables
    //Note access of this.dataOfBirth created by public member setDateOfBirth
    this.toString = function () {
        return 'toString called ' + this.id + ' ' + this.name + ' ' + fName + ' ' + lName + ' ' + this.dataOfBirth; 
    }
}
//Public function has access to member variable and can create on too but does not have access to private variable
Employee.prototype.setDateOfBirth = function (dob) {
    alert('setDateOfBirth called ' + this.id);
    this.dataOfBirth = dob;   //Creates new public member note this is accessed by toString
    //alert(fName); //Does not have access to private member
}
$(document).ready()
{
    var employee = new Employee(5, 'Shyam'); //Create a new object and initialize it with constructor
    employee.setLName('Bhaskar', 'Ram');  //Call privileged function
    employee.setDateOfBirth('1/1/2000');  //Call public function
    employee.id = 9;                     //Set up member value
    //employee.setFName('Ram');  //can not call Private Privileged method
    alert(employee.toString());  //See the changed object

}

5
that = thisは非常に一般的なパターンで、前述のCrockfordの著書「Javascript:The good parts」で一般化されています
オリゴフレン

8
thatthis関数が別のオブジェクトにバインドされている場合、スコープの問題を回避する代わりに使用されます。ここでは、保存thisthat、二度と使用しないことは、まったく使用しないのと同じです。(メソッド宣言ではなく)内部関数全体で変更する必要がthisありthatますConstructor。場合employeeされるapply編またはcall何らかの方法で編これらのメソッドは、以来、投げるかもしれないthis間違ってバインドされます。
Maroshii 2013

また、すべてのインスタンスには非効率的なプライベート関数の完全なコピーがあります。パブリックメソッドがプライベートクラス変数にアクセスできないという事実に加えて、dartに切り替えたいと思っています。残念ながらangulardartはスーパーベータです。
レイフォス2014年

これの「コンストラクタ」メソッドはどこにありますか?インスタンス化されたときにクラスのコンストラクタメソッドで通常実行されるロジックをどこに配置しますか?
BadHorsie 2015年

13

私はこれを思い起こしました:編集:実際には、誰かが同じ解決策にリンクしています。ああ!

var Car = function() {
}

Car.prototype = (function() {
    var hotWire = function() {
        // Private code *with* access to public properties through 'this'
        alert( this.drive() ); // Alerts 'Vroom!'
    }

    return {
        steal: function() {
            hotWire.call( this ); // Call a private method
        },
        drive: function() {
            return 'Vroom!';
        }
    };
})();

var getAwayVechile = new Car();

hotWire(); // Not allowed
getAwayVechile.hotWire(); // Not allowed
getAwayVechile.steal(); // Alerts 'Vroom!'

1
これは素晴らしいテクニックですが、コンストラクターでパラメーターをどのように許可しますか?たとえばvar getAwayVehicle = new Car(100);100速度はどこにあり、速度を警告したいとします。ありがとう!
Jason

1
それを理解し、var Car = function(speed) { this.speed = speed; }`return {constructor:Car、...` を持つことができます
Jason

11

閉鎖についての理解が不足しているため、そのような質問が何度も出てくると思います。JSではСlosuresが最も重要です。すべてのJSプログラマーは、その本質を感じなければなりません。

1.まず、個別のスコープ(閉鎖)を作成する必要があります。

function () {

}

2.この領域では、私たちは好きなことをすることができます。そして、誰もそれについて知りません。

function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
}

3.世界が私たちのレストランクラスについて知るためには、閉鎖からそれを返さなければなりません。

var Restaurant = (function () {
    // Restaurant definition
    return Restaurant
})()

4.最後に、次のようにします。

var Restaurant = (function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
    return Restaurant
})()

5.また、このアプローチには継承とテンプレート化の可能性があります

// Abstract class
function AbstractRestaurant(skills) {
    var name
    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return skills && name in skills ? skills[name]() : null
    }
    return Restaurant
}

// Concrete classes
SushiRestaurant = AbstractRestaurant({ 
    sushi: function() { return new Sushi() } 
})

PizzaRestaurant = AbstractRestaurant({ 
    pizza: function() { return new Pizza() } 
})

var r1 = new SushiRestaurant('Yo! Sushi'),
    r2 = new PizzaRestaurant('Dominos Pizza')

r1.getFood('sushi')
r2.getFood('pizza')

これが誰かがこの主題をよりよく理解するのに役立つことを願っています


2
ポイント4であなたが持っているのは素晴らしいです!プロトタイプでメソッドを使用することでパフォーマンスとメモリの両方の利点が得られ、これらのパブリックメソッドがプライベートメンバーに完全にアクセスできるのは、ここでの唯一の答えだと思います。+1
Hudon 2013年

7
動作しません。ここでの名前変数は、静的変数のように機能し、Restaurantのすべてのインスタンスで共有されます。これがjsbinです:jsbin.com/oqewUWa/2/edit?js,output
Shah

それは良い試みですが、Shitalが指摘したように、name変数にはバグがあります。
オリゴフレン2014

2
これが機能しないことを再確認するためにここに私の2cを追加すると見栄えが良くなりますが、上記で指摘したように、「name」は静的変数として機能します。つまり、すべてのインスタンスで共有されます
Paul Carroll

10

個人的には、JavaScriptでクラスを作成するために次のパターンを好みます。

var myClass = (function() {
    // Private class properties go here

    var blueprint = function() {
        // Private instance properties go here
        ...
    };

    blueprint.prototype = { 
        // Public class properties go here
        ...
    };

    return  {
         // Public class properties go here
        create : function() { return new blueprint(); }
        ...
    };
})();

ご覧のとおり、クラスプロパティとインスタンスプロパティの両方を定義できます。それぞれのプロパティはパブリックとプライベートのどちらでもかまいません。


デモ

var Restaurant = function() {
    var totalfoodcount = 0;        // Private class property
    var totalrestroomcount  = 0;   // Private class property
    
    var Restaurant = function(name){
        var foodcount = 0;         // Private instance property
        var restroomcount  = 0;    // Private instance property
        
        this.name = name
        
        this.incrementFoodCount = function() {
            foodcount++;
            totalfoodcount++;
            this.printStatus();
        };
        this.incrementRestroomCount = function() {
            restroomcount++;
            totalrestroomcount++;
            this.printStatus();
        };
        this.getRestroomCount = function() {
            return restroomcount;
        },
        this.getFoodCount = function() {
            return foodcount;
        }
    };
   
    Restaurant.prototype = {
        name : '',
        buy_food : function(){
           this.incrementFoodCount();
        },
        use_restroom : function(){
           this.incrementRestroomCount();
        },
        getTotalRestroomCount : function() {
            return totalrestroomcount;
        },
        getTotalFoodCount : function() {
            return totalfoodcount;
        },
        printStatus : function() {
           document.body.innerHTML
               += '<h3>Buying food at '+this.name+'</h3>'
               + '<ul>' 
               + '<li>Restroom count at ' + this.name + ' : '+ this.getRestroomCount() + '</li>'
               + '<li>Food count at ' + this.name + ' : ' + this.getFoodCount() + '</li>'
               + '<li>Total restroom count : '+ this.getTotalRestroomCount() + '</li>'
               + '<li>Total food count : '+ this.getTotalFoodCount() + '</li>'
               + '</ul>';
        }
    };

    return  { // Singleton public properties
        create : function(name) {
            return new Restaurant(name);
        },
        printStatus : function() {
          document.body.innerHTML
              += '<hr />'
              + '<h3>Overview</h3>'
              + '<ul>' 
              + '<li>Total restroom count : '+ Restaurant.prototype.getTotalRestroomCount() + '</li>'
              + '<li>Total food count : '+ Restaurant.prototype.getTotalFoodCount() + '</li>'
              + '</ul>'
              + '<hr />';
        }
    };
}();

var Wendys = Restaurant.create("Wendy's");
var McDonalds = Restaurant.create("McDonald's");
var KFC = Restaurant.create("KFC");
var BurgerKing = Restaurant.create("Burger King");

Restaurant.printStatus();

Wendys.buy_food();
Wendys.use_restroom();
KFC.use_restroom();
KFC.use_restroom();
Wendys.use_restroom();
McDonalds.buy_food();
BurgerKing.buy_food();

Restaurant.printStatus();

BurgerKing.buy_food();
Wendys.use_restroom();
McDonalds.buy_food();
KFC.buy_food();
Wendys.buy_food();
BurgerKing.buy_food();
McDonalds.buy_food();

Restaurant.printStatus();

このフィドルも参照してください。


これは私がES6クラスを使用し、それがあまりにもtranspilesか見たくなる
sheriffderek

9

このすべての閉鎖には費用がかかります。特にIEで速度の影響をテストしてください。命名規則に慣れている方がよいでしょう。IE6の使用を余儀なくされている企業のWebユーザーはまだたくさんいます...


34
誰が真剣に気にしていますか?
nowayyy

17
まだIE6を使用している9%は、速度、最適化、およびすべての最新のHTML5機能を気にしていません。したがって、クロージャーは何の費用もかかりません。
Gabriel Llamas 2012年

6
現在は0.5%(2012年8月)です。w3schools.com/ browsers / browsers_explorer.asp
Lorenzo Polidori

7
@LorenzoPolidori w3schoolsユーザー!==企業のWebユーザー;]
WynandB '19

命名規則(例:アンダースコアを前に付ける)が適しています。コードの保守が容易になり、メソッドはプロトタイプで定義されます。しかし最近は... typescriptでメソッドをプライベートとしてマークしています。
David Sherret

5

そんなに冗長にしないでください。それはJavascriptです。命名規則を使用するます。

es6クラスで長年働いた後、私は最近es5プロジェクトの作業を開始しました(すでに非常に冗長に見えるrequireJSを使用しています)。私はここで言及されたすべての戦略を何度も繰り返してきましたが、基本的には命名規則使用するために要約されます

  1. JavaScriptには、などのスコープキーワードはありませんprivate。JavaScriptに入る他の開発者は、このことを事前に知っています。したがって、単純な命名規則で十分です。アンダースコアを前に付けるという単純な命名規則により、プライベートプロパティとプライベートメソッドの両方の問題が解決されます。
  2. 速度上の理由からプロトタイプを利用しましょう。しかし、それ以上詳細にはなりません。es5の「クラス」を他のバックエンド言語で期待されるものにできるだけ近づけるようにしてみましょう(インスタンスを返す必要がない場合でも、すべてのファイルをクラスとして扱います)。
  3. より現実的なモジュールの状況でデモンストレーションしましょう(古いes5と古いrequireJsを使用します)。

my-tooltip.js

    define([
        'tooltip'
    ],
    function(
        tooltip
    ){

        function MyTooltip() {
            // Later, if needed, we can remove the underscore on some
            // of these (make public) and allow clients of our class
            // to set them.
            this._selector = "#my-tooltip"
            this._template = 'Hello from inside my tooltip!';
            this._initTooltip();
        }

        MyTooltip.prototype = {
            constructor: MyTooltip,

            _initTooltip: function () {
                new tooltip.tooltip(this._selector, {
                    content: this._template,
                    closeOnClick: true,
                    closeButton: true
                });
            }
        }

        return {
            init: function init() {
               new MyTooltip();  // <-- Our constructor adds our tooltip to the DOM so not much we need to do after instantiation.
            }

            // You could instead return a new instantiation, 
            // if later you do more with this class.
            /* 
            create: function create() {
               return new MyTooltip();
            }
            */
        }
    });

2
JavaScript言語も一般的なブラウザホストも、プライベート状態を「非表示」にするための命名規則に依存するオブジェクトを定義しないことに注意してください。開発者が概念を理解することは正しいかもしれませんが、それでも非常に非オブジェクト指向プログラミングへのオブジェクト指向アプローチ。
リッチレーマー、

そのようなものを作るのに良い参考資料をお願いしてもいいですか?この例には、私にとって新しい部分があります。define、およびconstructor構造そのもの。私はほとんど答えに同意しますが、私は多くのOOPの影響でJSとの作業を開始し、C#の経験があったため、TSに早すぎるほどにジャンプしました。私はこれらのことを学ぶ必要があり、プロトタイピング/手順のパラダイムを理解する必要があると思います。(賛成投票、ところで)
コールドケルベロス

1
@ColdCerberusこのコードスニペットはes5を使用しています。このアプローチの完全な画像は、gist.github.com / jonnyreeves / 2474026で確認できます。しかし、あなたは、このアプローチを取るとES6クラスを使用して、それを更新したいと思う、心に留めておく:googlechrome.github.io/samples/classes-es6とES6モジュール(インポート/エクスポートの構文):hackernoon.com/...
prograhammer

5

これをes10プライベートメソッドで実行できます。#メソッド名の前にaを追加するだけです。

class ClassWithPrivateMethod {
  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
    return #privateMethod();
  }
}

2
ただし、これはステージ3であり、公式にはまだ言語の一部ではありません。
misterhtmlcss

3

Crockfordのプライベートパターンまたは特権パターンに従うソリューションのいずれかを取ってください。例えば:

function Foo(x) {
    var y = 5;
    var bar = function() {
        return y * x;
    };

    this.public = function(z) {
        return bar() + x * z;
    };
}

攻撃者がJSコンテキストに対する「実行」権限を持たない場合は、「パブリック」または「プライベート」フィールドまたはメソッドにアクセスする方法がありません。攻撃者がそのアクセス権を持っている場合は、次のワンライナーを実行できます。

eval("Foo = " + Foo.toString().replace(
    /{/, "{ this.eval = function(code) { return eval(code); }; "
));

上記のコードはすべてのconstructor-type-privacyに共通であることに注意してください。ここでのいくつかの解決策では失敗しますが、クロージャベースの解決策のほとんどすべてが、異なるreplace()パラメータを使用してこのように壊れることがあることは明らかです。

これが実行された後で作成した任意のオブジェクトがnew Foo()持ってしようとしているevalなど、返品または変更値またはコンストラクタの閉鎖で定義されたメソッドを呼び出すことができる方法を:

f = new Foo(99);
f.eval("x");
f.eval("y");
f.eval("x = 8");

これで確認できる唯一の問題は、インスタンスが1つしかなく、ロード時に作成される場合には機能しないことです。しかし、実際にプロトタイプを定義する理由はありません。その場合、攻撃者は同じパラメータを渡す方法がある限り(たとえば、定数または使用可能な値から計算される場合)、コンストラクタの代わりにオブジェクトを再作成できます。

私の意見では、これはクロックフォードのソリューションをほとんど役に立たなくします。「プライバシー」は彼のソリューションの短所(読みやすさと保守性の低下、パフォーマンスの低下、メモリの増加)を簡単に破るので、「プライバシーなし」のプロトタイプベースの方法がより良い選択になります。

私は通常、マーク__private_protectedメソッドとフィールド(Perlスタイル)に先頭のアンダースコアを使用しますが、JavaScriptでプライバシーを確​​保するという考えは、それが誤解されている言語であることを示しています。

したがって、私は彼の最初の文を除いて、クロックフォードに同意しません

それでは、JSでどのようにして本当のプライバシーを取得しますか?サーバー側でプライベートである必要があるすべてのものを置き、JSを使用してAJAX呼び出しを実行します。


これはもっとよく知られているべき深刻な問題です。この攻撃に対する「防御」はありますか?
ジェームズ

@ジェームズ私が知っているものは何もありません、それは獣の性質だと思います。指摘したように、保護された環境で動作するサーバーに機能を移動できます。私の答えでやりたかった点は、Crockfordのソリューションは役に立たず、不必要にコードを複雑にし、それについて何かをする必要性を隠してしまうことです。
Fozi 2015

ユーザーが秘密のパスワードを入力した場合、サーバー側でこれを行うことはできません。ある時点で、パスワードは「プライベート」変数になります。攻撃者はそれを読み取ることができますか?私は自分のコードを信頼していますが、とにかく私の家の規格はeval()を許可していません。攻撃者は、私が適切にチェックしなかった悪意のあるサードパーティのJavaScriptプラグインまたはライブラリである可能性があります。そのため、これらをチェックする必要があります。攻撃者はまた、私のコードと相互作用してはならない側の広告のようなものかもしれません。私はすべてのコードを匿名でラップし、(function () {allMyStuff}());グローバルに何も公開しないようにすることで、それを防ぎます。
James

@ジェームスこれはOTを取得しています、これを続行したい場合は、新しい質問を開いてください。はい、攻撃者はパスワードを読み取ることができます。「プライベート」変数から。またはDOMから。または、AJAX APIを置き換えることもできます。または、彼はあなたのページを他のもので置き換えます。彼が上記のいずれも実行できない場合は、JS変数も読み取ることができないため、JSの「プライバシー」は必要ありません。重要なのは、現在誰もが使用しているクロックフォードの「解決策」はこの問題を解決していないということです。
Fozi、2015

疑似ランダムコードの難読化は、この攻撃に対する弱い防御になる可能性があると思います。固定された名前を持つ関数に依存できない場合、関数本体を変更するのはより困難です。f.eval('nameOfVariable')'nameOfVariable'が何であるかわからないときは、もっと難しいことです
Gershom


2

パブリック関数からプライベート関数にアクセスする機能を備えたパブリック関数とプライベート関数の全範囲が必要な場合は、次のようなオブジェクトのコードをレイアウトします。

function MyObject(arg1, arg2, ...) {
  //constructor code using constructor arguments...
  //create/access public variables as 
  // this.var1 = foo;

  //private variables

  var v1;
  var v2;

  //private functions
  function privateOne() {
  }

  function privateTwon() {
  }

  //public functions

  MyObject.prototype.publicOne = function () {
  };

  MyObject.prototype.publicTwo = function () {
  };
}

なぜこれが投票されたのか誰かが教えてもらえますか?は、私にはよく見えますよ。
thomasrutter 2010年

10
を実行するたびにnew MyObject、のプロトタイプがMyObject同じ値に置き換えられます。
bpierre

2
-1。.prototypeコンストラクタの内部には決して割り当てないでください。
Bergi、2014

2
var TestClass = function( ) {

    var privateProperty = 42;

    function privateMethod( ) {
        alert( "privateMethod, " + privateProperty );
    }

    this.public = {
        constructor: TestClass,

        publicProperty: 88,
        publicMethod: function( ) {
            alert( "publicMethod" );
            privateMethod( );
        }
    };
};
TestClass.prototype = new TestClass( ).public;


var myTestClass = new TestClass( );

alert( myTestClass.publicProperty );
myTestClass.publicMethod( );

alert( myTestClass.privateMethod || "no privateMethod" );

georgebrockに似ていますが、少し冗長(IMHO)です。(どこでも見たことがない)

編集:私はこれが少し役に立たないことに気づきました。独立したすべてのインスタンス化にはパブリックメソッドの独自のコピーがあるため、プロトタイプの使用が損なわれるからです。


2

これが私がプライベート/パブリックメソッド/メンバーとJavaScriptでのインスタンス化に関してこれまでで最も楽しんだものです:

こちらが記事です:http : //www.sefol.com/?p=1090

そしてここに例があります:

var Person = (function () {

    //Immediately returns an anonymous function which builds our modules 
    return function (name, location) {

        alert("createPerson called with " + name);

        var localPrivateVar = name;

        var localPublicVar = "A public variable";

        var localPublicFunction = function () {
            alert("PUBLIC Func called, private var is :" + localPrivateVar)
        };

        var localPrivateFunction = function () {
            alert("PRIVATE Func called ")
        };

        var setName = function (name) {

            localPrivateVar = name;

        }

        return {

            publicVar: localPublicVar,

            location: location,

            publicFunction: localPublicFunction,

            setName: setName

        }

    }
})();


//Request a Person instance - should print "createPerson called with ben"
var x = Person("ben", "germany");

//Request a Person instance - should print "createPerson called with candide"
var y = Person("candide", "belgium");

//Prints "ben"
x.publicFunction();

//Prints "candide"
y.publicFunction();

//Now call a public function which sets the value of a private variable in the x instance
x.setName("Ben 2");

//Shouldn't have changed this : prints "candide"
y.publicFunction();

//Should have changed this : prints "Ben 2"
x.publicFunction();

JSFiddle:http ://jsfiddle.net/northkildonan/kopj3dt3/1/


このアプローチには1つの重要な懸念があります。2つのオブジェクトを作成した場合、メモリ内に2つの同じメソッド(たとえば、PublicFunction)が存在し、1000のオブジェクトがすべてのメモリを消費します。
Artem G

2

ほとんどの場合、モジュールパターンは適切です。しかし、数千のインスタンスがある場合、クラスはメモリを節約します。メモリの節約が問題で、オブジェクトに少量のプライベートデータが含まれているが、パブリック関数が多い場合は、すべてのパブリック関数を.prototypeに配置してメモリを節約する必要があります。

これは私が思いついたものです:

var MyClass = (function () {
    var secret = {}; // You can only getPriv() if you know this
    function MyClass() {
        var that = this, priv = {
            foo: 0 // ... and other private values
        };
        that.getPriv = function (proof) {
            return (proof === secret) && priv;
        };
    }
    MyClass.prototype.inc = function () {
        var priv = this.getPriv(secret);
        priv.foo += 1;
        return priv.foo;
    };
    return MyClass;
}());
var x = new MyClass();
x.inc(); // 1
x.inc(); // 2

オブジェクトにprivはプライベートプロパティが含まれています。これはpublic関数を介してアクセスできますgetPriv()が、この関数はfalseを渡さない限り戻りますsecret。これはメインクロージャー内でのみ認識されます。


これは保護されたメンバーをシミュレートし、それから継承する型は保護されたメンバーにもアクセスできます。私もプライベートパターンよりもこのパターンを好んでいます
HMR 2015

2

これはどうですか?

var Restaurant = (function() {

 var _id = 0;
 var privateVars = [];

 function Restaurant(name) {
     this.id = ++_id;
     this.name = name;
     privateVars[this.id] = {
         cooked: []
     };
 }

 Restaurant.prototype.cook = function (food) {
     privateVars[this.id].cooked.push(food);
 }

 return Restaurant;

})();

プライベート関数のルックアップは、即時関数のスコープ外では不可能です。機能の重複がなく、メモリを節約できます。

欠点は、プライベート変数の検索がprivateVars[this.id].cooked型がばかばかしいことです。追加の「id」変数もあります。


これは去るRestaurantとしてundefined匿名関数から何かを返すていないので。
user4815162342 2014

どこで、どうやって?作成されたレストランへの参照が失われたと仮定すると、privateVarsはその所有者への参照を持ちません。参照グラフは非循環です。何が欠けていますか?
エヴァンレイ2014

実際、これはメソッド以外のプライベートプロパティをサポートする唯一の答えです。唯一の2つの問題は回答ですでに注記されています。
ピシュピッシュ

メモリリークが発生します。のインスタンスRestaurantがガベージコレクションされた場合、その値は内に留まりますprivateVars。このWeakMap場合、A はの良い代替品になる可能性がありますArray
Gershom

2

すべてのコードを無名関数でラップします:次に、すべての関数はwindowオブジェクトにアタッチされたプライベートな関数のみになります:

(function(w,nameSpacePrivate){
     w.Person=function(name){
         this.name=name;   
         return this;
     };

     w.Person.prototype.profilePublic=function(){
          return nameSpacePrivate.profile.call(this);
     };  

     nameSpacePrivate.profile=function(){
       return 'My name is '+this.name;
     };

})(window,{});

これを使って :

  var abdennour=new Person('Abdennour');
  abdennour.profilePublic();

FIDDLE


1

関連するに個人データを保存することを好みWeakMapます。これにより、パブリックメソッドをそれらが属するプロトタイプに保持できます。これは、多数のオブジェクトに対してこの問題を処理する最も効率的な方法のようです。

const data = new WeakMap();

function Foo(value) {
    data.set(this, {value});
}

// public method accessing private value
Foo.prototype.accessValue = function() {
    return data.get(this).value;
}

// private 'method' accessing private value
function accessValue(foo) {
    return data.get(foo).value;
}

export {Foo};

0

プライベート関数はモジュールパターンを使用してパブリック変数にアクセスできません


0

みんなが自分のコードをここに投稿していたので、私もそれをするつもりです...

JavaScriptで実際のオブジェクト指向パターンを導入したので、私はクロックフォードが好きです。しかし、彼はまた新たな誤解、「それ」を思いついた。

では、なぜ「あれ=これ」を使用しているのでしょうか。プライベート関数とは何の関係もありません。それは内部関数と関係があります!

Crockfordによると、これはバグのあるコードなので、

Function Foo( ) {
    this.bar = 0; 
    var foobar=function( ) {
        alert(this.bar);
    }
} 

だから彼はこれを行うことを提案しました:

Function Foo( ) {
    this.bar = 0;
    that = this; 
    var foobar=function( ) {
        alert(that.bar);
    }
}

だから私が言ったように、クロックフォードはそれとこれに関する彼の説明が間違っていたと確信しています(しかし彼のコードは確かに正しいです)。それとも、誰が自分のコードをコピーしているのかを知るために、JavaScriptの世界をだましていたのでしょうか。私は知らない...私はブラウザオタクではない; D

編集

ああ、それがすべてです。「var that = this;」とは何ですか。JavaScriptで意味ですか?

だから、Crockieは彼の説明で本当に間違っていた....しかし、彼のコードで正しかったので、彼はまだ素晴らしい人だ。:))


0

一般に、私は一時的にオブジェクトにプライベートオブジェクト_を追加しました。メソッドの「Power-constructor」でプライバシーを明示的に開く必要があります。プロトタイプからメソッドを呼び出すと、プロトタイプメソッドを上書きできます。

  • 「Power-constructor」でパブリックメソッドにアクセスできるようにします(ctxはオブジェクトコンテキストです)。

    ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
  • 今私はこのopenPrivacyを持っています:

    GD.Fabric.openPrivacy = function(func, clss, ctx, _) {
        return function() {
            ctx._ = _;
            var res = clss[func].apply(ctx, arguments);
            ctx._ = null;
            return res;
        };
    };

0

これは私が考え出したことです:

あなたがここで見つけることができる砂糖コードの1つのクラスが必要です。保護、継承、仮想、静的なものもサポートしています...

;( function class_Restaurant( namespace )
{
    'use strict';

    if( namespace[ "Restaurant" ] ) return    // protect against double inclusions

        namespace.Restaurant = Restaurant
    var Static               = TidBits.OoJs.setupClass( namespace, "Restaurant" )


    // constructor
    //
    function Restaurant()
    {
        this.toilets = 3

        this.Private( private_stuff )

        return this.Public( buy_food, use_restroom )
    }

    function private_stuff(){ console.log( "There are", this.toilets, "toilets available") }

    function buy_food     (){ return "food"        }
    function use_restroom (){ this.private_stuff() }

})( window )


var chinese = new Restaurant

console.log( chinese.buy_food()      );  // output: food
console.log( chinese.use_restroom()  );  // output: There are 3 toilets available
console.log( chinese.toilets         );  // output: undefined
console.log( chinese.private_stuff() );  // output: undefined

// and throws: TypeError: Object #<Restaurant> has no method 'private_stuff'

0
Class({  
    Namespace:ABC,  
    Name:"ClassL2",  
    Bases:[ABC.ClassTop],  
    Private:{  
        m_var:2  
    },  
    Protected:{  
        proval:2,  
        fight:Property(function(){  
            this.m_var--;  
            console.log("ClassL2::fight (m_var)" +this.m_var);  
        },[Property.Type.Virtual])  
    },  
    Public:{  
        Fight:function(){  
            console.log("ClassL2::Fight (m_var)"+this.m_var);  
            this.fight();  
        }  
    }  
});  

https://github.com/nooning/JSClass


0

プロトタイプhttps://github.com/TremayneChrist/ProtectJSで真のプライベートメソッドを使用できるようにする新しいツールを作成しました

例:

var MyObject = (function () {

  // Create the object
  function MyObject() {}

  // Add methods to the prototype
  MyObject.prototype = {

    // This is our public method
    public: function () {
      console.log('PUBLIC method has been called');
    },

    // This is our private method, using (_)
    _private: function () {
      console.log('PRIVATE method has been called');
    }
  }

  return protect(MyObject);

})();

// Create an instance of the object
var mo = new MyObject();

// Call its methods
mo.public(); // Pass
mo._private(); // Fail

1
どのように機能するか説明していただけますか?どのように/どこでメソッドを呼び出すことができます_privateか?
Bergi、2014

0

プライベートメソッドを定義できる実際のコンストラクター関数の周りにクロージャーを配置する必要があります。これらのプライベートメソッドを使用してインスタンスのデータを変更するには、関数の引数として、または.apply(this)でこの関数を呼び出すことによって、インスタンスに「this」を与える必要があります

var Restaurant = (function(){
    var private_buy_food = function(that){
        that.data.soldFood = true;
    }
    var private_take_a_shit = function(){
        this.data.isdirty = true;   
    }
    // New Closure
    function restaurant()
    {
        this.data = {
            isdirty : false,
            soldFood: false,
        };
    }

    restaurant.prototype.buy_food = function()
    {
       private_buy_food(this);
    }
    restaurant.prototype.use_restroom = function()
    {
       private_take_a_shit.call(this);
    }
    return restaurant;
})()

// TEST:

var McDonalds = new Restaurant();
McDonalds.buy_food();
McDonalds.use_restroom();
console.log(McDonalds);
console.log(McDonalds.__proto__);

実際には機能しません。すべてnew Restaurantに独自のrestaurantコンストラクタがあり、「プロトタイプ」は完全に乱用されます。
Bergi、2014

@ベルギ。実際、あなたは正しいです。それは機能しますが、リソースを独占することになります(そのように呼ばれますか?)。私はそれに関して私の答えを編集しました。
Flex Elektro Deimling 2014

更新していただきありがとうございます。以前のバージョンを何と呼ぶべきかわからない( "バグ" :-)
Bergi

0

もう手遅れだと思いますが、どうですか?

var obj = function(){
    var pr = "private";
    var prt = Object.getPrototypeOf(this);
    if(!prt.hasOwnProperty("showPrivate")){
        prt.showPrivate = function(){
            console.log(pr);
        }
    }    
}

var i = new obj();
i.showPrivate();
console.log(i.hasOwnProperty("pr"));

0

この質問にはすでに多くの回答がありますが、私のニーズに合うものは何もありません。だから私は自分の解決策を思いついた、それが誰かのために役立つことを願っています:

function calledPrivate(){
    var stack = new Error().stack.toString().split("\n");
    function getClass(line){
        var i = line.indexOf(" ");
        var i2 = line.indexOf(".");
        return line.substring(i,i2);
    }
    return getClass(stack[2])==getClass(stack[3]);
}

class Obj{
    privateMethode(){
        if(calledPrivate()){
            console.log("your code goes here");
        }
    }
    publicMethode(){
        this.privateMethode();
    }
}

var obj = new Obj();
obj.publicMethode(); //logs "your code goes here"
obj.privateMethode(); //does nothing

ご覧のとおり、JavaScriptでこのタイプのクラスを使用すると、このシステムは機能します。私が理解している限り、上記でコメントしたメソッドはどれもしませんでした。


1
好奇心が強い:あなたは本当に関数を実際に公開する必要がありますが、実行時に何もしないようにする必要があります。もしそうなら、なぜですか?このアプローチのメリットは何だと思いますか?私には、これは不必要なパフォーマンスオーバーヘッド、不明確なAPI、そしておそらくデバッグの地獄を引き起こすことにバインドされているように見えますが、私は常に新しい視点に向かって開いています...
JHH

2
@JHH正直に言うと、これを振り返ってみると、私はかなり手のひらになっています。オーバーヘッドは一般にまったく価値がありませんが、私にとってはこれらのクラスをあまり呼び出さなかったので、それほど大きな問題ではありませんでした。このようにした理由は、関数の記述と呼び出しの方法が比較的クリーンであるためです。当時はシンボルなどがわからなかったのですが、今ではクラスを使うときにそういうふうになっていると思います。この答えをまとめて削除することを検討しています。私はいくつかの愚かな答えを投稿しましたが、ねえ、あなたは生きて学びます。
thegunmaster

フィードバックをお寄せいただきありがとうございます!私は何かを誤解していたかどうかわかりませんでした。しかし、はい、私たちはみんな生きて学びます!
JHH 2018

0

プライベートインターフェイスとパブリックインターフェイス、および構成のサポートを備えたクリーンでシンプルな「クラス」ソリューションについては、この回答を参照してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.