原创作者: abruzzi   阅读:4708次   评论:4条   更新时间:2011-06-01    

 

第十一章 客户端的JavaScript

毫无疑问,到目前为止,JavaScript应用最为广泛,也最为成功的领域就是客户端,或者称为浏览器上的JavaScriptJavaScript为页面开发注入了活力,如与服务器交互形成的局部刷新,鼠标事件的响应,动态的页面风格变换等等,都直接依靠与JavaScript的支持。

11.1客户端JavaScript执行环境

我们在基础部分提到过,JavaScript代码都有一个执行环境,所有的JavaScript代码均被包含在一个全局对象中,比如在所有函数之外声明:

 

 

var x = 3;
var str = "global";
 

 

这两个变量声明语句事实上是为全局对象添加了两个属性xstr。可以将全局对象想象成一个大的匿名自执行函数:

 

 

(function(){
    var x = 3;
    var str = "global";
    //...
})();

 

 

 

在浏览器端,这个全局的对象称为windowwindow是所有JavaScript对象的根,我们可以通过window对象的属性document来访问页面本身,也可以调用window的一些方法来与用户交互,比如:

 

window.alert("This is a message");
alert("This is a message");
 

 

 

这两个语句事实上的效果是相同的,在引用全局对象window的方法时,我们可以忽略前缀,只使用方法名本身。

window对象是对浏览器当前窗口的引用,因为浏览器会将window与自身绘制出来的窗口绑定起来,我们对window的操作事实上会映射到浏览器窗口上。正是浏览器本身提供了这种脚本化的能力,我们才有机会通过JavaScript代码来完成诸如改变页面标题,弹出警告框,修改页面内容(通过对window.document的修改)等操作。

11.2文档对象模型(DOM)

DOM即文档对象模型,它是一个平台,提供语言无关的API,允许程序访问并更改文档的内容,结构以及样式。HTML文档是一个树形的结构,与浏览器中的页面中的结构一一对应。


W3C站点中的一个HTML结构示例

 

JavaScript通过修改/遍历DOM,即可映射到对WEB页面的操作上,比如一个简单的页面如下:

 

<html>
    <head>
    </head>
    <body>
       <div id="con">
       </div>
    </body>
</html>
 

 

 

通过JavaScript操作DOM

 

var con = document.getElementById("con");

 

 

 

即可对应到WEB页面中的id”con”div标签,可以为该标签设置背景色,或者绑定click事件的处理函数等等。JavaScript可以通过一些浏览器内置的方法来对DOM进行遍历,增加,删除DOM的子节点,访问DOM上的FormFrameImage等节点,修改他们的CSS样式,注册/注销事件处理函数,从而使页面“活动”起来。

11.3事件驱动模型

由于客户端JavaScript的开发属于用户界面开发的范畴,因此使用事件驱动模型就显得非常自然了,事件驱动的特点在于:代码块的运行与否与程序流程无关。而传统的流式代码的特点是,从函数的入口进入,依次调用各个子模块的处理函数,最后退出。这种编程模式主要适用于与UI关系不大的场合,很少有异步的过程,这些特点可能要追溯到计算机程序的最初模型:批处理。

而 事件驱动模型主要是面向用户的,你在实现无法知道用户会如何使用你的程序,因此就只能通过回调,事件监听等方式,当用户做出某个动作时才会触发之前已经注 册好的监听器,从而执行相关代码,而不是顺序的执行。甚至在一次运行中,部分代码始终没有被触发,也就根本不会被执行到。

比如在页面中,我们为按钮的点击事件注册了事件监听器,当点击的时候弹出一个对话框,但是用户打开页面后,浏览完内容后就直接关闭了,没有点击按钮,那么相关的代码就没有被触发,我们来看一个简单的示例:

 

我们有一个页面,内容如下:

 

<html>
<head>
<style>
body{
    margin: 20px;
    font-family : "Verdana";
    font-size : normal;
    background-color : #eee;
}
</style>
</head>
<body onload="init()">
    <p>
        <label for="toclick">Please click the button:</label>
        <input id="toclick" type="button" value="Click me" />
    </p>
</body>
</html>
 

 

 


页面click-me的展示

 

可以看到body标签的onload属性被赋予一个值”init()”,这是一个对JavaScript代码的调用,时机发生在当页面加载完成之后,也就是浏览器已经创建了所有body中的DOM对象之后。

 

我们来看看这个init函数的内容:

 

<script type="text/javascript" language="javascript">
function init(){
    var button = document.getElementById('toclick');
    button.onclick = function(){
        alert('button is clicked');
    }
}
</script>
 

 

 

这段代码先从document中获取id”toclick”的元素(也就是我们刚才在html中声明的button),然后为其click事件绑定一个函数,当click事件被触发时,弹出一个警告框。

 


警告框效果

 

11.4 与服务器端交互(Ajax)

      Ajax本身被作为前端技术,提出的时间是比较早的,但是由于种种原因,没有引起人们的普遍关注,而当Google的很多产品如Google MapGMail等横空出世的之后,Ajax才被越来越多的公司所接受并引入到自己的产品中。现在几乎所有的网站都有不同层次的Ajax交互,纯静态的页面已经非常少了。

      简而言之,Ajax表示异步JavaScriptXML,事实上,AjaxXML几乎没有任何关系,因为是异步交互,所以用户的页面不用刷新,在同一个页面中,客户端请求服务数据,当服务数据返回时,通过JavaScript将数据片段填充到页面的某个部分,即实现了局部刷新,这个过程对用户来说实际上是透明的。

      Ajax对服务器端没有限制,一个Ajax请求和普通的请求一样,发送数据(GET/POST)到一个服务端URL上。服务端的实现可以是php,可以是ASP或者JSP等。以J2EE为例,数据发送到servletservlet对请求进行处理,最后向输出流写入数据,浏览器得到这些数据之后,会按照Ajax调用时的注册情况来回调JavaScript函数,JavaScript在操作DOM上具有天生的优势,因此可以很方便的在无刷新的情况下更新页面的部分或者全部。

     

下面我们来看一个具体的实例,通过异步的向后端的php脚本发送请求,页面的内容得到了局部更新,而无需重刷整个页面。首先编写一个简单的php脚本:

 

<?php
 
$type = $_REQUEST['type'];
$string = $_REQUEST['string'];
 
if($type == 0){
    echo strtoupper($string);
}else{
    echo strtolower($string);
}
 
?>
 

 

 

php的逻辑很简单,请求中包含两个参数,typestring,如果type0,则将第二个参数string转换为大写返回,否则转换为小写返回。

 

 

XMLHttpRequest对象是进行Ajax的核心,所有的主流浏览器都已各自不同的方式进行了实现,如果不使用客户端JavaScript框架,那么使用起来会有所差异:

 

function initxhr(){
    if(window.XMLHttpRequest){
        xhr = new XMLHttpRequest();
    }else if(window.ActionXObject){
        xhr = new ActiveXObject("Msxml2.XMLHTTP");
    }else{
        throw new Error("xhr is not supported");
    }
}

 

 

 

创建了XMLHttpRequest对象之后,我们即可使用它来进行与服务端的异步通信:

 

function doajax(url, panelId){
    if(xhr == null){
        initxhr();
    }
 
    if(xhr != null){
        xhr.open("GET", url, true);
        xhr.onreadystatechange = updatePanel(panelId);
        xhr.send(null);
    }else{
        throw new Error("xhr is not inited");
    }
}

 

 

 

通过设置XMLHttpRequest对象的onreadystatechange属性,当服务端产生响应时,浏览器会回调此处注册的函数:

 

function updatePanel(panelId){
    return function(){
        if(xhr.readyState == 4){
            var response = xhr.responseText;
            alert(response);
            document.getElementById(panelId).innerHTML = response;
        }
    }
}

 

 

 

运行结果如下:

 


Ajax示例

11.5调试

在实际的开发环境中,不可能做到代码没有bug,变量的拼写错误,使用了未经初始化的变量,死循环等等,这些都是比较容易解决的错误,但是如果有隐藏的比较深的bug,时隐时现,我们就需要一个调试环境了。脚本语言的调试尤其艰难,因为一切都是运行时决定的,因此不可能预先知道代码有错误(不包括显式的词法错误)

我们这一节中介绍集中浏览器中的调试JavaScript的方式:

11.5.1 FireFox

FireFox以速度,可扩展性,标准性深受广大开发人员的喜爱。FireFox具有强大的插件机制,用户可以自己为FireFox增添新的功能(很多插件只需要有JavaScript/CSS/XUL经验即可),而WEB开发人员最喜爱的插件之一即为:FireBugFireBug是一个FireFox的插件,使用它,使前端开发人员丢弃了老旧的alertFireBug提供一个控制台,开发人员可以直接像在windows的控制台那样,通过简单的命令来查看变量的值,状态等。

作为一个调试工具,FireBug提供如其他IDE中那样的基本功能,断点,步进,watch等等功能,下面我们来详细介绍FireBug


 

通过在提示符”>>>”之后键入JavaScript代码即可执行这些代码,由图可见FireBug6个标签:控制台,HTML查看器,CSS查看器,脚本查看器,DOM查看器,以及网络性能监控。


HTML查看器标签

 

FireBug处于“查看”状态时,通过鼠标在页面上移动,页面的一部分区域会高亮,点击此高亮区域即进入查看状态,注意此时FireBugHTML查看窗口中的内容:它会高亮显示当前选中区域的HTML代码,而且你可以动态的编辑这些标签的属性和样式,对于页面大小,风格的微调十分有用。

 


FireBug DOM查看器

 

通过DOM查看器,我们可以清晰的看到页面中所有JavaScirpt对象,而且是以树的形式,可以逐层点开,查看详情,这对于调试页面非常有用。

 


FireBug的调试界面

 

通过使用FireBug,我们就可以丢弃alert方式的调试了,FireBug的调试与其他的IDE的使用非常类似,通过在代码左侧的行号旁边单击打断点,然后刷新页面进入调试环境,可以单步执行,直接返回等。右侧的面板上包含watch,栈情况,以及关于所有断点的信息,调试起来非常方便。

 

FireBug的控制台中常用的一些命令:

 

记录日志,一般用于调试时将JavaScript字符串打印出来

console.log(object[, object, ...])
 

 

 

打印消息,用于调试

console.info(obj)
 

 

 

用于打印一个对象,将JavaScript对象的各个属性树以直观的形式展现。

console.dir(obj)
 

 

 

console.trace()
 

 

 

11.5.2 Chrome

ChromeGoogle的浏览器,渲染引擎为苹果公司(Apple)的开源项目WebKit,而JavaScript引擎为V8,根据作者经验,Chrome为当前浏览器界综合性能最好的一款浏览器,无论是速度,资源的占用,响应时间,以及界面,可扩展性等方面,远远的超过了同侪。

Chrome一开始就附有开发人员工具,前端开发人员可以使用这个工具进行类似与FireBug那样对代码进行调试,同时可以动态增删HTML属性,查看页面响应时间,以便找出系统的瓶颈等。

FireBug一样,Chrome的内置开发人员工具也可以查看JavaScript的运行时结构,我们可以通过下面的一些图例来看Chrome的调试工具的用法:

      chrome中,CTRL-SHIFT-I启动开发人员工具:


chrome开发人员工具

 

console标签中,可以进行一些脚本的测试:

 


chromeconsole界面

 

在实际的项目中,console系列的方法非常有用,特别是web容器如tomcat等返回的是复杂的JavaScript对象时,console.dir(object)可以直观的让开发人员看到对象的结构,非常便于调试。

11.6客户端的MVC

MVC模型是在实际应用中使用的较多的一种模式,它在很大程度上降低了整个应用开发的复杂度。在J2EE应用中,使用了MVC的框架不计其数,比如structs,JSF等等。当然MVC作为一种应用程序的模型并不限制其使用的场景,比较著名的Swing工具包也是建立在MVC模型上的。

MVC模型中,应用被分为三个功能块,M表示模型(Model),通常为后台的数据;V表示视图(View),表示数据的展现,如Web页面或者2D库渲染出来的其他UI组件,而C表示控制器(Controller),负责逻辑部分的控制,协调模型和视图的关系。使用这个模型,可以降低个层次之间的耦合度,使得软件可以较大程度上得到重用。

一般而言,MVC是构建在整个系统中的,比如应用的数据来自于远程数据库或本地的文件系统,视图则在前端,直接展示给用户,控制器部分则运行在容器端。我们这个小节要讨论的并不是这个结构,而是纯粹建立在前端的MVC


MVC模型示意图

 

 

既然MVC模型的目的是降低层次间的耦合,降低开发的复杂度,使得软件尽量的到重用,我们不妨建立这样一套规则:

 

<!--[if !supportLists]-->l  <!--[endif]-->HTML表示模型

<!--[if !supportLists]-->l  <!--[endif]-->CSS来负责渲染视图

<!--[if !supportLists]-->l  <!--[endif]-->JavaScript负责控制前两者

 

HTML中,只是将文档的结构和内容组织起来,通过CSS进行渲染,布局等工作,而与用户交互的部分则由JavaScript来控制,我们来看这样一个例子:我们有一个页面,其中有一个panel,当点击这个panel时其背景色会发生变化。

      这个HTML文件看起来应该是这样的:

 

<html>
	<head>
		<script src="jquery-1.3.2.js" type="text/javascript" ></script>
		<script src="controller.js" type="text/javascript"></script>
		<link rel="stylesheet" href="style.css" type="text/css" />
	</head>
	<body>
		<div class="contentGray" id="content">
			This is the content of a panel
		</div>
	</body>
</html>

 

 

它引入了两个脚本文件(控制器)和一个样式表文件(视图风格),而HTML本身只不过定义了div的结构和其位于body中的层次关系。

我们的视图渲染部分:样式表的内容是这样的:

 

.contentGray{
	background : #ccc;
	color : blue;
	border : 1px solid black;
	font: 13px 'Courier New';
	width : 300px;
	height : 30px;
	padding-top : 10px;
	padding-left : 10px;
}

.contentDark{
	background : #666;
	color : white;
	border : 1px solid black;
	font : 13px 'Courier New';
	width : 300px;
	height : 30px;
	padding-top : 10px;
	padding-left : 10px;	
}

 CSS文件定义了类(contentGray)的样式及另外一个类(contentDark)的样式,这样HTML文件就可以看起来比较漂亮:



我们的需求是,当我们点击这个panel的时候,它的背景色加深,文字变成白色,当再次点击的时候又恢复之前的颜色和背景色。这种“动态”的就交给JavaScript来处理了:

 

$(document).ready(function(){
	$("div#content").click(function(){
		$(this).toggleClass("contentDark");
	})
});

 

使用jQuery可以为我们带来很多便利,我们选择idcontentdiv,然后注册事件处理函数,当click事件发生的时候,我们就切换CSScontentDark


 

当然,这个只是一个简单的例子,如果你的应用所涉及的页面比较多,而且控制器部分(JavaScript脚本)的工作量比较大,那么你会发现,客户端的MVC模型对你有很大的帮助,不但是界面的统一风格,而且JavaScript代码会更加模块化,从而可能一定程度上提高应用程序的效率,同时降低维护的难度。

 

11.7 Javascript/Ajax框架

随着富客户端的流行,基于WEB的应用越来越多,人们在开发过程中不再满足于DOM提供的简单API,特别是DOM对页面的操作比较繁琐,而且容易出错。当页面越来越华丽,页面UI越来越复杂(事件处理,特效处理)的时候,就有大量第三方JavaScript框架被开发出来了,比如较早的prototypedojo,以及yahooYUI,后来的jQuery,以及jQueryUI插件jQuery-UI,最早基于YUI而后来又进行了重构的ExtJs,号称纯OOMootools等等。

这些JavaScript框架的开发,大大的简化了页面的开发速度,也提高了开发效率,同时比较注重用户体验,这里列举出的框架几乎都是完全免费,所以应用十分广泛。我们在随后的两章中将列举两个最流行的框架做一些介绍,以期读者可以有一些感性的认识,关于jQueryExtJS的深入的研究已经大大的超出了本书的范围(事实上每一个框架都足以写一本书),有兴趣的读者可以参考相关的书籍。

评论 共 4 条 请登录后发表评论
4 楼 hastune 2012-09-12 14:29
应该继续ES的讲解。插入DOM和MVC其实没有必要的说~~
3 楼 youshini 2011-07-18 11:42
已阅完,这章好东西不多。
2 楼 hastune 2011-06-25 00:11
hongye612430 写道
Firebug能调试页面引入的JS文件中的JS代码么?

可以

我觉得作者这一章有些远离标题了。是《javascript内核》,这些有些走题了。
希望作者可以挑出一些代码来进行分析讲解。
比如jquery的缓存怎么做的。这样比较靠近主旨。

也可以另起一版,做一个web前端的入门级介绍。
1 楼 hongye612430 2011-06-03 13:17
Firebug能调试页面引入的JS文件中的JS代码么?

发表评论

您还没有登录,请您登录后再发表评论

文章信息

  • abruzzi在2011-05-09创建
  • abruzzi在2011-06-01更新
  • 标签: javascript, client
Global site tag (gtag.js) - Google Analytics