zoukankan      html  css  js  c++  java
  • 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源代码中就有好几处地方使用了该方式:

    Code

    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)
    这里先从一个例子开始:

    Code

    如果大家对上述代码中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" onclick="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" onclick="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源代码中的使用:

    Code

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

    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

  • 相关阅读:
    版本管理系统:svn和git
    Java学习笔记七 常用API对象三
    Java学习笔记六 常用API对象二
    Java学习笔记五 常用API对象一
    Java学习笔记三.3
    Java学习笔记三.2
    Java学习笔记三
    析构函数总结
    C++之类的构造函数,不得不学明白的重点
    C++拷贝构造函数(深拷贝,浅拷贝)
  • 原文地址:https://www.cnblogs.com/huyh/p/1429530.html
Copyright © 2011-2022 走看看