两个 JavaScript 面向对象的方法

[b][size=large]准备工作[/size][/b]

为了演示或者您试验,请先准备好下面的 HTML 模板。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
body, body * {
font: 10pt Arial;
}

.tbl {
border-style: solid;
border-width: 1px;
width: 500px;
table-layout: fixed;
}

.tbl thead {
background-color: #555;
color: #FFF;
}

.tbl tbody td {
border-style: solid;
border-width: 1px;
}
</style>
<script type="text/javascript">
</script>
</head>
<body>
<input type="button" value="Insert A Row" /><br />
<input type="text" id="indexText" />
<input type="button" value="Remove A Row" /><br />
<table class="tbl">
<thead>
<tr>
<td>Col A</td>
<td>Col B</td>
</tr>
</thead>
<tbody id="tblbody">
</tbody>
</table>
</body>
</html>

这个页面包含一个两列的表格。"Insert A Row" 这个按钮意在向此表中添加一行。"Remove A Row"意在从此表中删除一行。在文本框中指定要删除的行号(从一开始索引)。

[b][size=large]第一种,基于单个对象[/size][/b]


var tableObj = (function() {
var count = 0;

var validateIndex = function(index) {
if (index >= 1 && index <= count) return true;
return false;
};

return {
addRow: function(a, b) {
if (!a) a = "";
if (!b) b = "";
var tblBody = document.getElementById("tblbody");
var newRow = document.createElement("tr");
var colA = document.createElement("td");
colA.innerText = a;
var colB = document.createElement("td");
colB.innerText = b;
newRow.appendChild(colA);
newRow.appendChild(colB);
tblBody.appendChild(newRow);

count += 1;
},

removeRow: function(index) {
if (!index) index = 0;
if (!validateIndex(index)) {
throw "Index out of range: " + index;
return;
}
var tblBody = document.getElementById("tblbody");
var row = tblBody.childNodes[index];
tblBody.removeChild(row);

count -= 1;
}
};
})();

这种方法被称为块模式(Module Pattern)。最后的“()”会导致那个匿名函数立即执行,从而返回 return 块的对象。这个返回的对象被赋给了 tableObj。其中,count 为 tableObj 的私有成员变量,validateIndex() 函数为其私有成员函数。

为了看看效果,将上面的代码放在 <script> 标签之中。然后为“Insert A Row”按钮添加 οnclick="insertARow()",为“Remove A Row”按钮添加 οnclick="removeARow()"。最后,在 <script> 标签中再加入下面的两个函数,

function insertARow() {
tableObj.addRow("hi", "hello");
}

function removeARow() {
var index = document.getElementById("indexText").value;
if (/^\d+$/.test(index)) {
index = index / 1;
} else {
alert("Not a number");
return;
}
try {
tableObj.removeRow(index);
} catch (err) {
alert(err);
}
}

这样,按钮的事件响应函数就添加完成了。现在您打开页面,应该就可以试验一下效果了。这种方法的优点是短平快,缺点是复用性较差。当然,这里指的不是 copy-paste 式的复用 :)

假设有这样一个用例,还是这个页面,现在我需要再添加一个结构一样的表。难道要我们把上面的代码复制一遍吗?当然不。如果能支持以 new 的方法创建的话是最理想的。下面将介绍的方法基于类型,即构造函数。它是可以支持以 new 的方式创建对象的。

[b][size=large]第二种,基于类型[/size][/b]


var TableClass;
(function() {
/* Private static members
*/
var _debug = function(src, msg) {
if (window.console) {
window.console.log("[TableClass] " + src + ": " + msg);
}
};

// Constructor
TableClass = function(tbodyId) {
TableClass.instances.push(this);

/* Private member fields
*/
var count = 0;

/* Private member functions
*/
var validateIndex = function(index) {
_debug("validateIndex()", "count: " + count);
if (index >= 1 && index <= count) return true;
return false;
};

/* Initializer
*/
(function() {
TableClass.instances.push(this);
})();

/* Public member funcitons
*/
this.addRow = function(a, b) {
_debug("addRow()", "count: " + count);
if (!a) a = "";
if (!b) b = "";
var tblBody = document.getElementById(tbodyId);
var newRow = document.createElement("tr");
var colA = document.createElement("td");
colA.innerText = a;
var colB = document.createElement("td");
colB.innerText = b;
newRow.appendChild(colA);
newRow.appendChild(colB);
tblBody.appendChild(newRow);

count += 1;
};

this.removeRow = function(index) {
_debug("removeRow()", "count: " + count);
if (!index) index = 0;
if (!validateIndex(index)) {
throw "Index out of range: " + index;
return;
}
var tblBody = document.getElementById(tbodyId);
var row = tblBody.childNodes[index];
tblBody.removeChild(row);

count -= 1;
}
};

/* Public static members
*/
TableClass.instances = [];
})();

这样的模式目前我还不知道是否有正式的名称。不过就目前我所知道的,网上各位作者仍然把类似这种基于类型的方法称为块模式。我认为,上面的模式就实现的功能来说是最优秀的。因为它能实现私有成员、私有静态、公有成员及公有静态。我将其称为类模式(Class Pattern)。

注意,公有成员函数 addRow() 和 removeRow() 在代码中使用了一个私有成员变量 tbodyId。而只有 TableClass 的构造函数带一个 tbodyId 的参数。就是这个 tbodyId 充当了私有成员变量。如果您觉得这样不好看,也可以在构造函数中加上这样一句话,
var _tbodyId = tbodyId;

然后把那两处引用 tbodyId 的地方改成 _tbodyId。看您的喜好了。

将第一个例子改造,添加一个结构相同的表。首先添加 HTML,

<table class="tbl">
<thead>
<tr>
<td>Col A</td>
<td>Col B</td>
</tr>
</thead>
<tbody id="tblbody2">
</tbody>
</table>

然后初始化两个表对象,

var tableObj = new TableClass("tblbody");
var table2Obj = new TableClass("tblbody2");

最后改造按钮的事件响应函数,

function insertARow() {
tableObj.addRow("hi", "hello");
table2Obj.addRow("hello", "hi"); // added this row.
}

function removeARow() {
var index = document.getElementById("indexText").value;
if (/^\d+$/.test(index)) {
index = index / 1;
} else {
alert("Not a number");
return;
}
try {
tableObj.removeRow(index);
table2Obj.removeRow(index); // added this row.
} catch (err) {
alert(err);
}
}

OK。现在如果您点击“Insert A Row”应该可以看到两列内容相反但结构一致的表了。为了验证我们的公共静态成员 instances 是否有效,创建一个单独的按钮,
<input type="button" value="Count table instances" onclick="alert(TableClass.instances.length)" /><br />

点击“Count table instances”,结果会显示“2”。

[b][size=large]题外话,命名空间[/size][/b]

为了避免全局名字污染,通常我们写的控件都会放一个全局名称之下。像 YUI 的 YAHOO,jQuery 的 jQuery($),DWR 的 dwr。比如我在 GE,写的控件可能就会以 ge. 开头。命名空间实际上就是借助对象的嵌套来实现,比如

if (!dwr) var dwr = {};
if (!dwr.util) dwr.util = {};

dwr.util.escapeHtml = function(...) { ... };


[b][size=large]题外话,选项[/size][/b]

通常一个函数有可选参数的时候,大家会习惯性地将其放在函数签名的末尾。但如果可选参数比较多就不好看了。可以通过这样的方式来提供可选参数,

function fun(param1, optionalParams) {
if (!optionalParams) optionalParams = {};
...
if (optionalPrams.timeout) {
...
}
}

fun 函数带一个 param1 参数和一个 optionalParams 可选参数。这个 optionalParams 实际上代表了一个可选参数的集合。比如我可以这样调用,
fun("param1", { timeout: 1000 });

DWR 使用类似这样的方式来实现功能丰富的回调,而 jQuery 无疑是这方面最强大的库。结合 jQuery 的变量继承,可选参数的实现变得非常简单。因为通过继承,可选参数的默认值可以很容易地指定。

[b][size=medium]参考[/size][/b]

[list]
[*][url=http://yuiblog.com/blog/2007/06/12/module-pattern/]A JavaScript Module Pattern[/url]
[*][url=http://foohack.com/2007/08/yui-crockford-module-pattern-vs-prototypes-class-function/]YUI’s “Module Pattern” vs. Prototype’s Class Function[/url]
[*][url=http://blog.trendics.com/development/advantages-of-using-the-module-pattern-javascript-classes/]Advantages of using the Module Pattern to Structure Javascript Classes[/url]
[/list]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值