JavaScript私有方法


482

要使用公共方法创建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为私有方法,使两者都适用?

我已经读过Doug Crockford的文章几次,但似乎公共方法不能调用“私有”方法,而外部可以调用“特权”方法。

Answers:


403

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

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

9
在封口内隐藏薄片不会保证所有口译员的隐私。见code.google.com/p/google-caja/wiki/...
迈克·塞缪尔

51
@mikesamuel-是的,但是只有当这些解释器中有错误时才:)
jvenema 2010年

133
好的,这是一个私有方法,但与通常的原型方法相比,它会占用更多的内存,尤其是在创建大量此类对象的情况下。对于每个对象实例,它创建一个绑定到对象而不是类的单独函数。同样,直到对象本身被销毁,这也不会收集垃圾。
阿林丹

4
如果使对象成为McDonalds()从Restaurant()继承的对象,则以这种方式声明时,McDonalds无法覆盖私有方法。好吧,实际上您可以,但是如果其他方法调用私有方法将不起作用,它将调用该方法的原始版本,并且您也不能调用父方法。到目前为止,我还没有找到一种很好的方法来声明可以很好地继承的私有方法。这和性能的影响使这根本不是一个很好的设计模式。我建议使用某种前缀来表示私有方法,例如以下划线开头。
霍夫曼2013年

68
私有方法不应该被覆盖-它们是私有的。

163

使用自调用功能并调用

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, '>>');
}

exports.MyObject = MyObject;

加载文件:

var MyObject = require('./MyObject').MyObject;

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


(实验性)带有Bind运算符的ES7

绑定运算符::是ECMAScript 提议在Babel阶段0)中实现。

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

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

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

加载文件:

import MyObject from './MyObject';

let myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // TypeError: myObject.privateFun is not a function

34
这是最好的答案。私有方法的好处,没有垃圾。
TaylorMac,2014年

1
@TaylorMac除了该.call部分。
pishpish

1
@janje Huh?这就是问题的重点,f()javascript中没有私有的(目前还没有)。
伊夫·

2
@YvesM。问题的关键是选择模拟它的最佳模式。
pishpish

1
@changed拥有隐藏功能(无法从外部调用),漂亮的sintax(否.call)和小内存(实例上没有功能)的最佳折衷方案是什么?那是否存在?
西普里安·托莫阿格(CiprianTomoiagă)2016年

161

您可以像下面这样模拟私有方法:

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


7
我还建议将Douglas Crockford的网站作为私人/公共方法和成员的资源javascript.crockford.com/private.html
Jared

10
他在问题中提到了这一联系。
Gulzar Nazim

8
该方法的缺点是您不能让private_stuff()访问Restaurant中的其他私有数据,而其他Restaurant方法不能调用private_stuff()。好处是,如果您不需要我刚刚提到的任何条件,则可以将use_restroom()保留在原型中。

6
这应该是解决方案和可接受的答案,因为作者显然正在使用prototype属性。
加布里埃尔·拉马斯

23
使用@georgebrock提出的模式,所有私人数据将在所有餐厅对象之间共享。这类似于基于类的OOP中的静态私有变量和函数。我假设OP不会想要这个。为了说明我的意思,我创建了一个jsFiddle
feklee,2012年

35

在这些情况下,当您拥有公共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

IDE的自动完成功能不会检测到这种情况:(
单击Upvote 2011年

19
但这不是类,因此您不能在不同的状态下拥有2个“实例”。
DevAntoine 2012年

删除()部分,您将获得一个“类”。至少在您可以实例化具有不同状态的不同概念的地方。该模块模式虽然占用大量内存,但是……
oligofren 2014年

@DevAntoine查看26个答案中的17个的注释。在JavaScript中,可扩展类和私有方法不容易并存。在这种情况下,我的建议是将继承放在继承之上。使用与封闭的混凝土对象相同的方法创建可扩展的原型。然后,在原型内部,您可以决定何时在具体对象上调用方法。

是否有任何缺点呼吁私营功能的公共变量,像这样:aPrivateMethod = function() { MYLIB.aPublicProperty}
汉娜(Hanna)

21

这是我创建的课程,目的是为了了解道格拉斯·克罗克福德(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

}

5
在那=这是一个相当普遍的模式,通过上述克罗克福德在他的著作通俗化“的Javascript:好部分”
oligofren

8
thatthis当函数绑定到另一个对象时,使用而不是避免范围问题。在这里,您将存储this在其中that并且不再使用它,这与完全不使用它相同。您应该thisthat所有Constructor内部函数(而不是方法声明)进行更改。如果employeeapply编或call编着以某种方式这些方法可能会因为扔this将被错误地约束。
Maroshii

同样,每个实例都将具有私有功能的完整副本,效率很低。除了公共方法不能访问私有类var之外,我还想切换到dart。不幸的是,angulardart是超级beta。
雷·福斯

“构造函数”方法在哪里?在实例化类的构造函数方法时,通常将在哪里执行的逻辑放在哪里?
BadHorsie 2015年

13

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

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!'

1
这是一个很好的技术,但是如何在构造函数中允许参数呢?例如var getAwayVehicle = new Car(100);100速度在哪里,而您要提醒速度。谢谢!
杰森

1
弄清楚了,可以有var Car = function(speed) { this.speed = speed; }`return {构造函数:Car,...`
Jason 2010年

11

我认为由于缺乏对关闭的理解,这些问题一再提出。在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')

我希望这可以帮助某人更好地理解该主题


2
您在第4点中拥有的东西太神奇了!我认为这是所有问题的唯一答案,在这些问题上,您可以获得在原型上使用方法的性能/内存方面的收益,并且这些公共方法对私有成员具有完全访问权限。+1
休顿2013年

7
没用 这里的名称变量的作用类似于静态变量,并由Restaurant的所有实例共享。这是jsbin:jsbin.com/oqewUWa/2/edit?js,输出
Shital Shah

这是一个很好的尝试,但是正如Shital所指出的,name变量是错误的。
oligofren 2014年

2
在此处添加我的2c来重申这是行不通的,看起来不错,但是如上所述,“名称”将用作静态变量,即在所有实例之间共享
Paul Carroll

10

就个人而言,我更喜欢使用以下模式在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();

另请参见此Fiddle


这让我想用ES6类,看看它transpiles太
sheriffderek

9

所有这些关闭都会使您付出代价。确保测试速​​度的影响,尤其是在IE中。您会发现使用命名约定会更好。仍然有很多公司网络用户被迫使用IE6 ...


34
谁在乎,认真吗?
nowayyy 2011年

17
仍在使用IE6的9%并不关心速度,优化和所有现代HTML5功能。因此,关闭不会花费任何费用。
加布里埃尔·拉马斯


7
@LorenzoPolidori w3schools用户!==公司网站用户;]
WynandB 2014年

命名约定(例如,在下划线之前)是可行的方法。代码更易于维护,并且方法仍在原型上定义。不过,如今...我只是在打字稿中将方法标记为私有。
David Sherret'Feb 10'15

5

别那么冗长。它是Javascript。使用命名约定

在es6类中工作了多年之后,我最近开始从事es5项目的工作(使用requireJS看起来已经很冗长了)。我已经遍及这里提到的所有策略,基本上都归结为使用命名约定

  1. Javascript没有范围关键字,例如private。其他使用Javascript的开发人员将对此有所了解。因此,简单的命名约定已绰绰有余。带下划线前缀的简单命名约定解决了私有属性和私有方法的问题。
  2. 出于速度方面的考虑,让我们利用Prototype,但不要再冗长了。让我们尝试使es5“类”的外观与其他后端语言中的期望值尽可能接近(并且即使不需要返回实例,也将每个文件都视为类)。
  3. 让我们用更实际的模块情况进行演示(我们将使用旧的es5和旧的requireJs)。

my-tooltip.js

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

2
应该注意的是,Java语言和任何典型的浏览器主机都没有定义任何依赖于命名约定来“隐藏”私有状态的对象,因此尽管开发人员可以理解这个概念是正确的,但它仍然导致非常不面向对象编程的面向对象方法。
rich remer

我可以要求提供很好的参考吗?这个例子中有些部分对我来说是新的。的define,并且constructor和结构本身。虽然我大部分都同意答案,但是由于我有C#的经验,所以我开始与JS合作,对OOP的影响很大,甚至起步太早。我认为我必须学习这些东西,并且必须了解原型/过程范例。(建议,顺便说一句)
冷地狱犬

1
@ColdCerberus此代码段正在使用es5。您可以在这里看到这种方法的完整图片:gist.github.com/jonnyreeves/2474026。但请记住,你要采取这种做法,将其更新到使用ES6类:googlechrome.github.io/samples/classes-es6和ES6模块(导入/导出语法):hackernoon.com/...
prograhammer

5

您现在可以使用es10私有方法执行此操作。您只需要#在方法名称之前添加一个。

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

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

2
除此之外,这是第3阶段,尚未正式成为语言的一部分。
misterhtmlcss 19/12/28

3

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

请注意,以上代码是所有构造函数类型隐私的通用代码。此处的某些解决方案将失败,但是应该清楚的是,几乎所有基于闭包的解决方案都可以像这样使用不同的replace()参数来破坏。

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

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

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

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

我通常会使用前划线来标记__private_protected方法和字段(Perl样式),但是在JavaScript中具有隐私性的想法只是说明了它是一种被误解的语言。

因此,除了他的第一句话外,我不同意克罗克福德

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


这是一个严重的问题,应该更加众所周知。是否有针对这种攻击的“防御”?
詹姆斯

@James我所不知道的,我认为这是野兽的本质。正如我所指出的,您可以将功能转移到在受保护的环境中运行的服务器上。不过,我想克服的问题是Crockford的解决方案无济于事,不必要地使代码复杂化,并隐藏了对此做某事的必要性。
Fozi

如果用户输入密码,那么他将无法在服务器端进行操作。在某些时候,密码将在“ private”变量中。那么攻击者可以读它吗?我信任自己的代码,无论如何我的房屋标准都不允许eval()。攻击者可能是一些我未正确检查的恶意第三方JavaScript插件或库-因此,是的,我需要检查这些。攻击者也可能是一面像广告的东西,不应与我的代码进行交互。通过将我的所有代码包装在匿名文件中以防止(function () {allMyStuff}());全局暴露,我可以防止这种情况。
詹姆斯

@James这已经开始了,如果您想继续此操作,请打开一个新问题。是的,攻击者可以读取密码。来自您的“私人”变量。或来自DOM。或者,他可以替换AJAX API。或者他用其他东西代替了您的页面。如果他不能执行上述任何一项操作,那么就不需要JS“隐私”,因为他也无法读取您的任何JS变量。关键是,每个人现在都在使用的克罗克福德的“解决方案”并不能解决这个问题。
Fozi 2015年

我认为伪随机代码混淆可能无法抵抗这种攻击-如果您不能依赖具有固定名称的函数,则修改函数主体会更加困难。f.eval('nameOfVariable')当您不知道这'nameOfVariable'是什么时,更难做……
Gershom


2

如果要使用公共功能访问私有功能的所有公共和私有功能,请为这样的对象布置代码:

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

有人可以告诉我为什么这被否决吗?在我看来很好。
thomasrutter

10
每次您执行时new MyObject,的原型MyObject都会替换为相同的值。
bpierre

2
-1。永远不要分配给.prototype内部构造函数。
Bergi 2014年

2
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相似,但略微冗长(IMHO)这样操作是否有问题?(我没在任何地方看到它)

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


2

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


这种方法有一个重要的考虑因素-如果您创建了2个对象,则在内存中将有2个相同的方法(例如,使用PublicFunction)1000个对象将占用您的所有内存。
Artem G 2015年

2

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


它模拟受保护的成员,从其继承的类型也可以访问受保护的成员。我喜欢在一个私人这种模式,以及
HMR

2

那这个呢?

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”变量。


Restaurantundefined因为您没有从匿名函数返回任何内容。
user4815162342'2

在哪里以及如何?假设对创建的餐厅的引用丢失了,则privateVars将不会引用其所有者。参考图是非循环的。我想念什么?
埃文·雷斯

实际上,这是除方法之外还支持私有属性的唯一答案。答案中仅提到了两个问题。
pishpish

我看到了内存泄漏:当实例Restaurant被垃圾回收时,其值仍在内privateVars。在这种情况下,A WeakMap可能是一个很好的替代品Array
Gershom

2

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

小提琴


1

我更喜欢将私有数据存储在关联的中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};

0

私有函数无法使用模块模式访问公共变量


0

由于每个人都在这里发布自己的代码,所以我也要这样做...

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

那么,为什么他要使用“ that = this”呢?它与私有功能完全无关。它与内部功能有关!

因为根据Crockford,这是错误的代码:

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

编辑

嗯,这就是全部内容:'var that = this;'什么?在JavaScript中意味着什么?

因此Crockie的解释确实错了。。。。但是他的代码正确,所以他仍然是个好人。:))


0

通常,我将私有Object _临时添加到该对象。该方法必须在“电源构造器”中完全打开隐私。如果从原型中调用方法,则将能够覆盖原型方法

  • 使“ Power-constructor”中的公共方法可访问:(ctx是对象上下文)

    ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
  • 现在我有了这个openPrivacy:

    GD.Fabric.openPrivacy = function(func, clss, ctx, _) {
        return function() {
            ctx._ = _;
            var res = clss[func].apply(ctx, arguments);
            ctx._ = null;
            return res;
        };
    };

0

这是我得出的结论:

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

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

0
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


0

我创建了一个新工具,使您可以在原型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

1
您能解释一下它的工作原理吗?如何/在哪里可以调用_private的方法?
Bergi 2014年

0

您必须对实际的构造函数进行封闭,在此可以定义私有方法。要通过这些私有方法更改实例的数据,您必须将它们与函数一起使用,或者通过使用.apply(this)调用此函数来为它们提供“ 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__);

实际上,它不起作用。每个new Restaurant人都有自己的restaurant构造函数,“原型”被完全滥用。
Bergi 2014年

@贝尔吉 其实你是对的。它可以工作,但也可以作为资源猪(这样叫吗?)。我对此进行了修改。
Flex Elektro Deimling 2014年

感谢更新。不知道该怎么称呼以前的版本(但“ bug” :-)
Bergi 2014年

0

我知道为时已晚,但是呢?

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

0

这个问题已经有很多答案了,但是没有什么适合我的需求。所以我想出了自己的解决方案,希望对某人有用:

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中使用这种类型的类时,该系统有效。据我了解,上述方法都没有。


1
好奇:您是否真的需要真正公开该功能,但在运行时将其设置为无操作-而不是像所有/大多数其他答案一样将其隐藏在外部调用程序中?如果是这样,为什么?您认为此方法有什么好处?对我来说,这似乎只是不必要的性能开销,不清楚的API,而且很可能会导致调试地狱,但是我一直对新观点

2
@JHH,老实说,回头看时,我几乎掌心。开销通常根本不值得,尽管对我来说这没什么大不了的,因为我没有多次调用这些类。我这样做的原因仅仅是因为它在编写和调用函数的方式上相对比较干净。当时我还不了解符号等,但是现在我明白了,我认为这通常是使用类时要走的路。我正在考虑一起删除此答案。我已经发布了几个愚蠢的答案,但是,嘿,你生活和学习。
thegunmaster '18

感谢您的反馈!我不确定我是否误解了。但是,是的,我们都生活和学习!
JHH

0

请参阅此答案以获取一个干净,简单的“类”解决方案,该解决方案具有私有和公共接口并支持合成

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.