要创建一个带有公共方法的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的文章几次,但它似乎不像“私有”方法可以被公共方法调用,而“特权”方法可以被外部调用。
在这些情况下,当你有一个公共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
既然每个人都在这里张贴自己的代码,我也要这样做…
我喜欢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的解释是错误的....但他的代码是正确的,所以他还是个好人。:))
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,但不太啰嗦(恕我直言)
这样做有什么问题吗?(我哪儿都没见过)
编辑:我意识到这有点没用,因为每个独立的实例化都有自己的公共方法副本,从而破坏了原型的使用。
我认为这样的问题一次又一次地出现是因为缺乏对闭包的理解。С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')
我希望这能帮助人们更好地理解这个主题
老问题,但这是一个相当简单的任务,可以用核心JS正确解决…没有ES6的Class抽象。事实上,据我所知,类抽象甚至不能解决这个问题。
我们既可以使用老的构造函数,也可以使用Object.create()更好地完成这项工作。让我们先从构造函数开始。这本质上是一个与georgebrock的答案相似的解决方案,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"));
现在显然我们不能从外部访问菜单,但我们可以很容易地重命名餐厅的name属性。
这也可以用object .create()来完成,在这种情况下,我们跳过构造函数,简单地像var rest = object .create(prototypeFactory(menu))那样做,然后像rest.name = name那样将name属性添加到rest对象。