陈童的博客's Archivers

From everyinch on 2014-01-23 16:29:51

ADS2.4 JavaScript中的对象——日志对象

虽然在JavaScript中可以使用警告框来调试应用程序。但在进行某些DOM脚本编程时,很可能会遇到警告框的限制。例如,在研究DOM时,希望看看document对象都包含哪些方法和属性:
[code lang="js"]
for (i in document) { alert(i); }
[/code]
在Firefox中使用上面的循环会生成大约140次警告,那么多的警告框的确非常令人讨厌。现在要创建一个更好的方案,即使用浮动的调试日志随心所欲地记录日志消息。不再使用alert(),而是使用下面的对象:
[code lang="js"]
for (i in document) { ADS.log.write(i); }
[/code]
效果如下图所示:
<img src="http://www.everyinch.net/wp-content/uploads/2014/01/ads2_logwindow.jpg" alt="ads2_logwindow" width="207" height="205" class="alignnone size-full wp-image-4540" />
本示例创建的日志窗口只是定位在窗口中心、固定大小的<ul>元素。

myLogger对象

首先需要创建一个简单的HTML页面,其中包含ADS库和一个命名为myLogger.js的JavaScript文件。本示例通过实例化的ADS.log方法,随时使用ADS.log.write('log message')方法来记录日志信息。基本的框架如下:
[code lang="js"]
function myLogger(id) {

id = id || 'ADSLogWindow';
var logWindow = null;

var createWindow = function () { };
this.writeRaw = function (message) { };

}

myLogger.prototype = {
write: function (message) { },
header: function (message) { }
};

if(!window.ADS) { window['ADS'] = {}; }
window['ADS']['log'] = new myLogger();

if(!console) var console = ADSLog;
[/code]
myLogger对象具有如下特征:
1. myLogger是一个拥有私有、特权以及共有属性和方法的对象
2. myLogger对象也是一个构造函数,需要使用new关键字对其进行实例化
myLogger对象的每个成员都有特定的目的:
□ logWindow是一个私有属性,该属性将在内部被对象用来引用日志窗口的DOM节点
□ createWindow()是一个私有方法,用这个方法在DOM树中创建logWindow节点
□ writeRaw()是一个特权方法,该方法用于向日志窗口添加一条新记录。这个方法之所以是特权方法,是因为如果logoWindow尚未定义,它可以调用私有的createWindow()方法
□ write()是一个共有方法,它会对message中的尖括号进行编码,以便在日志窗口中显示HTML源代码。当message是一个对象并存在toString()方法时,调用对象的toString()方法。最终将编码后的message传递给writeRaw()方法
□ header()是一个共有方法,使用它来向日志窗口添加标题
在上述的代码结构中,共有方法是使用字面量语法定义的:
[code lang="js"]
myLogger.prototype = {
write: function(message) {},
header: function(message) {}
};
[/code]
当然,也可以使用点语法:
[code lang="js"]
myLogger.prototype.write = function(message) {};
header.prototype.write = function(message) {};
[/code]
如果打算向日志对象中添加更多的共有方法,那么使用字面量语法会减少冗余代码:
[code lang="js"]
// 点语法
myLogger.prototype.write = function (message) {};
myLogger.prototype.header = function (message) {};
myLogger.prototype.link = function (link) {};
// 对象字面量
myLogger.prototype = {
write: function (message) {},
header: function (message) {},
link: function (link) {}
};
[/code]

1. myLogger.createWindow()方法

私有的createWindow()方法用来创建日志窗口。因为日志以列表项的形式表现,所以使用无序列表作为日志的容器元素。
[code lang="js"]
createWindow(var createWindow = function () {

// 将日志窗口定位在浏览器窗口的中心
var browserWindowSize = ADS.getBrowserWindowSize();
var top = ((browserWindowSize.height - 200) / 2) || 0;
var left = ((browserWindowSize.width - 200) / 2) || 0;

// 创建作为日志窗口的DOM节点
logWindow = document.createElement('UL');

// 指定它的id
logWindow.setAttribute('id', id);

// 定位日志窗口
logWindow.style.position = 'absolute';
logWindow.style.top = top + 'px';
logWindow.style.left = left + 'px';

// 设置固定的尺寸,并允许滚动
logWindow.style.width = '200px';
logWindow.style.height = '200px';
logWindow.style.overflow = 'scroll';

// 增加一些样式
logWindow.style.padding= '0';
logWindow.style.margin= '0';
logWindow.style.border= '1px solid black';
logWindow.style.backgroundColor= 'white';
logWindow.style.listStyle= 'none';
logWindow.style.font= '10px/10px Verdana, Tahoma, Sans';

// 将其添加到文档中
document.body.appendChild(logWindow);
};
[/code]
createWindow()方法的两个关键部分:
首先,使用DOM方法createElement()创建一个无序列表,并将其赋值给私有的logWindow属性。在createWindow()方法的内部并没有定义logWindow变量,当赋值语句执行时,代码会到外部作用域中查找,于是在函数外部找到了私有的logWindow属性。
第二个关键部分是把logWindow添加到document.body中,这样会使它在浏览器中可见。私有的logWindow属性将维护一个指向相应DOM节点的引用。
createWindow()方法调用了ADS.getBrowserWindowSize()方法,其代码如下:
[code lang="js"]
/**
* 检索浏览器窗口的尺寸
*/
function getBrowserWindowSize() {
var de = document.documentElement;

// 对于大多数浏览器,使用 window.innerWidth
// 对于strict mode的IE,使用 document.documentElement.clientWidth
// 对于quirks mode的IE,使用document.body.clientWidth for MSIE in

return {
'width':(
window.innerWidth
|| (de && de.clientWidth )
|| document.body.clientWidth),
'height':(
window.innerHeight
|| (de && de.clientHeight )
|| document.body.clientHeight)
}
};
window['ADS']['getBrowserWindowSize'] = getBrowserWindowSize;
[/code]

2. myLogger.writeRaw()方法

特权方法writeRaw(),它负责真正地向logWindow添加新条目。writeRaw()方法创建列表项、为其添加样式、生成日志条目,最后将它添加到logWindow中。该方法的代码如下:
[code lang="js"]
this.writeRaw = function (message) {

// 如果不存在日志窗口,则创建它
if(!logWindow) createWindow();

// 创建列表项,并添加样式
var li = document.createElement('LI');
li.style.padding= '2px';
li.style.border= '0';
li.style.borderBottom = '1px dotted black';
li.style.margin= '0';
li.style.color= '#000';
li.style.font = '9px/9px Verdana, Tahoma, Sans';

// 将信息添加到日志节点
if(typeof message == 'undefined') {
li.appendchild(document.createTextNode('Message was undefined'));
} else if(typeof li.innerHTML != undefined) {
li.innerHTML = message;
} else {
li.appendchild(document.createTextNode(message));
}

// 将列表项添加到日志窗口
logWindow.appendChild(li);

return this;
};
[/code]
在创建和添加条目之前,需要先检测以下是否已经创建了日志窗口,如果没有,则调用私有的createWindow()方法。再有就是在为日志节点添加信息的过程中使用了innerHTML,对于innerHTML有一些争议,但innerHTML的确有很多优点:
□ 使用它比使用等价的DOM方法要少很多代码,而且也简单很多
□ 使用它可以使代码更清晰、更易读,因为它允许使用HTML字符串
□ 它的速度更快也更容易实现,因此效率更高
但innerHTML也有不少缺点:
□ 它可能会缺少长远的支持,因为它不是W3C规范中的内容
□ 它不维护对一个新创建的节点的引用
□ 它是一个针对浏览器中HTML的特性,对XML无效
□ 仅仅因为使用它更容易且速度更快,并不意味着就能开发出好的应用程序;很可能知识为自己懒惰找借口而已
下图展示了在ADS.log日志窗口中显示普通信息和<strong>标签的结果:
<img src="http://www.everyinch.net/wp-content/uploads/2014/01/ads2_logwindow1.jpg" alt="ads2_logwindow1" width="207" height="205" class="alignnone size-full wp-image-4548" />

3. myLogger。write()方法和myLogger.header()方法

write()方法只是将writeRaw()方法包装起来,同时执行了一些额外的检测,将小于号和大于号转换成相应的代码。这样,传递到ADS.log.write()中的任何HTML代码都将以源代码形式显示在日志窗口中。使用header()方法可以添加一个标题,如下图所示:
<img src="http://www.everyinch.net/wp-content/uploads/2014/01/ads2_logwindow2.jpg" alt="ads2_logwindow2" width="207" height="205" class="alignnone size-full wp-image-4551" />
为创建共有的write()和header()方法,把下列代码添加到myLogger对象的原型中:
[code lang="js"]
myLogger.prototype = {

/**
* 记录日志的共有方法
* 对message中的尖括号进行编码,以便在日志窗口中显示HTML源代码
* 当message是一个对象并存在toString()方法时,调用对象的toString()方法
* 最终将编码后的message传递给writeRaw()方法
*/
write: function (message) {
// message为空值
if(typeof message == 'string' && message.length==0) {
return this.writeRaw('ADS.log: null message');
}

// 如果message不是String,则试图调用对象的toString()方法
// 如果不存在toString()方法,则简单地记录对象的类型
if (typeof message != 'string') {
if(message.toString) return this.writeRaw(message.toString());
else return this.writeRaw(typeof message);
}

// 将 < 和 > 转换为 <; 和 >
message = message.replace(//g,">");

return this.writeRaw(message);
},


/**
* 输出一行标题
*/
header: function (message) {
message = '' + message + '';
return this.writeRaw(message);
}
};
[/code]
现在就可以将多种不同的信息记录到日志窗口中了。

查看完整版本: ADS2.4 JavaScript中的对象——日志对象

Tags:


©陈童的博客