@xiaoxiang
2015-04-25T01:47:06.000000Z
字数 8517
阅读 2436
技术
我们不是第一次听说这个名字了。它是做什么的呢?我们一起通过一个简单的例子认识一下它。
请打开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提供了变量、函数、流程控制和几个内置的类,具有类似C/C++的高级语言特征。这起码说明JavaScript具有完成复杂任务的潜力。不过实际使用中,我们主要通过JavaScript控制HTML元素的内容和外观,来实现页面和用户间的交互,在这一点上,JavaScript具有自己独特的语法风格,其中包含了一定的优点,也包含了一定的坑。下面将结合具体示例来讲解。(JavaScript = C + Lisp,几乎就是披着c语言外皮的Lisp语言。Lisp是世界上最黑客的语言)
注意JavaScript中也只允许使用英文标点符号(基本上的语言都是如此)。
单击div后显示提示框,内容为”Hello JavaScript”
讲解:
设定onclick属性后,在页面上单击这个div,就会执行onclick属性的内容,可以是一句到多句JavaScript语句。
看起来是在设置onclick属性,实质是指定触发了这个div的onclick事件时执行怎样的动作。onclick事件可以由用户触发,也可以由代码触发。
这个例子中执行了alert()函数,会弹出一个提示框,框中的文字就是函数参数。参数可以是常量,如本例。也可以是变量,见例2
单击div后显示提示框,内容是一个文本框中用户输入的内容
讲解:
这例子中div的onclick事件要执行的是两条连续的语句。第一条语句中声明了一个变量a,并读取文本框的输入存储到a中;第二条语句把a的值显示到提示框中。
读取文本框内容使用的语句如下:
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
var n = document.getElementById("intext1");
这里就是把这个HTML元素整体作为变量n的值。这时不仅可以从n读取这个元素的相关属性,也能够通过n正常设置它的属性并作用于页面中。这个方法非常重要,下文会经常用到。
JavaScript提供了如下三个强制类型转换函数,在确定操作前后数据类型不同时建议使用(如var n=”1”,希望得到n+1的值,使用var a=parseInt(n)+1);
parseInt()
:强制转换字符型或字符串为整数
parseFloat()
:强制转换字符型或字符串为小数
String()
:强制转换数字和逻辑为字符串
单击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。
鼠标移进、移出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的属性值的格式。设置长度请记得带单位,如果新的值里包含双引号,请先进行处理(加反斜杠或替换为单引号)。
延时执行/循环运行
这个JavaScript函数在执行后将在事件间隔d=1200(单位毫秒)之后,执行alert()函数。用到的函数原型是setTimeout( func, delay),即这是具有两个参数的一个函数,这个函数的功能是在时间间隔delay之后执行func。在实际使用时,用一个函数定义部分替换了func,从而成为了现在这个样子。
函数原型只会完成一次设定的功能。但是在这个间隔功能的基础上,我们可以自己封装出更加高级的用法。如,如果需要每隔一段时间就执行某一功能,那么可以在执行目标功能后,附加一行语句,重复执行这个延时函数,就可以实现不间断地重复时间间隔。
那么重复执行时如何让它停下呢?对应的函数是clearTimeout()。使用方法是,在设定事件间隔时为间隔对象设定名称用来标识;需要停止时把名称作为参数执行clearTimeout()函数即可。以下是一个二次封装后的函数示例,会在执行固定次数后自动停止。执行start()函数即开始一个10步循环。
请自己试着运行这个例子并仔细观察提示出现的时间,弄懂流程。
在这个例子的基础上还可以做出更多丰富的功能,比如颜色的渐变切换、div以一定速度移动等。把过程分为许多小步,每隔一段时间执行一小步即可。
Ajax/异步刷新
在我们做翻页的作业的时候,我们使用的方式是用超链接打开新一页的页面;在普通ASPX页面中,在更新页面上的内容时也会导致整个页面刷新。有没有办法在不刷新整个页面的前提下更新页面上的内容呢?这个技术就是Ajax。
先简单介绍一下工作流程:建立一个Ajax专用的数据对象,这个对象带有一些功能。适当设置这个对象的属性以后,它可以向指定的服务器(里面运行的页面)请求新的数据;在新的数据接受完毕以后,就可以用改变框架元素的innerHTML的方式把新内容呈现在页面上。这个过程可以避免刷新整个页面,数据传输过程中也可以不干扰用户其它的操作。
要实现完整的这件事情,需要同时设置客户端JavaScript和服务器端代码。在客户端JavaScript中执行上文中说到的Ajax对象来在客户端接收数据;在服务器端编写程序来根据客户端请求发送对应的数据。
下面提供一段完整的客户端代码和一份简单的服务器端代码。
在学习过这么多JavaScript的功能后,我们再把它与C#进行一下对比。
Language | C# | JavaScript |
---|---|---|
前台or后台 | 后台(页面发送给用户之前) | 前台(页面发送给用户之后) |
用户可见 | 不可见 | 用户可见(有风险) |
运行位置 | 服务器 | 用户自己的电脑上 |
主要作用 | 把数据添加到模版 | 在已经生成的页面进行用户交互 |
刷新页面 | 每个操作都刷新 | 不刷新页面 |
向数据库添加数据 | 可以,标准的方式 | 不建议这样做 |
运行前,进行编译 | 是,还会具体提示错误类型 | 执行一步编译一步(如果某一步出错会导致余下的都不能执行) |
读取页面中标签的内容 | 只能特定ASP标签 | 任意标签 |
(微软家提供的数据库处理程序是SQL Server,在读写数据时需要使用适当的用户名和密码,使用JavaScript操作的话会需要把用户名密码写在代码里,而这些代码是用户可见的,这会非常危险)
假设这样一种情境,用户在页面上输入用户名和密码进行登陆,登陆后打开新的页面,新页面弹出提示“登陆成功”。这个过程需要使用后台C#验证用户名密码和进行登陆操作,还需要激活新页面中的JavaScript来弹出提示。那么如何使用C#控制JavaScript呢?
我们已经学习过为div的onclick事件设置对应的JavaScript,其实还有一些事件可以做类似的设置。可以给HTML body设置onload事件,这样当页面加载完成时就会立即执行对应的JavaScript。这种方法设置后执行的代码是固定的、不可变的,但是也不需要C#配合。毕竟是最基本的方法,欠缺灵活性。
方法2基于方法1,加入了后台的配合。当C#向前台输出内容时,为一些特定内容多输出一些信息,如输出一个用户不可见的div,或者为现有的div设置自定义属性(可以设置info="3"这样的属性),它不会影响到HTML元素的显示。然后设置前台JavaScript时,先按方法1设置,但是在代码中,先从页面中的HTML元素中读取刚才多输出的信息,根据不同的信息来执行不同的动作。这个方法非常实用,而且在任何时候执行的JavaScript都可以读取这些额外的信息。
即,这方法的流程是: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标签即可。
在方法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的时候,它也可以成功,比如
foo = 'bar'
但是我没有在原先声明过foo变量,当初为了解决这个问题,就会给全局变量(在浏览器里是window变量)添加一个成员变量foo
,给它赋值'bar'
.很明显,这扩大了变量的作用域,万一其它地方还有foo变量呢?这种情况有人把它叫做污染全局空间。
var只会在当前闭包(一个函数的范围里)里有效,外部无效。这样子就不会污染外部了。
例子:
var foo=999;
function f1(){
alert(foo);
}
f1(); // 999
function f1(){
var bar=999;
}
alert(bar); // error
function f1(){
baz=999;
}
f1();
alert(baz); // 999
//上面例子等价于
(function f1(){
qux=999;
})();
alert(qux); // 999
//上面例子等价于
(function(){
foobar=999;
})();
alert(foobar); // 999
(function(){
for(var i=0;i<1;i++){
var foofoobar='你好,再见⊙▽⊙';
}
alert(foofoobar);
})();
js的string推荐用单引号,因为json强制双引号。所以这样子引入json数组不需要\'