JavaScript 设计模式






4.90/5 (16投票s)
JavaScript 现已成为客户端 Web 开发的明显语言。从一开始,浏览器就引入了各种支持访问和修改文档对象模型组件的功能。jQuery 已经将大多数原生功能抽象成新的结构,现在像 KnockOUT 和 BackBone 这样的渐进式库正在更进一步。
然而,如果原生 JavaScript 没有被正确处理,就会出现一些问题。
但是,如果原生 JavaScript 没有得到适当处理,就会存在一些问题。
- 对于变量声明,如果我们忘记了 "var",它就会变成一个全局变量。
- 如果两个函数在不同的 JavaScript 文件中具有相同的名称,则在页面中最后一个包含的文件中的函数将保留下来,而其他函数将丢失。
- 没有 "var" 的变量将在其他 JavaScript 文件的范围内,混合了这些文件中的变量。如果存在大量这样的变量分散在多个文件中,将很难找到用于特定用途的精确变量,通常一个变量会覆盖所有其他变量并崩溃整个状态。
- 具有类似功能的函数在不同的文件(用于不同的目的)中具有相同的名称会发生冲突。代码审查只能识别同一文件中的问题,但如果存在许多此类文件,则查找所有同名函数会造成问题。
模拟类
将所有全局函数名称和变量更改为标签,并将它们置于单个函数范围内。这创建了一个逻辑类;将属性和功能绑定在一起。
// Class wrapping
var anchorChange = {
config : {
colors: ["#F12", "#999"]
},
alterColor: function (link, newColor) {
link.style.backgroundColor = newColor;
},
init : function() {
var self = this; // assign reference to current object to 'self'
var anchors = document.getElementsByTagName("a");
var size = anchors.length;
for (var i = 0; i < size; i++) {
anchors[i].color = self.config.colors[i];
anchors[i].onclick = function () {
self.alterColor(this, this.color); // this is bound to anchor
return false; // prevent navigation
};
}
}
};
<form id="form1" runat="server">
<div>
<ul>
<li><a href="www.nitinsingh.com">My Website</a></li>
<li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
</ul>
<!-- Now call the function, not in the head but below the controls -->
<script type="text/javascript">
anchorChange.init();
</script>
</div>
</form>
这种包装器虽然提供了防止函数和变量混淆的安全性,但它并不能区分公共和私有函数。
模块模式
明确区分公共和私有项目。在直接访问中定义的任何内容都是私有的,在 'return' 块中定义的任何内容都是公共的。
/* Module Pattern */
var anchorChangeMod = function () {
// private property
config = {
colors: ["#F12", "#999"]
}
// private method, available within 'anchorChange' class not outside
function alterColor(link, newColor) {
link.style.backgroundColor = newColor;
}
return {
// all public code inside 'return' block
changeColor: function(link, newColor) {
// Calls private method inside public method
alterColor(link, newColor);
},
init : function() {
var self = this; // assign reference to current object to 'self'
var anchors = document.getElementsByTagName("a");
var size = anchors.length;
for (var i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick = function () {
// this is bound to anchor..
anchorChangeMod.changeColor(this, this.color);
return false; // prevent navigation
}
}
}
};
}();
<form id="form1" runat="server">
<div>
<div>
<ul>
<li><a href="www.nitinsingh.com">My Website</a></li>
<li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
</ul>
</div>
</div>
<script type="text/javascript">
// No constructor, directly call object
anchorChangeMod.init();
</script>
</form>
揭示模块模式
在这种模式中,整个结构在类块中声明,然后在类的末尾,有一个 'return' 块,其中函数被添加到公开的委托中。
/* Revealing Module */
function anchorChangeRev() {
// private property
config = {
colors: ["#F12", "#999"]
}
// private method, available within 'anchorChange' class not outside
function alterColor(link, newColor) {
link.style.backgroundColor = newColor;
}
var changeColor = function (link, newColor) {
alterColor(link, newColor);
}
var init = function () {
var self = this; // assign reference to current object to 'self'
var anchors = document.getElementsByTagName("a");
var size = anchors.length;
for (var i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick = function () {
anchorChangeRev().cc(this, this.color); // this is bound to anchor
return false; // prevent navigation
}
}
}
return {
// delegates to actual methods are marked as public
cc: changeColor,
init: init
}
};
<form id="form1" runat="server"> <div> <ul> <li><a href="www.nitinsingh.com">My Website</a></li> <li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li> </ul> <script type="text/javascript"> anchorChangeRev().init(); </script> </div> </form>
这里第一个是通过其调用函数的变量,第二个是函数委托。这两个函数都将在最终对象中获得公共范围。
懒函数
如果我们有一些功能只需要运行一次(通常用于初始化),而其他功能则需要重复运行,我们应用懒函数。
我们创建一个类,并在其中声明仅执行一次的代码。然后,在相同的范围内,使用要多次运行的函数声明相同的类。
/* Lazy loading */ var anchorChangeLazy = function () { // private property var config = { colors: ["#F12", "#999"] }; var anchors = document.getElementsByTagName("a"); var size = anchors.length; for (var i = 0; i < size; i++) { anchors[i].color = config.colors[i]; anchors[i].onclick = function () { anchorChangeLazy().changeColor(this, this.color); // this is bound to anchor return false; // prevent navigation } } // Redefine function to hold just the changeColor function // which will be invoked multiple times anchorChangeLazy = function () { return { changeColor: function (link, newColor) { link.style.backgroundColor = newColor; } }; }; };
<form id="form1" runat="server"> <div> <ul> <li><a href="www.nitinsingh.com">My Website</a></li> <li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li> </ul> <script type="text/javascript"> anchorChangeLazy(); // just the main function, no subfunction </script> </div> </form>
现在,每当调用类对象时,初始代码中的绑定只执行一次,而 changeColor 处理程序(在新类代码中)可供永久使用。
自定义对象构造函数
我们可以将函数添加到任何对象的 'prototype' 属性。这会将定义注入到已经声明的对象中。因此,初始化函数可以包装在构造函数中,而其他函数则添加到原型绑定中。
/* Custom object constructor */ var anchorChangeCusCon = function() { this.init(); }; // 'prototype' means class definition // any properties can be added to this anchorChangeCusCon.prototype.config = { colors: ["#F12", "#999" ] }; anchorChangeCusCon.prototype.changeColor = function(link, newColor) { link.style.backgroundColor = newColor; }; anchorChangeCusCon.prototype.init = function() { var self = this; var anchors = document.getElementsByTagName("a"); var size = anchors.length; for (var i = 0; i < size; i++) { anchors[i].color = self.config.colors[i]; anchors[i].onclick = function() { self.changeColor(this, this.color); return false; } } } // Call of this function var anchor = new anchorChangeCusCon(); // anchor.init(); Not needed, since this is invoked within the constructor
因此,我们已经发现了如何在自己的 JavaScript 类中为抽象和数据隐藏提供一些 OOP 原则。
享受美化你的客户端代码吧 :)