我最近正在阅读《学习 JavaScript 设计模式》这本书。我不明白的是module模式和揭示module模式之间的区别。我觉得他们是同一个东西。任何人都可以举个例子吗?
JavaScript 设计模式:module模式和揭示module模式的区别?
至少有三种不同的方式来实现module模式,但揭示module模式是唯一具有正式名称的module模式后代。
基本module模式
module模式必须满足以下条件:
- 私人成员住在关闭。
- 公共成员在返回对象中公开。
但是这个定义有很多歧义。通过以不同方式解决歧义,您可以获得module模式的变体。
揭示module模式
揭示module模式是最著名和最受欢迎的module模式变体。与其他替代方案相比,它具有许多优点,例如
- 重命名公共函数而不更改函数体。
- 通过修改单行将成员从公共更改为私有,反之亦然,而无需更改函数主体。
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
}
})();
带有对象字面量的module模式
这可能是module模式最古老的变体。与 RMP 不同的是,这个变体没有性感的官方名称。
它除满足原始条件外,还满足以下条件:
- 私有成员在闭包中定义。
- 公共成员在返回对象文字中定义。
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 不同的是,为了 makename
和sayHello
private,指向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!");}
}
})();
带有返回对象存根的module模式
该变体也没有正式名称。
它除满足原始条件外,还满足以下条件:
- 在开头定义了一个空的返回对象存根。
- 私有成员在闭包中定义。
- 公共成员被定义为存根的成员
- 对公共成员的引用是通过存根对象
使用我们的旧示例,您可以看到公共成员直接添加到存根对象中。
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;
})();
概括
揭示module模式和module模式的其他变体之间的区别主要在于如何引用公共成员。因此,RMP 更易于使用和修改,这也是其受欢迎的原因。然而,这些优势付出了巨大的代价(在我看来),Addy Osmani 在他关于揭示module模式的帖子中提到了这一点,
这种模式的一个缺点是,如果一个私有函数引用一个公共函数,那么如果需要补丁,则不能覆盖该公共函数。这是因为私有函数将继续引用私有实现,并且该模式不适用于公共成员,仅适用于函数。
引用私有变量的公共对象成员也受上述无补丁规则注释的约束。
因此,使用揭示module模式创建的module可能比使用原始module模式创建的module更脆弱,因此在使用过程中应小心。
简短的回答,在module模式中,我们在返回对象中定义函数。
在 Revealing Module 模式中,我们在闭包区域定义函数,并且只在返回对象中使用变量名。
这样做可以简化代码并具有许多其他优点
尝试根据热门评论答案做一些事情。如果通过将this传递给私有函数来调用私有函数可能更安全,因此私有函数可以使用此变量引用公共函数。
我使用以下代码创建了一个揭示module模式。
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!
如您所见,我们可以覆盖公共成员。