[关闭]
@xiaoxiang 2015-04-24T17:47:06.000000Z 字数 8517 阅读 2332

第三章 JavaScript

技术


第一节 初识JavaScript

我们不是第一次听说这个名字了。它是做什么的呢?我们一起通过一个简单的例子认识一下它。

请打开js sample.htm并点击其中的按钮和色板,观察页面上元素的变化。

这个页面的功能全部都是用JavaScript编写的。JavaScript最主要的作用就是这样,控制页面上的元素,包括它们的外观和它们的内容。这让一个HTML页面变得像是一个程序界面,而且能够与用户进行一些互动了不是么(对用户不同的操作作出不同的反应)。然而它却不需要服务器的参与,在任何设备的网页浏览器打开,不管网络条件如何,这个页面中的JavaScript都能够完成现有的功能。

这里是一个更为简单的JavaScript示例。以下是一个.htm文件的内容。
用浏览器打开这个页面,点击按钮1、按钮2都会显示一个内容是” Hello JavaScript!”的提示,它们做的是同一件事情,只不过写法不一样。按钮3会在下一个id为”area4text”的div中显示这句话。很直观地可以看到,这段HTML代码中,为带有功能的div设置了onclick属性,并且能够执行head中script段对应的代码。JavaScript就是使用这样的格式、这样的逻辑,通过操作页面上的HTML元素来实现丰富的与用户交互的功能。

第二节 JavaScript语法和应用

JavaScript提供了变量、函数、流程控制和几个内置的类,具有类似C/C++的高级语言特征。这起码说明JavaScript具有完成复杂任务的潜力。不过实际使用中,我们主要通过JavaScript控制HTML元素的内容和外观,来实现页面和用户间的交互,在这一点上,JavaScript具有自己独特的语法风格,其中包含了一定的优点,也包含了一定的坑。下面将结合具体示例来讲解。(JavaScript = C + Lisp,几乎就是披着c语言外皮的Lisp语言。Lisp是世界上最黑客的语言)

注意JavaScript中也只允许使用英文标点符号(基本上的语言都是如此)。

例1:

单击div后显示提示框,内容为”Hello JavaScript”

讲解
设定onclick属性后,在页面上单击这个div,就会执行onclick属性的内容,可以是一句到多句JavaScript语句。

看起来是在设置onclick属性,实质是指定触发了这个div的onclick事件时执行怎样的动作。onclick事件可以由用户触发,也可以由代码触发。

这个例子中执行了alert()函数,会弹出一个提示框,框中的文字就是函数参数。参数可以是常量,如本例。也可以是变量,见例2

例2:

单击div后显示提示框,内容是一个文本框中用户输入的内容

讲解
这例子中div的onclick事件要执行的是两条连续的语句。第一条语句中声明了一个变量a,并读取文本框的输入存储到a中;第二条语句把a的值显示到提示框中。
读取文本框内容使用的语句如下:

  1. document.getElementById('intext1').value

它用来读取id值为”intext1”的HTML元素的值,括号中为目标元素的id。这里体现了id的另一个作用,用于在JavaScript语句中找到它。”intext1”作为字符串变量,成为语句的参数。但是因为这个语句已经位于onclick=””的双引号中,需要写为'intext1'才能够正确执行。

注意alert()函数的参数类型,alert(a)将在提示框中显示变量a的值,alert("a")则会恒定地显示字母”a”。

document.getElementById() 是一个非常重要、非常常用的函数,在使用时与其它语句的组合有好几种不同的方式。下列语句都能够把文本框的内容读取到变量a中,在使用时各有方便之处

序号 语句 适用情况
1 var a = document.getElementById('intext1').value 单个语句单个操作
2 var i = 1;
var m= "intext" + i ;
var a = document.getElementById(m).value;
要定位的元素的ID是根据需要生成的,操作多个并列元素
3 var n = document.getElementById("intext1");
var a = n.value;
对单个元素要进行多项不同的操作

这里也体现了JavaScript一个很重要的特色,变量不显式区分类型,声明时统一用var,但赋值时还是要按照对应的数据格式。

JavaScript变量声明语句 变量类型 对应C#变量声明语句
var a = 1; 数字 int a = 1; (或double a=1;)
var a = "Hello" 字符串 string a = "Hello";
var a = true; 逻辑 bool a = true;

甚至可以是一个类的实例,如上一页中方法3

  1. var n = document.getElementById("intext1");

这里就是把这个HTML元素整体作为变量n的值。这时不仅可以从n读取这个元素的相关属性,也能够通过n正常设置它的属性并作用于页面中。这个方法非常重要,下文会经常用到。

JavaScript提供了如下三个强制类型转换函数,在确定操作前后数据类型不同时建议使用(如var n=”1”,希望得到n+1的值,使用var a=parseInt(n)+1);

parseInt() :强制转换字符型或字符串为整数
parseFloat() :强制转换字符型或字符串为小数
String():强制转换数字和逻辑为字符串

例3:

单击div后,div中数字变大1,起到计数器的作用

讲解

当JavaScript代码段比较长,又或者有些功能需要反复使用的时候,可以在head标签建立script标签,在其中定义函数;在这里定义的函数可以被下文调用。建议主要使用这种方式。

这里的函数声明格式是:

function + 函数名 + ( 参数表 ) + { 函数内容 }

由于JavaScript不区分变量类型,这里也不用声明返回值类型,参数表中也不需要声明类型。如可以定义这样的函数:

function add(a,b) { alert(a+b); }

例2中曾经使用document.getElementById('intext1').value来取得文本框中的值,这里仍然使用类似的方式。但是这次访问的是元素的innerHTML属性。value属性只适用于input类型的HTML元素,得到的是控件的值;innerHTML属性适用于所有类型的HTML元素,得到是标签头尾之内的所有内容。对innerHTML属性赋值,可以动态地改变div中的内容,如本例。

div中同时有文字和数字,需要先把数字和文字分离开。这里用到split函数,调用方式是对内容是文本的变量直接使用“变量名.split(分割符)”,将把目标变量以分隔符为界进行不限数目的分割。分割结果是一个数组。例子中,div的数字是后的部分,是分割结果数组的第二个元素,内容仍然属于文本。然后使用强制类型转换化为整数后即可以进行++操作了。最后输出回原div。

例4:

鼠标移进、移出div时外观随着改变

讲解:

之前的例子中我们已经看到可以使用onclick事件设置单击时激发的函数,这个例子中设置了鼠标移入div面积时发生的onmouseover事件和移出时发生的onmouseout事件。设置分别执行不同的函数,就可以达到改变按钮外观的作用。

在这个例子中,调用函数时把div自己的id 作为参数传入函数,目的是如果存在多个这样的div,则他们可以全都调用这同一对函数,只要改变传入的参数值就可以控制不同的div的外观。

这个例子中的函数通过JavaScript改变了div的外观。外观相关的属性位于HTML元素的style子项下。这些外观相关的属性基本对应了所有常用CSS属性,不过其中有一些CSS属性名有所变化,聪明的你知道是为什么吗?以下对比几个常用的JavaScript外观属性名与CSS属性名。

外观项目 CSS属性名 JavaScript外观属性名
字体颜色 color .style.color
背景颜色 background-color .style.backgroundColor
外边距 margin .style.margin
外边距-右 margin-left .style.marginLeft
背景图片 background-image .style.backgroundImage

所以其实大多数CSS属性名都得到了沿用。没有沿用的称为JavaScript 中的“驼峰”式写法,因为在中央出现一个大写字母。在赋值时一律以文本类型进行赋值,格式仍然沿用CSS的属性值的格式。设置长度请记得带单位,如果新的值里包含双引号,请先进行处理(加反斜杠或替换为单引号)。

例5:

延时执行/循环运行

这个JavaScript函数在执行后将在事件间隔d=1200(单位毫秒)之后,执行alert()函数。用到的函数原型是setTimeout( func, delay),即这是具有两个参数的一个函数,这个函数的功能是在时间间隔delay之后执行func。在实际使用时,用一个函数定义部分替换了func,从而成为了现在这个样子。

函数原型只会完成一次设定的功能。但是在这个间隔功能的基础上,我们可以自己封装出更加高级的用法。如,如果需要每隔一段时间就执行某一功能,那么可以在执行目标功能后,附加一行语句,重复执行这个延时函数,就可以实现不间断地重复时间间隔。

那么重复执行时如何让它停下呢?对应的函数是clearTimeout()。使用方法是,在设定事件间隔时为间隔对象设定名称用来标识;需要停止时把名称作为参数执行clearTimeout()函数即可。以下是一个二次封装后的函数示例,会在执行固定次数后自动停止。执行start()函数即开始一个10步循环。

请自己试着运行这个例子并仔细观察提示出现的时间,弄懂流程。
在这个例子的基础上还可以做出更多丰富的功能,比如颜色的渐变切换、div以一定速度移动等。把过程分为许多小步,每隔一段时间执行一小步即可。

例6:

Ajax/异步刷新

在我们做翻页的作业的时候,我们使用的方式是用超链接打开新一页的页面;在普通ASPX页面中,在更新页面上的内容时也会导致整个页面刷新。有没有办法在不刷新整个页面的前提下更新页面上的内容呢?这个技术就是Ajax。

先简单介绍一下工作流程:建立一个Ajax专用的数据对象,这个对象带有一些功能。适当设置这个对象的属性以后,它可以向指定的服务器(里面运行的页面)请求新的数据;在新的数据接受完毕以后,就可以用改变框架元素的innerHTML的方式把新内容呈现在页面上。这个过程可以避免刷新整个页面,数据传输过程中也可以不干扰用户其它的操作。

要实现完整的这件事情,需要同时设置客户端JavaScript和服务器端代码。在客户端JavaScript中执行上文中说到的Ajax对象来在客户端接收数据;在服务器端编写程序来根据客户端请求发送对应的数据。
下面提供一段完整的客户端代码和一份简单的服务器端代码。
在学习过这么多JavaScript的功能后,我们再把它与C#进行一下对比。

Language C# JavaScript
前台or后台 后台(页面发送给用户之前) 前台(页面发送给用户之后)
用户可见 不可见 用户可见(有风险)
运行位置 服务器 用户自己的电脑上
主要作用 把数据添加到模版 在已经生成的页面进行用户交互
刷新页面 每个操作都刷新 不刷新页面
向数据库添加数据 可以,标准的方式 不建议这样做
运行前,进行编译 是,还会具体提示错误类型 执行一步编译一步(如果某一步出错会导致余下的都不能执行)
读取页面中标签的内容 只能特定ASP标签 任意标签

(微软家提供的数据库处理程序是SQL Server,在读写数据时需要使用适当的用户名和密码,使用JavaScript操作的话会需要把用户名密码写在代码里,而这些代码是用户可见的,这会非常危险)

第三节 JavaScript和C#交互

假设这样一种情境,用户在页面上输入用户名和密码进行登陆,登陆后打开新的页面,新页面弹出提示“登陆成功”。这个过程需要使用后台C#验证用户名密码和进行登陆操作,还需要激活新页面中的JavaScript来弹出提示。那么如何使用C#控制JavaScript呢?

使用C#控制JavaScript

方法1(基本):在页面加载完毕时执行JavaScript

我们已经学习过为div的onclick事件设置对应的JavaScript,其实还有一些事件可以做类似的设置。可以给HTML body设置onload事件,这样当页面加载完成时就会立即执行对应的JavaScript。这种方法设置后执行的代码是固定的、不可变的,但是也不需要C#配合。毕竟是最基本的方法,欠缺灵活性。

方法2(基本+元素属性):

方法2基于方法1,加入了后台的配合。当C#向前台输出内容时,为一些特定内容多输出一些信息,如输出一个用户不可见的div,或者为现有的div设置自定义属性(可以设置info="3"这样的属性),它不会影响到HTML元素的显示。然后设置前台JavaScript时,先按方法1设置,但是在代码中,先从页面中的HTML元素中读取刚才多输出的信息,根据不同的信息来执行不同的动作。这个方法非常实用,而且在任何时候执行的JavaScript都可以读取这些额外的信息。
即,这方法的流程是:C#向前台输出特定信息——前台JavaScript读取这些信息——根据信息内容决定执行怎样的动作

方法3(C#输出JavaScript代码):

如果嫌方法2中先输出信息再读取出来的操作繁琐,那么可以使用C#直接输出要执行的JavaScript内容。这个方法的原理是,在页面中任何位置都可以输出一个完整的script标签,浏览器在发现它之后会立即执行其中的内容。

如C#可以这样向前台输出:
就既输出了内容,又执行了JavaScript代码。

这个办法的缺点在于,由于C#是服务器端把所有需要输出的内容都添加完毕后才输出到客户端的,所以JavaScript代码实际的执行时间还是在页面加载完成时。但是这个办法可以在C#后台输出时就灵活地决定是否需要执行,需要执行就输出script标签,不需要执行就不输出;并且需要执行的时候可以直接把函数参数添加到script标签中,不再需要从前台其它HTML标签中读取信息。


假设一种情境,用户在进行一个猜谜游戏。为了免得用户查看JavaScript而看到谜底,我们要求用户每填写一个答案,都要与服务器联络验证是否正确。即通过JavaScript调用后台的功能,与JavaScript提供的数据进行对比验证。那么如何用JavaScript调用后台C#函数代码呢?
使用JavaScript控制C#
方法1(基本,基于ASP.NET控件):
我们已经学习过在前台添加asp:button标签、asp:label标签和asp:textbox标签,分别起到按钮、文本输出和文本输入的作用。我们可以很方便地设置asp:button调用后台函数,在后台函数中读取asp:textbox标签的用户输入。
基于这个用法,我们可以向前台添加这几个标签,然后把为它们设置CSS属性”display:none”,这样它们虽然存在于页面上,但是却不会显示。在需要调用后台函数时,先使用JavaScript把需要传到后台的内容设置到asp:textbox输出到前台翻译后的标签内(<input type=”type” />,使用value属性),然后用JavaScript点击按钮即可。假设需要点击的按钮的id为btn1,模拟它的点击的JavaScript代码如下: (这行代码可以模拟任何元素的单击事件)
方法1的缺点在于,由于最终调用的是asp标签的按钮事件,以ASP.NET的方式执行,会刷新整个页面。但是与后台交互的方式是我们所熟悉的,用来交互的后台代码也是同一个页面对应的aspx.cs文件中的。如果需要返回值,返回值输出到asp:label标签即可。

方法2(Ajax相关,以功能为导向)

在方法1当中,我们调用的是前台页面对应的后台代码。但是实际应用中,我们最紧迫的后台功能需求是数据的读取和写入,而这些代码和哪个页面对应已经变得不重要。

在这样的情况下,我们可以使用新的页面类型,ASHX。这种页面类型不具有ASPX中的前台标签,它可以直接回传所需的数据,包括纯文本、图片以及其它各种类型的文件。它就是作为一个纯粹的数据输出页面。相比之下ASPX会传输一个完整的HTML页面。

建立一个新的空ASHX文件,其中的ProcessRequest()函数是用来处理访问请求的。在访问这个ASHX时,就会调用这个函数。我们可以在这个函数内编写后台C#代码,根据传入的数据不同(context.Request.QueryString),进行不同的后台操作,再传回不同的数据。

请结合第二节例6,使用Ajax的方式,就可以用JavaScript访问ASHX页面,一方面进行了后台操作,还能够获得回传的数据。

这种方法并不如方法1直接,但是推荐大家多学习、多使用这种方法。在这种方法中,把“给用户看到的页面”和“用来获得数据的页面”进行了分离,即前者ASPX,后者ASHX,从功能的角度进行了分离;而在分成两部分以后,也降低了设计难度,ASPX中只考虑如何正确显示页面框架和初始信息,ASHX中只考虑如何正确读取和写入数据并进行适当的回传。而且这种方法也避免了方法1中刷新整个页面的问题。

另外,由于JavaScript非常常用、非常重要,而且具有函数封装功能,所以有很多开发者开发了自己的JavaScript高级函数库,可以用这样的库直接执行一些高级的功能。并不是说自己编不出来,而是自己编的时候会有点麻烦,而且已经有别人写好的,可以直接拿过来用。比较著名的有jQuery(桌面,使用最广泛,严重推荐)、prototype、jQueryMobile(移动)、YUI。

在开发JavaScript时可以使用一些调试用的浏览器插件,方便在出现bug时快速修复。FireFox推荐使用FireBug,IE推荐使用Firebug Lite、SuperPreview,Chrome推荐使用Google Chrome Developer Tools。在调试时请注意,如果同一段代码在不同浏览器下得到了不同的效果,并不是不会出现的,可以使用更加通用更加标准的写法,也可以针对不同的浏览器单独进行代码的适配。

JavaScript的内容暂时就介绍到这里,这一章中的知识技能已经足够完成绝大多数的网站操作,有其它的需求可以直接百度,百度上有丰富的JavaScript讲解和范例。希望大家可以自行设计制作几个小网站,其中有静态页面的用户交互,也有使用后台代码执行的数据读取与写入,在编写过程中就能够体会到各个方面的各种技巧的优劣,也加深对网站各个方面的理解。

后序:

由于JavaScript是来自lisp的方言scheme,诞生起就带着浓重的函数式的风格。其设计之初就没受java,c#等面向对象的思想的束缚。

js是基于对象的,但是它的对象不是类的实例(也就是,没有java,c#等class这种关键字(据说ES6加入了class和extends的关键词,不知道是好事是坏事),对象就是对象)。但是js有个机制——closure(闭包),让js可以实现面向对象的封装,prototype可以实现继承。

为什么js用var?
js设计之初是不准备让语言报错。所以,当没有var的时候,它也可以成功,比如

  1. foo = 'bar'

但是我没有在原先声明过foo变量,当初为了解决这个问题,就会给全局变量(在浏览器里是window变量)添加一个成员变量foo,给它赋值'bar'.很明显,这扩大了变量的作用域,万一其它地方还有foo变量呢?这种情况有人把它叫做污染全局空间。

var只会在当前闭包(一个函数的范围里)里有效,外部无效。这样子就不会污染外部了。

例子:

  1. var foo=999;
  2. function f1(){
  3.   alert(foo);
  4. }
  5. f1(); // 999
  1. function f1(){
  2.   var bar=999;
  3. }
  4. alert(bar); // error
  1. function f1(){
  2. baz=999;
  3. }
  4. f1();
  5. alert(baz); // 999
  1. //上面例子等价于
  2. (function f1(){
  3. qux=999;
  4. })();
  5. alert(qux); // 999
  1. //上面例子等价于
  2. (function(){
  3. foobar=999;
  4. })();
  5. alert(foobar); // 999
  1. (function(){
  2. for(var i=0;i<1;i++){
  3. var foofoobar='你好,再见⊙▽⊙';
  4. }
  5. alert(foofoobar);
  6. })();

js的string推荐用单引号,因为json强制双引号。所以这样子引入json数组不需要\'

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注