`
katelin
  • 浏览: 30088 次
  • 性别: Icon_minigender_2
  • 来自: 厦门
社区版块
存档分类
最新评论

Introduction to JS -辛苦整理,转载请注明出处!

阅读更多

                          --- UBAO Team
                           --- Kate Lin
一、Target


   掌握JS语言中比较容易误解的概念

 

二、Getting start – Why we need to study?

 

     我第1次使用JS是在1个财务项目中。当用户在页面上填完借方所有科目及贷方所有科目的金额后,在焦点离开最后1个输入框时,程序需要自动累计出借方总金额和贷方总金额,并对比借方金额与贷方金额是否相等,如果不相等就给客户以提示。我们借助JS完成了这个当时看起来很酷的功能。之后JS又被我们大量的运用在表单的校验中-帮助用户以正确格式填写信息。事实上很多人对JS的应用,很长时间就停留在这种阶段-做1些页面上的校验、简单的事件处理的辅助性工作上。

 

     但实际上JS可以做的工作远不止这些(也不会很多,但我相信大部分情况下会超越你的想象)。因为要知道光靠css和html,我们的页面是不可能动起来的!我们需要借助JS来与用户进行交互。其实在很早以前,在很多web应用中,JS就被用来编写客户端的1些常用组件。如菜单、树、Grid等等。虽然直到最近由于Ajax的热炒,这位默默无闻的小兵才从后台走向前台。

 

    Ajax的使用,使某些web应用更易用、更人性化,同时带来了用户体验的提升。它所带来的局部刷新页面的方式,使得页面需要1次性载入的东西更少,每次与服务端交互完毕后返回的东西更少,需要更新的地方更少。合理的使用它对网站来说是大有裨益的(注意是合理,不是滥用。至于什么样是合理,应该是因时因地制宜。什么时候它应该发挥锦上添花的作用,什么时候它可以大规模的应用,取决于实际的项目环境)。

 

    我们来看1些典型而成功的Ajax应用:
    google的搜索提示。当我们敲入关键字的前几个字符,google会立即在下拉菜单里面为我们列出匹配的词条及相应的搜索结果。
    chinaren网站的登陆。当我们输入完正确的用户密码后,页面登陆部分那个小区域会显示我们成功登陆的信息。

 

    要做到这些其实并不复杂。Ajax并不是什么新发明的技术。它是1系列我们1直在用的技术的集合。它利用XMLHttpRequest进行异步数据获取; 利用文本或html或xml或json作为交互数据的载体; 利用css渲染返回的数据; 最后利用JS将以上这些过程串联起来处理。虽然说JS从语言自身来说存在着不少弊端,但依然有它的优势(在jdk1.6中甚至提供了在JAVA中编写JS代码的支持)。如果html的变革不那么迅速的到来。 在web2.0流行的今天,我们要做的就是掌握它的使用,合理的将它的优势发挥出来。

 

  JS是门入手容易进阶难的语言,对知识点不够清楚的理解会导致编写复杂应用时的困难。因此我写这篇文档的原因,除了想整理1下自己所学。更是为了起抛砖引玉的作用,希望有高手能够写些深入的文章以省下我学习的时间,厚厚。个人的理解表达能力有限,有误的地方,欢迎大家补充纠正。

 

三、Syntax – explanation on easily confuse place

     3.1 dataTypes and variable

     学过编程的地球人都知道,每次我们开始学习1门语言,很多时候总是先学习它所支持的数据类型(变量可以看成是某种数据类型的实例)。 对于有编程经验的人来说,可能感觉很多语言支持的数据类型都是大同小异。这里还要罗嗦的讲这个部分是因为JS对变量的定义简单灵活而不严密,需要特别注意。

 

     和JAVA等强类型语言不1样,JS使用方式var variableName = value; 定义变量。它并不把数据类型的声明作为变量声明的第1部分!(有时我写完JAVA代码再去写脚本时,常常会用int variableName = value; 在js里声明变量, 直到运行时报错才发现!!-_-—JS是解释型语言,不需要编译,很多错误只有到运行时才能发现,但现在有些IDE也会提供在编写代码时的语法检查,比如intelliJ)。

 

    在强类型的语言社会里,1个变量它是什么打他出生那刻就确定了。如果规划他要当流浪汉,他就只能做流浪汉这1类的工作。如果有天他忽然想改行竞选当总统,除非总统继承自流浪汉或总统与流浪汉实现1样的接口,否则那是不可能的事。如JAVA那样的语言,在定义变量的时候必须明确的指定变量的类型。这很严密也带来了不方便。在这上面JS很民主,它提供了宽松的方式。1个变量究竟是什么类型,要看它被赋的值是什么类型。你还可以在代码的任何角落通过赋值把你的变量指向任何类型的值。不需要任何转型,不会出错!甚至它有七十二变的本事,在你需要它成为什么类型的时候,它能自动转型为你所期待的类型!


那么JS里究竟有哪些数据类型呢?以下给出的几种就是JS所支持的数据类型:
     基本数据类型:数字、文本、布尔型。
     小数据类型:undefined 、null。(所谓小,意思就是它只有1个值)
     复合数据类型:对象 、数组(数组表现的行为与对象不同,所以虽然它其实也是1种对象,但仍把它独立当成1种数据类型)、函数。
     Note: 这里有些容易误解的地方需要注意。比如函数是1种数据类型。Date、RegExp、Error是Js里的专用对象,而非数据类型。

     函数作为1种数据类型正是JS灵活特征的体现之一。这意味着函数可以被存储在变量、数组、对象中,也可以作为参数进行传递。

 

 alert(typeof undefined); // undefined
 alert(typeof null); // object。null也是1种object类型。但是它并不具备object的特性。

 var d = new Date();
 alert(typeof d); // object。说明Date不是1种类型,只是1种专用对象

 //存储在变量中的函数,因为这个函数没有名字,所以它也是1个匿名函数。
 var v_f = function(){}
 alert(typeof v_f); // function。 函数是1种数据类型
 
 var arr = [new Function("alert(1+1);"), new Function("alert(2+2);")]; //存储在数组中的函数
 alert(typeof arr); // object。数组是1种对象
 arr[0](); //2
 arr[1](); //4

 var o = new Object();
 o.m1 = function(){
  return 'method';
 }
 alert(typeof o);// object

 function handler(){
  alert('handle event');
 }
 function callback(evtHandler){
  evtHandler();
 }
 callback(handler); //将函数作为参数传递,是不是很酷?利用这种特性可以实现回调

 

     3.2 auto datatype conversion

      当某个类型的值被用于需要基它类型值的环境中时,JS会自动尝试将这个值转为所需要的类型。所以我们常常可以看到这样的用法:

var object = {};
 if( object ){ //object自动转为true,不需要写成object != null
  alert('object not null');
 }
 var b = new Boolean(false);
 if( b ){ //b自转转为true
  alert('expression is true');
 }

 
   执行的结果是打印
           object not null
           expression is true
产生这个结果的原因是非空的对象在需要布尔值的环境中会被转为true。要注意对原始值为false的包装对象也是如此。但如果用if( b = = true) 那么它就不会进if语句了。因为此时进行了运算。

 

     3.3 global object
     

      在执行JS代码之前,JS解析器将会先创建一个global object。在html文档中, window对象作为指向global object的一个引用,定义了全局的属性(如parseInt、setTimeout方法)。任何定义在函数体外,或函数体内没用var语句声明的变量都属于global object的属性。

 

      3.4  scope chain
     

        作用域链定义了JS代码查找变量的顺序。每个函数都有其执行的作用域链。作用域链是一个对象链。当JS代码需要查询1个变量的值时,它从链的第1个对象开始延着作用域链查询,直至找到变量的定义。一个函数的和用域链为 它自己调用对象全局对象。

var i = 0;
 function out(){    
  function inner(){  
   //查找i的定义:inner-->out-->global.只要在某个位置找到就立即返回
   alert(i);  
  }
  inner();
 }
 out();

 

     

         3.5  this, who are u point to?

      在很多语言中,this都是重要且强大的关键字。在Java中,它总是指向当前的类的instance。但在JS里this总是指向它的东家,就是function的所有者。这个隐含的意思就是说如果function换了东家,this的引用也会随之改变。

    在JS里定义的函数,如果没有指定调用者,则默认的它的所有者是一个隐含的全局对象(在html页面里,全局对象可以看作是window)。

var name = 'attribute in global'; //可以将这个变量看成是定义在全局对象里的属性
 function Test1(){
  alert('this point to '+(window == this? 'global' : 'local') + ': ' + this.name);
 }
 
 Test1(); //this point to global:attribute in global。没有指定它的调用者,所以这时this指向的是全局对象window。Test1里面打印的this.name即是全局对象的属性
 
 function Test2(){
  this.name = 'attribute in local';
  alert('this point to '+(window == this? 'global' : 'local') + ': ' + this.name);
 }
 
 var t2 = new Test2(); // this point to local:attribute in local。这时this指向的是t2这个对象,所以Test2里面打印的是在Test2内定义的属性。

setTimeout(function(){alert('say hello!');} , 1000); //setTimeout是定义在全局对象里的方法,可以直接调用

 

所以当对你this的指向不知所以然时,很容易写出导致错误的脚本,如下例:

<div id="d3" onclick="handler()">click me and u will find error occur!!</div>
<script type="text/javascript">
function handler(){
  this.style.color = 'red'; // will cause "this.style is null or not an object" error
 }
</script>

 

在上面的例子中,程序员原本希望在单击层d3时将它的字体着色改成红色,但是不幸事与愿违,单击的时候错误产生了。为什么?因为此时this指向的是window对象(非层d3)。window对象是没有style属性的,所以Error就产生了。

是不是没听懂呢?那我们再说得更清楚些,看下例:

<div id="d1" onclick="handler()">click me and alert true</div>
<div id="d2">click me and alert false</div>

<script type="text/javascript">
function handler(){
  alert(this == window);
 }
 document.getElementById('d2').onclick = handler;
</script>

 

    在上例代码中,层d1采用inline event registration model来注册onclick事件。这种注册方式只是告诉浏览器,在单击d1的时候应该去触发函数handler。因此这时handler里的this仍指的是全局对象window。层d2使用脚本方式注册onclick事件。这时候函数handler被copy给了d2的onclick属性。因此在单击d2时,方法里的this指向的就是d2这个对象,而不再是全局的window对象了。

 

    因此对于那个错误的例子,正确的处理方式如下。将this做为参数传给事件处理函数,或者在事件处理函数中通过document.getElementById来获取要操作的DOM元素d3。

<div id="d3" onclick="handler(this)">click me and change my color</div>
<script type="text/javascript">
function handler(divObj){    
  divObj.style.color = 'red';
  //document.getElementById(‘d3’).style.color = ‘red’; 
}
</script>

 

 3.6 Function and Method
     作为对象的属性的Function就是Method。但实际上当你直接调用1个Function时,你其实是在调用全局对象的Method。所以从这个角度上来说Function和Method没什么不同。实际上它们最大的不同主要在于设计意图上的不同。Method更多的是为了定义自定义类的行为,在方法体内,this指代的是调用它的,定义它的自定义类的实例。

 

  3.7 Execution Context
     执行上下文即JS代码执行的上下文环境。

            

3.8 apply &&call


     如何调用1个函数?最简单的方法是用method ( arguments )。我们现在将介绍的两个方法也是用来调用函数的。但是它们比较另类,因为它们可以在调用函数的时候改变其其运行的上下文环境(改变函数的东家,记得上面说的吗,所以函数里面的this的指向也就随之改变)。这两个很cool的兄弟是apply和call。它们的格式如下:


    invokedMethod.apply( invodeObject, [args]);
    invokedMethod.call( invodeObject, args1, args2, .., argsN);

说明1下:invodedMethod是将被调用的函数。invodeObject是函数的新东家,args是被调用函数的参数。

为了容易理解,我写一个简单的例子:

var brand = ‘china’;

//定义1个将被调用的方法
function printBrand(computer){
  //注意this的改变
  alert(computer + ' use brand ' + this.brand);
 }

 function IBM(){
  this.brand = 'ThinkPad';
 }

 function Lenovo(){
  this.brand = 'Lenovo';
 }

 var ibm = new IBM();
 var lev = new Lenovo();

 /**
     * apply与call相同处:
  * 1、作用相同.都是使某个方法作为某个对象的方法运行.
  * 2、第1个参数都是要调用该方法的对象。  *               
     * 不同处:
  * 1、apply方法,第二个参数是数组或arguments类型的变量
*    call方法第二个参数是以逗号分隔的任何变量
* 2、所有参数是都不是必选的。第1参数为空时,默认为window。
*
  * plus不明白的看例子的输出就会明白了^^
  */
 printBrand.apply(ibm, ['X2x']);  // X2x use brand ThinkPad
 printBrand.call(ibm, 'T2x');  // T2x use brand ThinkPad

 printBrand.apply(lev, ['X6x']);  //X6x use brand Lenovo
 printBrand.call(lev, 'T6x');  // T6x use brand Lenovo

printBrand.call();  // undefined use brand china

以上的代码,其实相当于:
 function printBrand(computer){
  //注意this的改变
  alert(computer + ' use brand ' + this.brand);
 }

 function IBM(){
  this.brand = 'ThinkPad';
 }

 function Lenovo(){
  this.brand = 'Lenovo';
 }

var ibm = new IBM();
 var lev = new Lenovo();

 ibm.printBrand = printBrand; //将printBrand方法给ibm对象
 lev.printBrand = printBrand; //将printBrand方法给lev对象

 ibm.printBrand('X2x');
 lev.printBrand('X6x');

 

看到这里,也许大家会想到利用这两个方法,我们就可以巧妙的实现JS类的继承。

function Parent(id){      
    this.id = id;      
    this.getLocation = function(){      
      alert(this.id + ' at XiaMen UBao');      
    }      
  }    
     
  function Child(id){   
    /**调用Parent这个Function。并用指向Child实例的this替换Parent里的this*/  
    Parent.apply(this, arguments); 
 //Parent.call(this, id);
  }
  
  var c1 = new Child('katelin');      
  c1.getLocation(); // 打印 'katelin at XiaMen UBao'

 

写到这里,我觉得自己可能说得太罗嗦了,上面说的都是比较容易搞错的地方,而且还有很多我自己也不是很清楚的未提及的地方。 希望大家自己看书,再把心得写出来分享。

 

四、Code Style

         为什么说code style很重要。想想看也许在某个时候,由于你不小心少打了一个分号,你的代码又被去掉了换行符压缩了。拜IE所提供的基本上没用的错误信息,我们将不得不用肉眼从那1行长得没有尽头的代码里面找出错误的根源,那将是多么可怕的1件事-_-!!(我就曾经遭遇过。最后我花了1晚上才找出没加分号的那行。自从那次后,我写脚本时都很老实的在每行后面加上分号)。


         好的代码风格不仅减少错误的发生机率,也为后面维护的人留下方便之门。通过对语法的深入了解和实践的累,我们可以整理出1些好的编码风格并形成良好的编程习惯。有时候这也是经验的1种体现。


1. 尽量将变量的声明集中放在函数体的头部
2. 在每行的末尾加上分号;
3. if和else后面的语句用{}括起来
4. 使用/**/添加注释

 


 五、Tips

          1. 拼装大的字符串,尽量少用“+”符,尽量使用数组。
           在开发动态页面的过程中,我们经常要拼装html,有时候我们还需要拼装要提交的数据。如果直接用操作符“+”进行字符串连接操作,它的效率是不高的。

           你可能会问为什么不高捏?我想大概因为当我们使用“+”符时,JS会为字符串内部创建一个瞬态的String包装对象。这种频繁的创建,丢弃可能是导致性能不高的原因。所以推荐使用数组来处理大字符串连接。

function StringBuffer(str){
var arr = [];
if(str) {
 arr[0] = str;
}
this.pos = arr.length;
this.append = function(s){
arr[this.pos++] = s;
return this;
}
this.toString = function() {return arr.join("");}    
this.clear = function(){arr = [];}

 

六、Better Design - Three Layer Model

       J2EE的前端开发,即web页面的开发,经常是让人非常诟病的地方。混杂着大量的css,html,js,java代码的页面常常让维护的人员无所适从。所以说对于大型的web应用的前端开发来说,在开发前需要对页面做合理的分层。制定清淅可行的规范,以降低之后的维护成本。我们1般会将页面分为三层,这也是比较清楚的1种实践。
   内容 :这是静态的html文本数据。
   展示:通过css对html文本进行渲染以提高页面的观感。
   动作:通过脚本来控制用户的交互过程。

 

下周有时间再写1点关于Ext的

分享到:
评论
1 楼 Mr._B 2012-03-29  
顶一个 虽然很早前的博文了

相关推荐

Global site tag (gtag.js) - Google Analytics