【发布时间】:2014-05-19 08:33:04
【问题描述】:
我最近正在阅读学习 JavaScript 设计模式一书。我不明白的是模块模式和显示模块模式之间的区别。我觉得它们是一样的。谁能举个例子?
【问题讨论】:
标签: javascript design-patterns module-pattern revealing-module-pattern
我最近正在阅读学习 JavaScript 设计模式一书。我不明白的是模块模式和显示模块模式之间的区别。我觉得它们是一样的。谁能举个例子?
【问题讨论】:
标签: javascript design-patterns module-pattern revealing-module-pattern
至少有三种不同的方式来实现模块模式,但显示模块模式是唯一具有正式名称的模块模式后代。
模块模式必须满足以下条件:
但这个定义有很多含糊之处。通过以不同的方式解决歧义,您可以获得模块模式的变体。
显示模块模式是模块模式变体中最著名和最受欢迎的。与其他替代品相比,它具有许多优势,例如
RMP 除了满足原始条件之外,还满足三个附加条件:
下面的例子展示了它是如何使用的
var welcomeModule = (function(){
var name = "John";
var hello = function(){ console.log("Hello, " + name + "!");}
var welcome = function() { console.log( hello() + " Welcome to StackOverflow!");}
return {
name: name,
sayHello: hello,
sayWelcome: welcome
}
})();
如果您想将name 和sayHello 设为私有,您只需在返回对象中注释掉相应的行。
var welcomeModule = (function(){
var name = "John";
var hello = function(){ console.log("Hello, " + name + "!");}
var welcome = function() { console.log( hello() + " Welcome to StackOverflow!");}
return {
//name: name,
//sayHello: hello,
sayWelcome: welcome
}
})();
这可能是模块模式最古老的变体。与贼法牧不同,此变体没有性感的官方名称。
除原件外,还满足以下条件:
this 引用公共成员。在下面的示例中,您可以看到,与 RMP 相比,函数定义实际上是在返回对象字面量中,并且对成员的引用由 this 限定。
var welcomeModule = (function(){
return {
name: "John",
sayHello: function(){ console.log("Hello, " + this.name + "!");}
sayWelcome: function() { console.log( this.hello() + " Welcome to StackOverflow!");}
}
})();
请注意,与 RMP 不同,为了使 name 和 sayHello 私有化,还必须更改各种函数体定义中指向 name 和 sayHello 的引用。
var welcomeModule = (function(){
var name = "John";
var sayHello = function(){ console.log("Hello, " + name + "!");};
return {
//name: "John",
//sayHello: function(){ console.log("Hello, " + this.name + "!");}
sayWelcome: function() { console.log( hello() + " Welcome to StackOverflow!");}
}
})();
这个变种也没有正式名称。
除原件外,还满足以下条件:
使用我们的旧示例,您可以看到公共成员直接添加到存根对象中。
var welcomeModule = (function(){
var stub = {};
stub.name = "John";
stub.sayHello = function(){ console.log("Hello, " + stub.name + "!");}
stub.sayWelcome = function() { console.log( stub.hello() + " Welcome to StackOverflow!");}
return stub;
})();
如果您想像以前一样将name 和sayHello 设为私有,则必须更改对现在私有成员的引用。
var welcomeModule = (function(){
var stub = {};
var name = "John";
var sayHello = function(){ console.log("Hello, " + name + "!");}
stub.sayWelcome = function() { console.log( hello() + " Welcome to StackOverflow!");}
return stub;
})();
显示模块模式与模块模式的其他变体之间的区别主要在于如何引用公共成员。因此,RMP 更易于使用和修改,这也是其受欢迎程度的原因。然而,这些优势需要付出巨大的代价(在我看来),Addy Osmani 在他的 Revealing Module Pattern 帖子中提到了这一点,
这种模式的一个缺点是,如果一个私有函数引用一个公共函数,那么如果需要补丁,则该公共函数不能被覆盖。这是因为私有函数将继续引用私有实现,并且该模式不适用于公共成员,仅适用于函数。
引用私有变量的公共对象成员也受上述无补丁规则注释的约束。
因此,使用 Revealing Module 模式创建的模块可能比使用原始 Module 模式创建的模块更脆弱,因此在使用过程中应小心。
【讨论】:
welcomeModule.name = 'James'; ,console.log(welcomeModule.name) 会返回“James”,但 welcomeModule.sayHello() 会输出“Hello, John”。但是,如果我公开一个像setName = function(newName){ name = newName; } 这样的公共方法并用它更改名称,我仍然会使用consule.log(welcomeModule.name) 得到“John”,即使welcomeModule.sayHello(); 会输出新的更改名称。这究竟是如何工作的? jsfiddle.net/isaacalves/brug6bsu/3
只是尝试根据顶级评论答案做一些事情。如果通过传递 this 来调用私有函数可能会更安全,因此私有函数可以使用 this 变量。
我使用以下代码创建了一个具有启发性的模块模式。
var HTMLChanger = (function () {
var privateFunc = function () {
this.sayHello();
}
var hello = function () {
console.log('say Hello');
}
var callPrivate = function () {
privateFunc.call(this);
}
return {
sayHello: hello,
callPrivate: callPrivate
};
})();
HTMLChanger.callPrivate();
//say Hello
HTMLChanger.sayHello = function() { console.log('say Hi!') };
HTMLChanger.callPrivate();
//say Hi!
如您所见,我们可以覆盖公共成员。
【讨论】:
简短的回答, 在模块模式中,我们在返回对象中定义函数。
在Revealing Module模式中,我们在闭包区域定义函数,并且只在返回对象中使用变量名。
这样做可以简化代码并具有许多其他优势
【讨论】: