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


当前回答

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

其他回答

既然每个人都在这里张贴自己的代码,我也要这样做…

我喜欢Crockford,因为他在Javascript中引入了真正的面向对象模式。但他也产生了一个新的误解,那个“那个”。

那么他为什么要用"that = this"呢?这和私人活动完全没有关系。它与内部函数有关!

因为根据Crockford的说法,这是有bug的代码:

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世界,知道谁在复制他的代码?我不知道……我不是浏览器极客

EDIT

啊,这就是问题所在:'var that = this;'在JavaScript中是什么意思?

所以Crockie的解释是错误的....但他的代码是正确的,所以他还是个好人。:))

使用自调用函数和调用

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, ">>");
}
    
module.exports= MyObject;

加载文件:

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

(新!)未来JavaScript版本中的原生私有方法

TC39私有方法和JavaScript类的getter/setter建议是第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:如果你想知道为什么是#前缀,请阅读这篇文章。

(已弃用)ES7绑定操作符

警告:绑定操作符TC39命题接近死亡https://github.com/tc39/proposal-bind-operator/issues/53#issuecomment-374271822

绑定操作符::是一个ECMAScript提议,在Babel(阶段0)中实现。

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

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

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

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

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

我已经创建了一个新工具,允许您在原型上拥有真正的私有方法 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

这样的结局会让你付出代价。确保你在IE中测试了速度的影响。您会发现使用命名约定会更好。仍然有很多企业网络用户被迫使用IE6……