要使用公共方法创建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的几次写法,但看起来似乎不能通过公共方法调用"私有"方法,并且可以在外部调用"特权"方法.
你可以做到,但缺点是它不能成为原型的一部分:
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
JavaScript使用原型,并且没有像面向对象语言那样的类(或方法).JavaScript开发人员需要在JavaScript中思考.
维基百科报价:
与许多面向对象的语言不同,函数定义和方法定义之间没有区别.相反,区别发生在函数调用期间; 当一个函数作为一个对象的方法被调用时,该函数的local 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,则不需要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
绑定运算符::
是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
在这种情况下,如果你有一个公共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
这是我创建的类,用于理解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
}
我想到了这个:编辑:实际上,有人已经链接到相同的解决方案.咄!
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!'
我认为这些问题一次又一次地出现,因为对闭包缺乏了解.С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')
我希望这有助于人们更好地理解这个主题
就个人而言,我更喜欢以下模式在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
+= 'Buying food at '+this.name+'
'
+ ''
+ '- Restroom count at ' + this.name + ' : '+ this.getRestroomCount() + '
'
+ '- Food count at ' + this.name + ' : ' + this.getFoodCount() + '
'
+ '- Total restroom count : '+ this.getTotalRestroomCount() + '
'
+ '- Total food count : '+ this.getTotalFoodCount() + '
'
+ '
';
}
};
return { // Singleton public properties
create : function(name) {
return new Restaurant(name);
},
printStatus : function() {
document.body.innerHTML
+= '
'
+ 'Overview
'
+ ''
+ '- Total restroom count : '+ Restaurant.prototype.getTotalRestroomCount() + '
'
+ '- Total food count : '+ Restaurant.prototype.getTotalFoodCount() + '
'
+ '
'
+ '
';
}
};
}();
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();
所有这些关闭将花费你.确保测试速度影响,尤其是在IE中.您会发现最好使用命名约定.还有很多企业网站用户被迫使用IE6 ......