523

public メソッドで 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_foodanduse_restroomメソッドからは呼び出せるが、クラスのユーザーからは外部から呼び出せないプライベート メソッドを作成するにはどうすればよいですか?

つまり、メソッドの実装で次のことができるようにしたいと考えています。

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

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

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

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

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

4

34 に答える 34

439

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

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();
    }
}
于 2008-09-11T01:26:52.583 に答える
201

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

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

call 関数を使用すると、適切なコンテキストでプライベート関数を呼び出すことができます ( 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, ">>");
}
    
module.exports= MyObject;

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

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

(new!) 将来の JavaScript バージョンのネイティブ プライベート メソッド

TC39プライベート メソッドと JavaScript クラスのゲッター/セッターの提案はステージ 3 です。つまり、近いうちに、JavaScript がプライベート メソッドをネイティブに実装することになります。

JavaScript プライベート クラス フィールドは、最新の JavaScript バージョンに既に存在することに注意してください。

使用方法の例を次に示します。

class MyObject {

  // Private field
  #foo;
    
  constructor(foo) {
    this.#foo = foo;
  }

  #privateFun(prefix) {
   return prefix + this.#foo;
  }
    
  publicFun() {
    return this.#privateFun(">>");
  }

}

このコードを古い JavaScript エンジンで実行するには、 JavaScript トランスパイラー/コンパイラーが必要になる場合があります。

#PS:プレフィックスの理由が気になる場合は、こちらをお読みください

(非推奨) Bind Operator を使用した ES7

警告: バインド オペレーター TC39 の命題はほぼ死んでいます https://github.com/tc39/proposal-bind-operator/issues/53#issuecomment-374271822

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

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

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

function privateFun (prefix) {
  return prefix + this._foo;
}
于 2014-08-07T01:34:39.930 に答える
164

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

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 をご覧ください。

于 2008-09-11T01:27:38.573 に答える
37

パブリック 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
于 2008-09-11T02:30:32.540 に答える
21

Douglas Crockford が彼のサイトPrivate Members in 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

}
于 2012-02-15T05:43:12.013 に答える
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!'
于 2009-09-01T10:13:29.010 に答える
11

クロージャーの理解が不足しているため、そのような質問が何度も出てくると思います。Closures は JS で最も重要なことです。すべての 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')

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

于 2013-08-14T03:06:00.707 に答える
9

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

于 2008-09-15T13:47:18.217 に答える
2

モジュール パターンの神格化: モジュール パターンの公開

非常に堅牢なパターンへのきちんとした小さな拡張。

于 2008-09-11T03:10:32.367 に答える
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 に似ていますが、少し冗長です (私見) この方法で何か問題はありますか? (どこかで見たことがない)

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

于 2011-04-18T14:00:24.533 に答える
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();

フィドル

于 2015-06-25T05:48:30.753 に答える
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 function を介してアクセスできますが、この関数はを渡さない限りgetPriv()戻ります。これは、メイン クロージャ内でのみ認識されます。falsesecret

于 2015-02-02T14:28:19.913 に答える
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 () {
  };
}
于 2008-09-29T10:39:21.760 に答える
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'
于 2013-08-14T18:49:07.407 に答える
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

于 2013-12-10T17:02:29.120 に答える
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"));
于 2015-01-28T11:14:18.203 に答える
0

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

Crockford が好きなのは、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);
    }
}

ですから、私が言ったように、Crockford がそれとこれについての説明を間違っていたことは確かです (しかし、彼のコードは確かに正しいです)。それとも、誰が彼のコードをコピーしているのかを知るために、Javascript の世界をだましているだけなのでしょうか? わかりません...私はブラウザオタクではありません;D

編集

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

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

于 2012-11-11T02:10:44.297 に答える
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
于 2014-01-16T16:18:11.673 に答える
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__);
于 2013-05-17T09:25:15.367 に答える
0

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

于 2011-05-29T19:37:04.127 に答える