要创建一个带有公共方法的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创建类的以下模式:

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(); }
        ...
    };
})();

如您所见,它允许您定义类属性和实例属性,每个属性都可以是公共的和私有的。


Demo

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();

看看这小提琴。

其他回答

关于这个问题已经有很多答案了,但是没有一个符合我的需求。所以我提出了自己的解决方案,希望对别人有用:

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中使用这种类型的类时,这个系统可以工作。据我所知,上面评论的方法都没有。

一般来说,我将私有Object _临时添加到对象中。 您必须在该方法的“Power-constructor”中显式地打开隐私。 如果您从原型中调用该方法,您将会 能够覆盖原型方法

使一个公共方法在"Power-constructor"中可访问:(ctx是对象上下文) ctx。test = GD.Fabric。(“测试”,GD.Test开放。原型,ctx, _);//是私有对象 现在我有这个openPrivacy: GD.Fabric.openPrivacy = function(func, clss, ctx, _) { 返回函数(){ ctx。_ = _; Var res = clss[func]。应用(ctx、参数); ctx。_ = null; 返回res; }; };

你可以这样做,但缺点是它不能成为原型的一部分:

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();
    }
}

以遵循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); }; "
));

注意,上面的代码对于所有构造函数类型-privacy都是通用的。这里的一些解决方案会失败,但应该清楚的是,几乎所有基于闭包的解决方案都可以通过不同的replace()参数像这样被破坏。

在此执行之后,任何用new Foo()创建的对象都将有一个eval方法,可以调用该方法来返回或更改构造函数闭包中定义的值或方法,例如:

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

我能看到的唯一问题是,它不会工作的情况下,只有一个实例,它是在加载时创建的。但这样就没有理由真正定义原型了,在这种情况下,攻击者可以简单地重新创建对象,而不是构造函数,只要他有方法传递相同的参数(例如,它们是常量或从可用值计算出来的)。

在我看来,这使得Crockford的解决方案毫无用处。由于“隐私”很容易被打破,他的解决方案的缺点(可读性和可维护性降低,性能下降,内存增加)使得基于“无隐私”原型的方法成为更好的选择。

我通常使用前导下划线标记__private和_protected方法和字段(Perl风格),但在JavaScript中拥有隐私的想法只是表明它是一种被误解的语言。

因此,除了Crockford的第一句话外,我不同意他的观点。

那么如何在JS中获得真正的隐私呢?将所有需要私有的东西放在服务器端,并使用JS进行AJAX调用。

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