要创建一个带有公共方法的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的文章几次,但它似乎不像“私有”方法可以被公共方法调用,而“特权”方法可以被外部调用。


当前回答

以下是迄今为止我最喜欢的关于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/

其他回答

老问题,但这是一个相当简单的任务,可以用核心JS正确解决…没有ES6的Class抽象。事实上,据我所知,类抽象甚至不能解决这个问题。

我们既可以使用老的构造函数,也可以使用Object.create()更好地完成这项工作。让我们先从构造函数开始。这本质上是一个与georgebrock的答案相似的解决方案,georgebrock的答案受到了批评,因为所有由Restaurant构造函数创建的餐厅都将具有相同的私有方法。我会努力克服这个限制。

function restaurantFactory(name,menu){ function Restaurant(name){ this.name = name; } function prototypeFactory(menu){ // This is a private function function calculateBill(item){ return menu[item] || 0; } // This is the prototype to be return { constructor: Restaurant , askBill : function(...items){ var cost = items.reduce((total,item) => total + calculateBill(item) ,0) return "Thank you for dining at " + this.name + ". Total is: " + cost + "\n" } , callWaiter : function(){ return "I have just called the waiter at " + this.name + "\n"; } } } Restaurant.prototype = prototypeFactory(menu); return new Restaurant(name,menu); } var menu = { water: 1 , coke : 2 , beer : 3 , beef : 15 , rice : 2 }, name = "Silver Scooop", rest = restaurantFactory(name,menu); console.log(rest.callWaiter()); console.log(rest.askBill("beer", "beef"));

现在显然我们不能从外部访问菜单,但我们可以很容易地重命名餐厅的name属性。

这也可以用object .create()来完成,在这种情况下,我们跳过构造函数,简单地像var rest = object .create(prototypeFactory(menu))那样做,然后像rest.name = name那样将name属性添加到rest对象。

我认为这样的问题一次又一次地出现是因为缺乏对闭包的理解。Сlosures是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')

我希望这能帮助人们更好地理解这个主题

模块模式的典范:揭示模块模式

一个非常健壮的模式的简洁的小扩展。

我知道这是一个老话题,但我试图找到一种方法来保持代码的“简单性”的可维护性的目的,并保持轻的内存负载。它有这样的模式。希望能有所帮助。

const PublicClass=function(priv,pub,ro){
    let _priv=new PrivateClass(priv,pub,ro);
    ['publicMethod'].forEach(k=>this[k]=(...args)=>_priv[k](...args));
    ['publicVar'].forEach(k=>Object.defineProperty(this,k,{get:()=>_priv[k],set:v=>_priv[k]=v}));
    ['readOnlyVar'].forEach(k=>Object.defineProperty(this,k,{get:()=>_priv[k]}));
};

class PrivateClass{
    constructor(priv,pub,ro){
        this.privateVar=priv;
        this.publicVar=pub;
        this.readOnlyVar=ro;
    }
    publicMethod(arg1,arg2){
        return this.privateMethod(arg1,arg2);
    }
    privateMethod(arg1,arg2){
        return arg1+''+arg2;
    }
}
// in node;
module.exports=PublicClass;
// in browser;
const PublicClass=(function(){
    // code here
    return PublicClass;
})();

同样的原则适用于老式浏览器:

var PublicClass=function(priv,pub,ro){
    var scope=this;
    var _priv=new PrivateClass(priv,pub,ro);
    ['publicMethod'].forEach(function(k){
        scope[k]=function(){return _priv[k].apply(_priv,arguments)};
    });
    ['publicVar'].forEach(function(k){
        Object.defineProperty(scope,k,{get:function(){return _priv[k]},set:function(v){_priv[k]=v}});
    });
    ['readOnlyVar'].forEach(function(k){
        Object.defineProperty(scope,k,{get:function(){return _priv[k]}});
    });
};

var PrivateClass=function(priv,pub,ro){
    this.privateVar=priv;
    this.publicVar=pub;
    this.readOnlyVar=ro;
};
PrivateClass.prototype.publicMethod=function(arg1,arg2){
    return this.privateMethod(arg1,arg2);
};
PrivateClass.prototype.privateMethod=function(arg1,arg2){
    return arg1+''+arg2;
};

为了减轻公共类的冗长和负载,将此模式应用于构造函数:

const AbstractPublicClass=function(instanciate,inherit){
    let _priv=instanciate();
    inherit.methods?.forEach(k=>this[k]=(...args)=>_priv[k](...args));
    inherit.vars?.forEach(k=>Object.defineProperty(this,k,{get:()=>_priv[k],set:v=>_priv[k]=v}));
    inherit.readonly?.forEach(k=>Object.defineProperty(this,k,{get:()=>_priv[k]}));
};

AbstractPublicClass.static=function(_pub,_priv,inherit){
    inherit.methods?.forEach(k=>_pub[k]=(...args)=>_priv[k](...args));
    inherit.vars?.forEach(k=>Object.defineProperty(_pub,k,{get:()=>_priv[k],set:v=>_priv[k]=v}));
    inherit.readonly?.forEach(k=>Object.defineProperty(_pub,k,{get:()=>_priv[k]}));
};

使用:

// PrivateClass ...
PrivateClass.staticVar='zog';
PrivateClass.staticMethod=function(){return 'hello '+this.staticVar;};


const PublicClass=function(priv,pub,ro){
    AbstractPublicClass.apply(this,[()=>new PrivateClass(priv,pub,ro),{
        methods:['publicMethod'],
        vars:['publicVar'],
        readonly:['readOnlyVar']
    }]);
};
AbstractPublicClass.static(PublicClass,PrivateClass,{
    methods:['staticMethod'],
    vars:['staticVar']
});

PS:这种方法的默认(大多数时候可以忽略不计)是,与完全公共相比,它可以占用很小的计算负载。但只要你不使用它与高度请求类应该是可以的。

你必须在你实际的构造函数周围放一个闭包,在那里你可以定义你的私有方法。 要通过这些私有方法改变实例的数据,你必须给它们一个"this",要么作为函数参数,要么用.apply(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__);