JavaScript私有方法

要创建一个带有公共方法的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_fooduse_restroom方法调用,但不能由类的用户外部调用?

换句话说,我希望我的方法实现能够做到:

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

但这是行不通的:

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

我如何定义private_stuff作为一个私有方法,使这两个都成立?

我读过道格·克罗克福德的报道几次,但它似乎不像“私有”方法可以被公共方法调用,而“特权”方法可以被外部调用。

444212 次浏览

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

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

你可以像这样模拟私有方法:

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

在这些情况下,当你有一个公共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

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

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

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

如果你想要所有的公共函数和私有函数,并让公共函数能够访问私有函数,可以这样布局一个对象的代码:

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

我想到了这个:编辑:实际上,有人已经链接到一个相同的解决方案。咄!

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!'
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,但没有那么啰嗦(恕我直言) 这样做有什么问题吗?(我在任何地方都没有看到它)

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

私有函数不能使用模块模式访问公共变量

下面是我创建的类,以理解Douglas Crockford在他的网站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


}

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

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

编辑

啊,这就是所有的:'var that = this;'JavaScript的意思?

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

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

  • 使一个公共方法在"Power-constructor"中可访问:(ctx是对象上下文)

    ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
    
  • Now I have this openPrivacy:

    GD.Fabric.openPrivacy = function(func, clss, ctx, _) {
    return function() {
    ctx._ = _;
    var res = clss[func].apply(ctx, arguments);
    ctx._ = null;
    return res;
    };
    };
    
你必须在你实际的构造函数周围放一个闭包,在那里你可以定义你的私有方法。 要通过这些私有方法改变实例的数据,你必须给它们一个"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__);

我认为这样的问题一次又一次地出现是因为缺乏对闭包的理解。Сlosures是JS中最重要的东西。每个JS程序员都必须感受到它的本质。

首先,我们需要创建单独的作用域(闭包)。

function () {


}

在这个领域,我们可以做任何我们想做的事情。没人会知道的。

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
}
}
为了让全世界知道我们的餐厅班, 我们必须从闭包中返回它
var Restaurant = (function () {
// Restaurant definition
return Restaurant
})()

在最后,我们有:

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

此外,这种方法具有继承和模板的潜力

// 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')

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

这是我得出的结论:

需要一类糖代码你可以在这里找到。还支持保护,继承,虚拟,静态的东西…

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

这个呢?

var Restaurant = (function() {


var _id = 0;
var privateVars = [];


function Restaurant(name) {
this.id = ++_id;
this.name = name;
privateVars[this.id] = {
cooked: []
};
}


Restaurant.prototype.cook = function (food) {
privateVars[this.id].cooked.push(food);
}


return Restaurant;


})();
在直接函数的作用域之外,私有变量查找是不可能的。 没有重复的函数,节省内存。

缺点是查找私有变量是笨拙的privateVars[this.id].cooked是荒谬的输入。还有一个额外的“id”变量。

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

我已经创建了一个新工具,允许你在原型上有真正的私有方法 # EYZ0 < / p >

例子:

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

使用自调用函数和调用

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提案是第三阶段。这意味着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 transpiler /编译器

PS:如果你想知道为什么#前缀,读到这

(已弃用)ES7绑定操作符

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

绑定操作符::是ECMAScript的建议在巴别塔实现 (阶段0)。

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


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


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

选择任何遵循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进行AJAX调用。

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

我知道有点晚了,但是这样怎么样?

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

模块模式在大多数情况下是正确的。但是如果您有数千个实例,类可以节省内存。如果需要考虑节省内存,并且您的对象包含少量私有数据,但有大量公共函数,那么您将希望所有公共函数都驻留在.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包含私有属性。它可以通过公共函数getPriv()访问,但是这个函数返回false,除非你将secret传递给它,而这只有在主闭包中才知道。

将所有代码包装在匿名函数中:然后,所有函数将是私有的,只有函数附加到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();

提琴

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

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

就我个人而言,我更喜欢用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(); }
...
};
})();

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


演示

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

参见< >强这个小提琴< / >强

我更喜欢将私有数据存储在相关的WeakMap中。这允许你将你的公共方法保留在原型上。对于大量的对象,这似乎是处理这个问题的最有效的方法。

const data = new WeakMap();


function Foo(value) {
data.set(this, {value});
}


// public method accessing private value
Foo.prototype.accessValue = function() {
return data.get(this).value;
}


// private 'method' accessing private value
function accessValue(foo) {
return data.get(foo).value;
}


export {Foo};

别啰嗦了。这是Javascript。使用命名约定

在es6类工作了多年之后,我最近开始了一个es5项目(使用requireJS,它看起来已经非常冗长了)。我已经反复研究了这里提到的所有策略,基本上都归结为使用命名约定:

  1. Javascript没有像private这样的作用域关键字。其他进入Javascript的开发人员会事先知道这一点。因此,一个简单的命名约定就足够了。用下划线作为前缀的简单命名约定解决了私有属性和私有方法的问题。
  2. 让我们利用原型来提高速度,但不要再啰嗦了。让我们试着让es5“类”看起来像我们在其他后端语言中可能期望的那样(并将每个文件视为一个类,即使我们不需要返回实例)。
  3. 让我们用一个更现实的模块情况来演示(我们将使用旧的es5和旧的requirej)。

# EYZ0

    define([
'tooltip'
],
function(
tooltip
){


function MyTooltip() {
// Later, if needed, we can remove the underscore on some
// of these (make public) and allow clients of our class
// to set them.
this._selector = "#my-tooltip"
this._template = 'Hello from inside my tooltip!';
this._initTooltip();
}


MyTooltip.prototype = {
constructor: MyTooltip,


_initTooltip: function () {
new tooltip.tooltip(this._selector, {
content: this._template,
closeOnClick: true,
closeButton: true
});
}
}


return {
init: function init() {
new MyTooltip();  // <-- Our constructor adds our tooltip to the DOM so not much we need to do after instantiation.
}


// You could instead return a new instantiation,
// if later you do more with this class.
/*
create: function create() {
return new MyTooltip();
}
*/
}
});

ES12私有方法

您现在可以使用Es12私有方法来完成此操作。您只需要在方法名之前添加#

class ClassWithPrivateMethod {
#privateMethod() {
return 'hello world';
}


getPrivateMessage() {
return #privateMethod();
}
}

一个丑陋但有效的解决方案:

function Class(cb) {
const self = {};


const constructor = (fn) => {
func = fn;
};


const addPrivate = (fnName, obj) => {
self[fnName] = obj;
}


const addPublic = (fnName, obj) => {
this[fnName] = obj;
self[fnName] = obj;
func.prototype[fnName] = obj;
}
    

cb(constructor, addPrivate, addPublic, self);
return func;
}


const test = new Class((constructor, private, public, self) => {
constructor(function (test) {
console.log(test)
});
public('test', 'yay');
private('qwe', 'nay');
private('no', () => {
return 'hello'
})
public('asd', () => {
return 'this is public'
})
public('hello', () => {
return self.qwe + self.no() + self.asd()
})
})
const asd = new test('qweqwe');
console.log(asd.hello());

> .

ES2021 / ES12 - Private Methods

.

私有方法名以#开头,可以在定义它的类中访问只有

class Restaurant {


// private method
#private_stuff() {
console.log("private stuff");
}


// public method
buy_food() {
this.#private_stuff();
}


};


const restaurant = new Restaurant();
restaurant.buy_food(); // "private stuff";
restaurant.private_stuff(); // Uncaught TypeError: restaurant.private_stuff is not a function

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

我们既可以使用老的构造函数,也可以使用Object.create()更好地完成这项工作。让我们先从构造函数开始。这本质上是一个类似于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"));

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

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

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

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

2021年这里!

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

如果你检查它,你会发现它很容易实现。在大多数情况下,你不需要做任何奇怪的事情,如使用代理对象,下划线函数(_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

顺便说一句,这是没有使用最新的ES6 ""除了语言。

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

< a href = " https://git。io/JEPGi" rel="nofollow noreferrer">Check my gist . io/JEPGi" rel="nofollow noreferrer">Check my gist