color=#0000FF]    最近看到论坛里有很多朋友问Js的相关问题,其实小弟也早就想好好总结一下Javascript这门语言了,最近忙着准备毕业答辩,趁着端午假期,可以有空闲时间静下心来总结自己所学,希望能分享给各位!自从在Java基础版面完成了10多篇的《Java杂谈》连载以来,得到论坛里很多朋友的支持和鼓励,包括一些前辈的肯定。时隔大概半年,又再次提笔开始些学习笔记,笔者一定更加努力严谨,希望大家能共同提高!    废话少说,开始进入Js的世界吧(按照老习惯,以问题形式展开):
    (一)Js是什么?
    一种误解是Javascript跟Java有渊源。其实很多书上也提到了Js的历史,笔者简单说一下。Javascript最早是由网景公司开发的一种脚本语言,其语法源头大部分是借鉴另外一门脚本语言Perl,早期的名称是Livescript, 跟Java完全不相关。最后改成Javascript并一致流行到现在完全是网景公司(Netscape跟Sun公司的一种行销策略)。现在的Js官方名称应该叫ECMAScript,ECMA代表欧洲计算机制造商协会,它对Js进行标准化制定,标准的实现包括浏览器对Js的解释。区别Js和Java是学好它的前提(尽管Js和Perl等脚本语言也逐渐推出了模拟面向对象的实现方式,本质上来说还是过程化的脚本语言)。    (二) Js的特点?
    既然是脚本语言,也就是说类似Html这种标记性语言一样,Js的执行其实不需要经过编译的过程,各种厂商的浏览器内置Js解释器能对Js进行正确解析,很显然我们能推理出的信息就是Js的解释执行应该是按照从上到下的顺序来进行的。写一些例子要运行不要任何开发工具,记事本写完丢进浏览器就可以被执行。当然,浏览器内置解释Js的调适和报错机制因厂商而异,特别是浏览器底Js执行的报错不如人意,甚至很多时候都无法准确给出错误出现在哪一行,使得Js程序员在初学经验尚浅的时候饱受折磨(笔者深有体会)。    先来看看Js的语法特点吧:
    Js程序是用Unicode字符集编写的,也就是说16位的Unicode编码可以标识地球上通用的每一种书面语言。这事国际化的一个重要特征,在Web开发需要国际化的时候作用相当大。(这里指ECMAScript标准化之后的Js版本1.5)。    Html不支持大小写,Js对大小写敏感,另外Js是一种松散的弱类型语言,比如语句放在不同的行上没有分号也不会报错,大家都可以联想一下常用编程语言在编译时候IDE的编译器会提示的错误在Js中通通不会出现,这样就大概都能猜到编写Js代码的时候有哪些必须注意的地方了。    弱类型的意思是Js中的变量没有类型之分,声明不声明都不会有错,若声明则统一用var关键字即可。Js支持的类型包括数字、文本字符串、布尔值、null、undefined、Object、Function。其中复合对象Object本质上包括两种,一种是对命名变量值的无序集合(稍后见示例),另外一种是有序集合(即大家所熟悉的数组),顺便提一下,Js对数组实现原理是栈存储,所以Js的数组才会有内置的push()和pop()方法。Js语言内定义的专用对象包括Date、RegExp(正则表达式)、Array、Error、Math、String、Number等。    Js数字格式允许的精度范围是正负2的53次方,但某些运算的数字范围只支持正负2的31次方。Js的字符串支持单引号或者双引号的括起来的Unicode字符序列,在数字环境中,解释器会自动将字符串转换成数字,我们只要把一个字符串减去0就可以将其转换成一个数字(转换规则相对比较严格,数字前后不能带空格等)。Js的解释器在遇到bool类型的变量时候也会根据使用环境自动完成转换,例如数字环境中true就转换为数字1,false就转换为0,字符串环境中就会转换为true或false串。    Js的函数非常特殊,因为它被当作一个变量来处理,可以被赋值传递,存储在变量中。总是说理论,这里举一个非常实用体现Js函数做为变量来处理的例子吧,现在我们需要在浏览器加载页面之后调用一系列函数来完成业务逻辑。然后Html页面只有一个onload函数可以重写赋值,怎么实现一个onload调用多个自定义函数呢:
function addLoadEvent(func){
    var oldOnload = window.onload;
    if(typeof window.onload != 'function'){
window.onload = func;
    }else{
window.onload = function(){
    oldOnload();
    func();
        }
    }
}
[    ——我们先把window.onload函数保存在变量oldOnload中,之后再判断window的onload函数是否是函数类型,针对第一次加载之前onload为空的情况,如果不是函数类型则将onload函数第一次赋值为func参数存储的函数;如果onload已经是函数类型了,则先调用原oldOnload变量中存储的onload函数,之后再紧接着执行传递进来的func函数。 由此我们可以看到一个调用队列构成,如果我们需要在页面加载时候按次序调用A()、B()、C()三个函数只需要加三行代码:
     addLoadEvent(A);
    addLoadEvent(B);
    addLoadEvent(C);    从这个例子我们可以体会到Js函数的灵活,毕竟是不需要通过编译直接被解释的语言,熟悉了浏览器解析Js的规律之后才能实用Js强大的功能。所以Js就是一把双刃剑,有人说它简单,其实它的链式调用结构可以很高效的完成一些算法,不需要遵循繁琐的语法规则(适配编译器),功能十分强大;然而眼下的Web开发现状是大多数人不遵循合理的规范去写Js,使得页面Js代码乱而且不易维护,大家都因Js语言松散而养成了写Js习惯也松散的坏毛病。说到底还是一个程序员的基本素质问题,编写良好风格的代码,不管使用什么语言都是应该遵守的规则。    继续介绍Js语言的其它特点吧,Js中声明了变量未赋值的会默认为null,而调用未声明的变量返回的值应该是undefined,虽然undefined和null值不同,但==运算却将两者看作相等。Js中的传值和传址基本规则是:基本数据类型通过传值来操作,而引用类型传址。比较特殊的是字符串在Js中被当作基本类型传值来操作,比较字符串可以用基本类型操作符==来完成,但有一种情况例外,就是两个都使用了new关键字的String对象比较是比较地址,其它情况都是比较值:
    var s1 = new String(“hello”);
    var s2 = new String(“hello”);
    var s3 = “hello”;
    alert(s1==s2); //return false
    alert(s1==s3); //return true
    前文提到了,一些在其它语言的编译错误操作在Js中有着不会出错的解释方式,因为Js不需要编译直接解释执行。例如重复声明变量在Js中完成的只不过是一次赋值操作而已;未声明(没有var)的变量会自动隐式创建为全局变量, 即使在在一个函数体内使用。这里就出现了一种坏习惯造成的问题,如果在函数内不声明变量直接使用,一旦全局有一个同名变量,程序员误认为这只是局部变量,实际全局变量会被修改。    Js语法中有一个容易被忽略的地方就是并没有块级别作用域,只存在全局和局部两种作用而已。这也是Js函数的特殊性相关的,在函数内部无论几层嵌套括号,局部变量在整个函数中都是有定义的(注意是整个函数中),下面这个例子可以给出有力的证明:    var ss = “global”;
    function tt(){
alert(ss);
var ss = “local”;
alert(ss);
    }
    ——由于局部变量ss的定义占据整个函数,所以第一次输出是undefined,不会是全局的global。这里顺便提到一个在Js中很强大却难以理解的功能就是闭包,什么是Js中的闭包呢?先了解一下Js作用域链(又称调用链)的概念:每个Js执行环境都有一个和它关联在一起的作用域链,这个作用域链是一个对象列表或者对象链,当Js需要解析变量x的值时,就开始查找该链的第一个对象,以此类推找下去。例如在一个不含嵌套的函数体中作用域链就有两个对象构成,一个函数调用对象,另外一个是全局对象,引用变量首先查找调用对象再是全局对象。    Js中的函数是通过词法来划分作用域的,这意味着函数是在定义它们的作用域里运行,而不是执行它们的作用域里运行,当定义了函数,当前的作用域链就保存起来,并成为函数内部状态的一部分(除了顶层作用域链仅由全局对象组成,跟词法没关系)。那么,当定义一个嵌套的函数时,作用域链就包括了外围函数,在这个嵌套函数里,可以访问外围函数中定义的所有变量(参照刚讲完的局部变量原则)。虽然作用域链已经形成,但其链上调用点的属性值是可以更改的,当嵌套函数的引用保存到一个全局作用域中,即使函数被调用返回退出了,函数的局部变量名字和值都依然存在,这就形成了一个闭包(将要执行的代码以及执行这些代码的作用域构成一个综合体)。可能理解闭包对大多数人比较困难,需要弄清楚词法分析等概念,但其实用起来很清楚简单,我们来看一个例子吧:
function f(s){ //传递参数s
    var x = "local"; //局部变量x
    function g(){ //嵌套函数
         alert(x);
        alert(s);
    }
    return g; //返回嵌套函数引用
}var global = f("hello"); //对全局变量global赋值
global(); //调用全局函数变量,以此弹出”local”和”hello”
    ——这个例子说明,即使函数f已经执行完毕,但由于闭包形成,f的局部变量和参数值在定义f(即使浏览器解释前5行)的时候就已经定义在调用链上了,然而随嵌套函数引用g的返回而挂在了全局调用链上,所以通过闭包连接依然可以找到已经执行退出的函数的局部变量和参数值。这就是所谓的闭包!    闭包是一种有趣又强大的技术,我们并不是一定要用它做什么,但真正理解它可以帮助我们学习到Js的精髓。    最后介绍一下Js的面向对象模拟特性吧,虽然不是真正的面向对象。Js中每个对象都有一个constructor属性,它引用了初始化这个对象的构造函数,有助于确定一个对象的类型。Js中的instanceof运算符本质就是检查constructor属性值。所有的函数都有一个prototype属性,当这个函数被定义的时候,prototype属性自动创建和初始化,初始化值是一个对象,只带有一个属性,名为constructor,指回到和原型相关联的那个构造函数上。强调一点:Js没有真正的类,只有通过构造函数和原型函数实现的伪类而已。看个例子:
function Person(name, age){
    this.name = name;
    this.age = age;
}Person.prototype.display = function(){ return this.name+”,”+this.age;}
    ——定义了一个Person类,Js中this关键字代表函数调用对象(还记得刚说完的函数调用链么),正因如此,每次调用this值都会不同,所以模拟出了类成员变量的效果。而prototype代表函数的constructor属性,不因调用对象而改变,但可以通过this关键字引用调用对象,实现了静态方法的效果。那么面向对象的封装即私有变量又怎么实现呢?答案自然是闭包!
function Person(name, age){
this.getName = function(){return name;}
this.getAge = function(){ return age;}
}
    ——还记得刚讲过的闭包么?即使在函数调用退出之后,函数参数name和age也会保留在函数返回值的作用域中能被访问到,这个返回值就是所谓构造函数返回的新建对象,而保留下来的参数值就是这个新建对象的私有变量。只能通过getName和getAge才能在调用链上找到闭包形成的name和age值。    (三) Js实用经验?
    Js的语法还有许多细节是可以推敲和研究的,这里就不罗唆了。笔者向来只喜欢讲关键本质核心或者实用的东西。最后一部分简单介绍一点Js的实用性吧。毕竟很多时候我们不是用Javascript去处理数据或者编写算法,大多时候是用来操作浏览器中的DOM对象,从而改变网页中的元素属性和内容,达到动态Html的效果。所以实践Js主要还是用来操作网页元素,之前所举的window 的onload函数就是个不错的例子。    我们经常要使用很多次document.getElementById来获取页面元素值,写多了之后不免有些罗唆,Js变量的命名规则中允许室友$符来做为变量名字,DWR、prototype等框架中就使用$(‘id’)来代替document.getElementById(‘id’)这一长串字母,其实实现起来也是非常简单的(严谨的写法):function $() {
    var elements = new Array(); 
    for (var i = 0; i < arguments.length; i++){ 
       var element = arguments[i]; 
       if (typeof element == 'string')       
          element = document.getElementById(element); 
       if (arguments.length == 1) 
          return element; 
    elements.push(element); 
   }
   return elements; 
}
    最后我想分享一些Js编程的感想,很多次被莫明其妙的Js错误搞得晕头转向的时候,其实笔者还是感叹计算机就是笨,永远只会按照固定的模型去做事情。牢骚两句,真正想说的是一旦发现浏览器警告的Js那一行没有明显的错误,很可能是由于从前向后解析Js代码时存在一些语法或者词法错误,只要想想Js的函数支持的是词法作用域,在定义的时候就要创建并初始化作用域链的,而浏览器的检错没那么智能,需要我们尽量熟悉Js的松散和词法分析方式才能很快的准确定位到错误所在并写出高质量的Js代码来。更多稀奇古怪的错误最后发现其实就是类似function后面没有括号、嵌套之后少一个右括号、引号用的中文引号之类的傻瓜错误,希望各位碰到的时候也能耐心的去寻找,同时注意规范的编程风格。[/color]

解决方案 »

  1.   

    第一次在Web开发版面发帖子,请大家多多指教!
      

  2.   

    js里面把function叫做类?
    就是方法吧?
    虽然很实用,但其是还是有很多限制的
    如:对REGEX并不是全部支持,解析的时候是按照顺序的
    事实上正式开发的时候是不会出现全局变量这种问题的
    因为常用的js都会写在诸如common.js之类的文件里面,只有页面上一些特殊需要判断的地方,如为空,regex之类的才会用到js
    而且我比较喜欢把跳转写到js里面
    function xx(){
        document.forms[0].action="<html:write page='xxx.do'/>";
        document.forms[0].submit();
    }
    这样虽然有好处,但在之前的判断上就很伤脑筋
    而且js里面只要语法有问题,根本就是点下去一点反应都没有
    比如说
    if(a>b){
       alert('a')
    }else if(a=b){
       alert('=')
    }...
    你当中else if 那个if不加的话,那就是(@#@……%(*@#……%(@%
    个人感觉js就像是java版html语言
    而且调试起来也很麻烦虽然说有fierfoxdebug但是开发的时候为了保持环境,根本就不让装的
    总之,期待2.0的表现了