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


当前回答

下面是我创建的类,用来理解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

}

其他回答

2021年这里!

这个polyfill有效地隐藏了你的私有属性和方法,当你试图读取你的私有属性时返回undefined,当你试图执行你的私有方法时返回TypeError,从而有效地使它们对外部都是private,但让你通过使用你的公共方法访问它们。

如果你检查它,你会发现它很容易实现。在大多数情况下,你不需要做任何奇怪的事情,如使用代理对象,下划线函数(_myprivate), getter或setter。都不是。唯一需要做的事情是在构造函数中放置一段类似的代码片段,目的是让您向外界公开您的公共接口。

((self) => ({
      pubProp: self.pubProp,
      // More public properties to export HERE
      // ...
      pubMethod: self.pubMethod.bind(self)
      // More public mehods to export HERE
      // Be sure bind each of them to self!!!
      // ... 
 }))(self);

上面的代码就是魔术发生的地方。它是一个IIFE,返回一个对象,其中只包含您想要公开并绑定到第一次实例化的对象的上下文的属性和方法。

你仍然可以访问你的隐藏属性和方法,但只能通过你的公共方法,就像OOP应该做的那样。 将这部分代码视为module.exports

顺便说一句,这是没有使用最新的ECMAScript 2022 #添加到语言中。

'use strict'; class MyClass { constructor(pubProp) { let self = this; self.pubProp = pubProp; self.privProp = "I'm a private property!"; return ((self) => ({ pubProp: self.pubProp, // More public properties to export HERE // ... pubMethod: self.pubMethod.bind(self) // More public mehods to export HERE // Be sure to bind each of them to self!!! // ... }))(self); } pubMethod() { console.log("I'm a public method!"); console.log(this.pubProp); return this.privMethod(); } privMethod() { console.log("I'm a private method!"); return this.privProp } } const myObj = new MyClass("I'm a public property!"); console.log("***DUMPING MY NEW INSTANCE***"); console.dir(myObj); console.log(""); console.log("***TESTING ACCESS TO PUBLIC PROPERTIES***"); console.log(myObj.pubProp); console.log(""); console.log("***TESTING ACCESS TO PRIVATE PROPERTIES***"); console.log(myObj.privProp); console.log(""); console.log("***TESTING ACCESS TO PUBLIC METHODS***"); console.log("1. pubMethod access pubProp "); console.log("2. pubMethod calls privMethod"); console.log("3. privMethod access privProp"); console.log("") console.log(myObj.pubMethod()); console.log(""); console.log("***TESTING ACCESS TO PRIVATE METHODS***"); console.log(myObj.privMethod());

看看我的主旨

一般来说,我将私有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; }; };

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

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

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,但不太啰嗦(恕我直言) 这样做有什么问题吗?(我哪儿都没见过)

编辑:我意识到这有点没用,因为每个独立的实例化都有自己的公共方法副本,从而破坏了原型的使用。

以遵循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调用。