Javascript进阶

前言
我的上一篇<Javascript简述>对JavaScript做了浅尝辄止的描述,并没有深入讲解其细节内容。本文则会从下面几方面的内容对JavaScript做一些整理与深入的讲解:
1. object
2. this & closure
3. call & apply
4. arguments
深入了解这些概念将使你在JavaScript的使用上更加的游刃有余。

一、object
JavaScript是弱类型的脚本语言,所以声明变量时不用指定类型。
在JavaScript中有三个基本类型boolean,number,string与特殊类型null(空/无效),undefined(无定义),引用类型则是object。
数组和函数都是实现为object类型的对象。一般情况下在JavaScript中可以这样定义对象,类似perl中的hash:
var functions = {
 show:function(){
  alert("Show"); },
 hide:function(){
  alert("Hide"); }
};

这里有两种方式调用其中的函数:
1, functions.show();
2, functions["show"]();
显然用第二种方式可以让我们在程序中动态生成函数调用方式,有更大的自由性,充分体现出JavaScript动态语言的特性。
例如:
functions[condition?"show":"hide"](); // 根据condition条件决定调用show/hide

在jQuery源代码中就有好几处地方使用了该方式:

ContractedBlock.gif ExpandedBlockStart.gif Code

 
// Save the old toggle function
 _toggle: jQuery.fn.toggle,

 toggle: 
function( fn, fn2 ){
  
var bool = typeof fn === "boolean";

  
return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
   
this._toggle.apply( this, arguments ) :
   fn 
== null || bool ?
    
this.each(function(){
     
var state = bool ? fn : jQuery(this).is(":hidden");
     jQuery(
this)[ state ? "show" : "hide" ]();
    }) :
    
this.animate(genFx("toggle"3), fn, fn2);
 },

 fadeTo: 
function(speed,to,callback){
  
return this.animate({opacity: to}, speed, callback);
 },

ECMAScript为object类型定义了一个内部属性prototype。在对象的属性解析过程中,会需要用到这个内部属性所引用的对象链--即原型链,原型链终止于链中原型为null的对象。
可以通过一个公共的prototype属性,来对与内部的prototype属性对应的原型对象进行赋值或重新定义。
例如:

function  Person(first, last) {
  
this .first  =  first;
  
this .last  =  last;
}
Person.prototype.fullName 
=   function () {
 
return   this .first  +   '   '   +   this .last;
}
Person.prototype.toString 
=   function (){
    
return   ' [Person:  '   +   this .fullName()  +   ' ] ' ;
};

var  simon  =   new  Person( ' Simon ' ' Willison ' );
document.write(simon.fullName());

 

二、this与闭包(closure)
这里先从一个例子开始:

ContractedBlock.gif ExpandedBlockStart.gif Code
//V2.1 - Based on http://www.openjs.com/scripts/jx/ - released under BSD license
//
 2.1 version by Chris Maunder (http://www.codeproject.com/) 
//
  - use XMLHttpRequest as 1st priority + use window.XMLHttpRequest in test, allow loading 
//
    indicator to be specified. Also added '$'. Added custom error handler. Added optional 
//
    load parameters removed bind method
jx = {
 http:
false//HTTP Object
 format:'text',
 callback:
function(data){},
 indicator:
false,
 customError:
false,
 customhandler:
false,
 $: 
function(id) {return document.getElementById(id);},
 error: 
function(code){alert('An error occurred (status '+code.toString()+')')},
 
//Create a xmlHttpRequest object - this is the constructor. 
 getHTTPObject : function() {
  
var http = false;
  
if (window.XMLHttpRequest) {
   
try {http = new XMLHttpRequest();}
   
catch (e) {http = false;}
  } 
else if(typeof ActiveXObject != 'undefined') {
   
try {http = new ActiveXObject("Msxml2.XMLHTTP");}
   
catch (e) {
    
try {http = new ActiveXObject("Microsoft.XMLHTTP");}
    
catch (E) {http = false;}
   }
  }
  
return http;
 },
 
// This function is called from the user's script. 
 //Arguments - 
 // url - The url of the serverside script that is to be called. Append all the arguments to 
 //   this url - eg. 'get_data.php?id=5&car=benz'
 // callback - Function that must be called once the data is ready.
 //  loadingId - id of 'loading' HTML element to be displayed
 //  errCallback - custom error callback
 // format - The return type for this function. Could be 'xml','json' or 'text'. If it is json, 
 //   the string will be 'eval'ed before returning it. Default:'text'
 // method - GET or POST. Default 'GET'
 //  customHandler - custom error callback
 load : function (url,callback /*,loadingId,errCallback,format,method,customHandler*/) {
  
var method='GET';
  
var format='text';
  
this.init(); //The XMLHttpRequest object is recreated at every call - to defeat Cache problem in IE
  if(!this.http||!url) return;
  
//XML Format need this for some Mozilla Browsers
  if (this.http.overrideMimeType) this.http.overrideMimeType('text/xml');

  
this.callback=callback;
  
if(arguments[2])this.indicator=this.$(arguments[2]);
  
if(arguments[3])this.customError=arguments[3];
  
if(arguments[4])method = arguments[4].toUpperCase();
  
if(arguments[5])format = arguments[5].toLowerCase();
  
if(arguments[6])this;customhandler = arguments[6]
  
var ths = this//Closure
  var now = "cache-bust=" + new Date().getTime(); // IE cache kill
  url += (url.indexOf("?")+1? "&" : "?";
  url 
+= now;

  
var parameters = null;

  
if(method=="POST") {
   
var parts = url.split("\?");
   url 
= parts[0];
   parameters 
= parts[1];
  }
  
this.http.open(method, url, true);

  
if(method=="POST") {
   
this.http.setRequestHeader("Content-type""application/x-www-form-urlencoded");
   
this.http.setRequestHeader("Content-length", parameters.length);
   
this.http.setRequestHeader("Connection""close");
  }

  
if(this.customhandler) { //If a custom handler is defined, use it
   this.http.onreadystatechange = this.customhandler;
  } 
else {
   
this.http.onreadystatechange = function () {
    
if(!ths) return;
    
var http = ths.http;
    
if (ths.indicator)ths.indicator.style.display='';
    
if (http.readyState == 4) {//4 = document loaded.
     if (ths.indicator)ths.indicator.style.display='none';
     
if(http.status == 200||http.status==0) {
      
var result = "";
      
if(http.responseText) result = http.responseText;
      
if(ths.format.charAt(0== "j") { // JSON -> eval result first
       result = result.replace(/[\n\r]/g,""); //\n's in JSON string cause errors in IE
       result = eval('('+result+')');
      } 
else if(ths.format.charAt(0== "x") { //XML
       result = http.responseXML;
      }
      
if(ths.callback) ths.callback(result); // process
     } else {     
      
if(ths.customError)ths.customError(http.status); else ths.error(http.status);
     }
    }
   }
  }
  
this.http.send(parameters);
 },
 init : 
function() {this.http = this.getHTTPObject();}
}

如果大家对上述代码中this与闭包的用法有疑惑的话,可以继续往下看。

讲到这里就不得不提及JavaScript的作用域(scope)。所有的JavaScript代码都是在一个执行环境中被执行,有自己的作用域。这部分内容相对来说比较复杂,读者有兴趣可以详细参考<Javascript 闭包>。

JavaScript中的this就是被调用对象的引用。形象的说,是当前执行环境的上下文对象;简单的说,就是函数的拥有者Owner(这点对理解Event Handling很重要)。

闭包并不是JavaScript特有的概念,Martin Fowler早些年就发表了一篇关于闭包的文章中文版)。
闭包是具有闭合作用域 的匿名函数。简单来说就是在function内定义的function,內部的function可以存取外部function內定义的变量。

现在来看看上面说的那个例子:
1、定义了一个jx对象,由于是作为Ajax使用的,所以它提供的一些属性都是为了自定义参数使用的。
2、我们关注它的load方法:
 this.init();    // 对XMLHttpRequest对象进行初始化
 var ths = this; // 在这里把jx对象本身保存到ths中是为了onreadystatechange函数中可以正确使用到jx对象中的属性(否则onreadystatechange内部的this指向的是http对象而不是我们要使用的jx对象)。
 this.http.onreadystatechange = function () {
    if(!ths) return;
    var http = ths.http;
    if (ths.indicator)ths.indicator.style.display='';
    if (http.readyState == 4) {//4 = document loaded.
     if (ths.indicator)ths.indicator.style.display='none';
     if(http.status == 200||http.status==0) {
      ...... 
      if(ths.callback) ths.callback(result); // process
     }
     ......
    }
   } 

这里还要强调一下JavaScrpt的Event Handling里this的使用:
假设Html页面上有元素<div id="prompt"">Hello World!</div>,我们可以通过两种机制给div元素添加上下面的click事件:
// 在Html页面上定义click函数
function fn_click(){
 this.style.color = "#cc0000"; // 此时this指向的是函数的拥有者--页面,确切说是JavaScript的window对象
}
1, Copying
var divP = document.getElementById("prompt");
divP.onclick = fn_click;
顾名思义,Copying机制是通过把fn_click函数拷贝给div的onclick属性。因此事件执行后this指向的就是触发事件的div元素,故该函数在div点击后可以正常运行。

【注:这里给读者提个疑问--我们应该如何更改fn_click中的this指向,使其指向的是你需要的对象上而不是触发事件的div元素?这篇文章对此有精彩的论述:JavaScript's Slippery this Reference and Other Monsters in The Closet
2, Referring
Referring机制则是找到引用的fn_click函数后再执行它。
<div id="prompt" οnclick="javascript:fn_click();">Hello World!</div>
大家可以想想其结果是什么呢?

由于其采用的是Referring机制,故fn_click函数中的this指向的是全局对象window,那么显然onclick后会弹出错误--style不是window对象的属性。
为了解决这个问题,可以修改为:
function fn_click(obj){
 obj.style.color = "#cc0000"; 
}
<div id="prompt" οnclick="javascript:fn_click(this);">Hello World!</div>


三、call与apply
apply
Allows you to apply a method of another object in the context of a different object (the calling object).
call
Allows you to call (execute) a method of another object in the context of a different object (the calling object).
作用都是将函数绑定到另外一个对象上去运行,两者只是在定义参数方式有所区别:
var result = fun.apply(thisArg[, argsArray]);
var result = fun.call(thisArg[, arg1[, arg2[, ...]]]);

这里的参数差别就决定了apply在需要传参数的应用上更有优势。

请看下面的两个例子:
1, 为了保证在没有window.console的情况下,仍可以输出参数,可以采用如下方法:

function  log() {
    
if ( window.console )
        console.debug.apply( console, arguments );
    
else
        alert( [].join.apply( arguments, [
'   ' ] ) );
}

log( 'json feed received:', json );

2, 利用Array中的slice方法并通过call生成新的数组,简化操作:

var  args  =  [];  //  empty array
//
 copy all other arguments we want to "pass through" 
for ( var  i  =   2 ; i  <  arguments.length; i ++ )
{
    args.push(arguments[i]);
}
func.apply(obj, args);

//  通过刚才的介绍,我们可以简化为:
var  args  =  [].slice.call(arguments, 2 );
func.apply(obj, args);

 

四、arguments
在JavaScript函数代码中可以使用特殊的对象arguments来实现不定参数的效果。
它以类数组的形式保存了当前函数调用的参数,但是它实际上并不是数组,使用arguments instanceof Array会返回"false",不过我们可以使用下标获取其值以及长度length属性(表示调用参数的数目)。此外arguments还有个非常有用的属性callee,它表示对当前调用函数对象自身的引用,特别是可以用它来调用自身的匿名函数。

注:
1, 网上有不少方法说明arguments不是数组,大家有兴趣可以去看看。
2, 函数属于引用类型,有自己的属性和方法,其中length声明了函数期望的参数个数。
函数名.length或arguments.callee.length   // 形参个数
arguments.length  // 实参个数

请看下面这个经典例子--与C#中String.Format()方法类似:

String.prototype.format  =   function (){
 
var  args  =  arguments;   //  将参数保存到args中,以便于在stringobject.replace函数中被使用
  return   this .replace( / \{(\d+)\} / g,
   
function (m,s,i,t){
    
return  args[s];  //  s是模式中子表达式匹配的字符串,正是{0},{1}中的0,1
   });
}
var  formats  =   " {0} is {1}! " ;
document.write(formats.format(
" hans " , " chinese " ));

其在jQuery源代码中的使用:

ContractedBlock.gif ExpandedBlockStart.gif Code

 
// Mozilla, Opera and webkit nightlies currently support this event
 if ( document.addEventListener ) {
  
// Use the handy event callback
  document.addEventListener( "DOMContentLoaded"function(){
   document.removeEventListener( 
"DOMContentLoaded", arguments.callee, false );
   jQuery.ready();
  }, 
false );

 
// If IE event model is used
 } else if ( document.attachEvent ) {
  
// ensure firing before onload,
  // maybe late but safe also for iframes
  document.attachEvent("onreadystatechange"function(){
   
if ( document.readyState === "complete" ) {
    document.detachEvent( 
"onreadystatechange", arguments.callee );
    jQuery.ready();
   }
  });

 

通过前面的讲解,最后再看看下面这个例子:

var  Class  =  {
 create : 
function () {
  
return   function () {  this .initialize.apply( this , arguments); }
 }
};

var  vehicle  =  Class.create();
vehicle.prototype 
=  {
 initialize : 
function (type){
  
this .type = type;
 },

 showSelf : 
function (){
  
return   ' this vehicle is  '   +   this .type;
 }
};

var  moto  =   new  vehicle( ' Moto ' );
log.info(moto.showSelf());

现在,大家可以看明白这个例子吗? 

五、References
http://www.javascriptkit.com/jsref/
http://www.quirksmode.org/js/this.html

http://www.blueidea.com/tech/web/2007/4855.asp
http://blog.csdn.net/mumuTiger/archive/2008/03/25/2217731.aspx
http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html

 

转载于:https://www.cnblogs.com/huyh/archive/2009/04/11/1429530.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值