这是“ 我最喜欢的JavaScript设计模式”的后续文章,希望能回答它提出的一些问题,并提供有关模式某些功能的更深入的讨论。
那些将我描述的模式与JavaScript模块模式进行比较的人这样做很准确,因为它确实是一种非常相似的构造。 克里斯蒂安·海尔曼(Christian Heilmann)的“ 揭示模块模式” ( Revealing Module Pattern)通过提供更多公开显示方法的灵活性,增加了一个漂亮的额外功能。 (感谢链接!)
但是所有复杂的设计模式都提出了相同的基本问题-首先使用它是否有具体优势? 如果不了解其好处,可能很容易认为这只是一个毫无意义的冗长和过度设计的解决方案,或者只是遵循某种时尚。
但是使用这种模式有真正而重要的好处,它不是试图使JavaScript“更像”任何其他语言,也不是模仿经典继承。 试图充分利用JavaScript固有的继承和作用域功能,并为JavaScript开发所特有的环境问题提供可靠的解决方案。
密封盖
在单个外壳中包装脚本可以大大提高封装效率 。
所有JavaScript作者都必须面对脚本相互冲突的问题。 全局作用域中的任何定义都将覆盖具有相同名称的任何先前的定义或内置对象。 最终,您的脚本可能会破坏另一个脚本,或者被另一个脚本破坏。 但是,当然,作者不一定知道其他对象将与它们同时存在,因此管理它的唯一方法是避免将任何对象置于全局范围内。
在实践中,不可能完全避免这种情况,但是我们可以做的是将脚本包装到单个附件中,从而将脚本必须放入全局范围的对象数量减少到一个。 所有脚本都受益于干净的环境,因此所有脚本都应维护一个脚本。 对于库和第三方工具开发人员而言,这不仅是一种好习惯,对于所有脚本编写也是一个好主意。
命名还是匿名? 公还是私?
匿名闭包本身无法从外部访问。 这有好处吗? 如果仅意味着其他脚本无法访问它们,为什么还要将脚本包装在匿名闭包中?
那就是为什么! 这样其他脚本无法访问它们。 这里重要的用例是第三方工具的开发,这些工具提供了外部功能,但不希望暴露其内部机制。 有些脚本只运行后台任务,根本不需要任何输入。 因此,如果是这样,那么就没有必要提供任何输入-整个脚本可以包装在匿名外壳中,那么全局冲突的可能性就为零。
但是脚本通常提供一些公共功能,这在将公共数据与私有数据分开时很有帮助。 健壮的代码库不应因用户错误而破坏自身,例如将重要属性修改为意外值; 但是,如果所有数据都是公开的,则用户可以轻松地做到这一点。
我过去犯的一个错误是,向脚本提供API来允许用户破坏内部值,仅仅是因为这些值被记录为公共属性。 如果将它们记录为私有变量,则它们将是安全的,因为用户无法从封闭范围之外修改私有变量。
如果您只是为自己,为自己的应用程序或站点编写脚本,那么您可能会认为此类考虑是没有根据的。 如果您控制100%的代码库,那么它的内部交互也都在您的控制之下,并且您可以通过更改名称之一来解决类似名称冲突的问题。 不过,即使在并非绝对必要的情况下,我也倾向于使用这种模式,因为我发现它更易于管理。
这个或那个?
任何函数的封闭范围都可以称为this
,因此,当我们定义一个命名或匿名的附件时, this
是指顶层的附件。 并且它继续从其公共方法中引用该附件。
但是在私有功能中, this
是指直接封闭范围(私有功能),而不是顶级封闭范围。 因此,如果我们希望能够引用顶级范围,则必须创建一个变量,该变量可以从任何地方引用它。 这就是"THIS"
的目的:
function MyScript(){}
(function()
{
var THIS = this;
function defined(x)
{
alert(this); //points to defined()
alert(THIS); //points to MyScript()
}
}).apply(MyScript);
可以将其称为没有其他保留的任何内容。 有人称它为"that"
或"self"
; 我什至尝试使用非英语单词,例如"la"
或"das."
但是最终我还是选择了大写的"THIS"
因为在许多语言中,使用所有大写的常量来声明常量是一个长期的惯例,这似乎是合乎常理的。
每当需要定义私有常量时,我都使用相同的约定:也就是说,私有值在脚本执行的生存期内不会改变。 使用浏览器变量是一个很好的例子:
var
OLDER_WEBKIT = /applewebkit/([0-4]|[5][0-2])/i.test(navigator.userAgent),
KONQUEROR = navigator.vendor == 'KDE';
理想情况下,我们将使用const
而不是var
来声明它们,因为真正的常量使用的内存更少,但是Internet Explorer不支持。
特权值
我最后要看的是使用特权值的好处。 它们实际上只是私有值,可以使用公共方法重新定义。
var options = {
x : 123,
y : 'abc'
};
this.define = function(key, value)
{
if(defined(options[key]))
{
options[key] = value;
}
};
这样做的目的是对何时以及如何定义这些值进行更多控制。 例如,定义函数可以包含详细的验证,该验证将某些成员限制为预定义值的范围,或者仅允许在特定时间或特定顺序对其进行定义。
我在CSSUtilities库中使用了此方法,效果很好, 该库提供了一种定义全局配置的方法(例如,异步执行模式,只能为true或false)。 私有变量与公共定义方法的组合提供了对这些定义的控制,因为用户无法独立于定义函数来重新定义它们,因此不能无效地定义它们。
缩图信用: superkimbo
From: https://www.sitepoint.com/the-anatomy-of-a-javascript-design-pattern/