• 原文:http://www.gracecode.com/Archive/Display/2086

    我们已经知道,null 没有任何的属性值,并且无法获取其实体(existence)值。所以 null.property 返回的是错误(error)而不是 undefined 。

    考虑下面的代码

    if (node.nextSibling.className == ...) {
       ...
    }

    在 node 或者 node.nextSibling 为空(null)的情况下,会返回错误(error)。所以,通常情况下的解决方案的代码为

    if ((node) && (next = node.nextSibling) && ... ) {
       ...
    }

    那么,当条件判断一多的情况下,代码会形成下面的情况

    if (
    (node) &&
    (node.nextSibling) &&
    (node.nextSibling.className == ...)
    ... ) {
       ...
    }

    随着判断条件的不断的增加,代码会变得非常的“丑陋”。

    有个小的“伎俩”,可以简化条件判断表达式。我们可以增加个空对象({})或者零(0)作为替代

    if ( next = (node || 0).nextSibling) ) {
       ...
    }

    那么,上述的代码就可以这样写

    if (((node || 0).nextSibling || 0).className == ...) {
       ...
    }

    --Split--

    就个人而言,上述的从某种角度而言,代码会非常的精简。但日常实际的编码过程中,尤其是多人配合的情况下,这些代码可能会给其他开发人员造成一定的困扰。

    正如 小马 所言,如果已经在使用某些框架,需要具体问题具体分析。比如上述的条件判断代码,使用 YUI 编码就可以使用

    YAHOO.util.Dom.hasClass(el, className)

    显得更加的精简,并且相比上述的代码更容易理解。

  • 函数是进行模块化程序设计的基础,编写复杂的Ajax应用程序,必须对函数有更深入的了解。javascript中的函数不同于其他的语言,每个函 数都是作为一个对象被维护和运行的。通过函数对象的性质,可以很方便的将一个函数赋值给一个变量或者将函数作为参数传递。在继续讲述之前,先看一下函数的 使用语法:
    以下为引用的内容:
    function func1(…){…}
    var func2=function(…){…};
    var func3=function func4(…){…};
    var func5=new Function();
      这些都是声明函数的正确语法。它们和其他语言中常见的函数或之前介绍的函数定义方式有着很大的区别。那么在JavaScript中为什么能这么写?它所遵循的语法是什么呢?下面将介绍这些内容。
      认识函数对象(Function Object)
      可以用function关键字定义一个函数,并为每个函数指定一个函数名,通过函数名来进行调用。在JavaScript解释执行时,函数都是被维护为一个对象,这就是要介绍的函数对象(Function Object)。
      函数对象与其他用户所定义的对象有着本质的区别,这一类对象被称之为内部对象,例如日期对象(Date)、数组对象(Array)、字符串对象 (String)都属于内部对象。这些内置对象的构造器是由JavaScript本身所定义的:通过执行new Array()这样的语句返回一个对象,JavaScript内部有一套机制来初始化返回的对象,而不是由用户来指定对象的构造方式。
      在JavaScript中,函数对象对应的类型是Function,正如数组对象对应的类型是Array,日期对象对应的类型是Date一样, 可以通过new Function()来创建一个函数对象,也可以通过function关键字来创建一个对象。为了便于理解,我们比较函数对象的创建和数组对象的创建。先看数组对象:下面两行代码都是创建一个数组对象myArray:
    以下为引用的内容:
    var myArray=[];
    //等价于
    var myArray=new Array();
    同样,下面的两段代码也都是创建一个函数myFunction:
    function myFunction(a,b){
          return a+b;
    }
    //等价于
    var myFunction=new Function("a","b","return a+b");
      通过和构造数组对象语句的比较,可以清楚的看到函数对象本质,前面介绍的函数声明是上述代码的第一种方式,而在解释器内部,当遇到这种语法时, 就会自动构造一个Function对象,将函数作为一个内部的对象来存储和运行。从这里也可以看到,一个函数对象名称(函数变量)和一个普通变量名称具有 同样的规范,都可以通过变量名来引用这个变量,但是函数变量名后面可以跟上括号和参数列表来进行函数调用。
      用new Function()的形式来创建一个函数不常见,因为一个函数体通常会有多条语句,如果将它们以一个字符串的形式作为参数传递,代码的可读性差。下面介绍一下其使用语法:
    以下为引用的内容:
    var funcName=new Function(p1,p2,...,pn,body);
      参数的类型都是字符串,p1到pn表示所创建函数的参数名称列表,body表示所创建函数的函数体语句,funcName就是所创建函数的名称。可以不指定任何参数创建一个空函数,不指定funcName创建一个无名函数,当然那样的函数没有任何意义。
      需要注意的是,p1到pn是参数名称的列表,即p1不仅能代表一个参数,它也可以是一个逗号隔开的参数列表,例如下面的定义是等价的:
    以下为引用的内容:
    new Function("a", "b", "c", "return a+b+c")
    new Function("a, b, c", "return a+b+c")
    new Function("a,b", "c", "return a+b+c")
      JavaScript引入Function类型并提供new Function()这样的语法是因为函数对象添加属性和方法就必须借助于Function这个类型。
    函数的本质是一个内部对象,由JavaScript解释器决定其运行方式。通过上述代码创建的函数,在程序中可以使用函数名进行调用。本节开头列出的函数定义问题也得到了解释。注意可直接在函数声明后面加上括号就表示创建完成后立即进行函数调用,例如:
    以下为引用的内容:
    var i=function (a,b){
           return a+b;
    }(1,2);
    alert(i);
      这段代码会显示变量i的值等于3。i是表示返回的值,而不是创建的函数,因为括号“(”比等号“=”有更高的优先级。这样的代码可能并不常用,但当用户想在很长的代码段中进行模块化设计或者想避免命名冲突,这是一个不错的解决办法。
      需要注意的是,尽管下面两种创建函数的方法是等价的:
    以下为引用的内容:
    function funcName(){
           //函数体
    }
    //等价于
    var funcName=function(){
           //函数体
    }
      但前面一种方式创建的是有名函数,而后面是创建了一个无名函数,只是让一个变量指向了这个无名函数。在使用上仅有一点区别,就是:对于有名函数,它可以出现在调用之后再定义;而对于无名函数,它必须是在调用之前就已经定义。例如:
    以下为引用的内容:
    <script language="JavaScript" type="text/javascript">
    <!--
    func();
    var func=function(){
           alert(1)
    }
    //-->
    </script>
      这段语句将产生func未定义的错误,而:
    以下为引用的内容:
    <script language="JavaScript" type="text/javascript">
    <!--
    func();
    function func(){
          alert(1)
    }
    //-->
    </script>
      则能够正确执行,下面的语句也能正确执行:
    以下为引用的内容:
    <script language="JavaScript" type="text/javascript">
    <!--
    func();
    var someFunc=function func(){
          alert(1)
    }
    //-->
    </script>
    由此可见,尽管JavaScript是一门解释型的语言,但它会在函数调用时,检查整个代码中是否存在相应的函数定义,这个函数名只有是通过function funcName()形式定义的才会有效,而不能是匿名函数。
    函数对象和其他内部对象的关系
      除了函数对象,还有很多内部对象,比如:Object、Array、Date、RegExp、Math、Error。这些名称实际上表示一个类 型,可以通过new操作符返回一个对象。然而函数对象和其他对象不同,当用typeof得到一个函数对象的类型时,它仍然会返回字符串 “function”,而typeof一个数组对象或其他的对象时,它会返回字符串“object”。下面的代码示例了typeof不同类型的情况:
    以下为引用的内容:
    alert(typeof(Function)));
    alert(typeof(new Function()));
    alert(typeof(Array));
    alert(typeof(Object));
    alert(typeof(new Array()));
    alert(typeof(new Date()));
    alert(typeof(new Object()));
      运行这段代码可以发现:前面4条语句都会显示“function”(可以理解为构造器,即构造一个对象的仪器),而后面3条语句则显示“object”(即已经被实例化了的构造器,即类的实例:对象),可见new一个function实 际上是返回一个函数。这与其他的对象有很大的不同。其他的类型Array、Object等都会通过new操作符返回一个普通对象。尽管函数本身也是一个对象,但它与普通的对象还是有区别的,因为它同时也是对象构造器,也就是说,可以new一个函数来返回一个对象,这在前面已经介绍。所有typeof返回 “function”的对象都是函数对象。也称这样的对象为构造器(constructor),因而,所有的构造器都是对象,但不是所有的对象都是构造器。
      既然函数本身也是一个对象,它们的类型是function,联想到C++、Java等面向对象语言的类定义,可以猜测到Function类型的作用所在,那就是可以给函数对象本身定义一些方法和属性,借助于函数的prototype对象,可以很方便地修改和扩充Function类型的定义,例如下面扩展了函数类型Function,为其增加了method1方法,作用是弹出对话框显示"function":
    以下为引用的内容:
    Function.prototype.method1=function(){
          alert("function");
    }
    function func1(a,b,c){
          return a+b+c;
    }
    func1.method1();
    func1.method1.method1();
      注意最后一个语句:func1.method1.mehotd1(),它调用了method1这个函数对象的method1方法。虽然看上去有 点容易混淆,但仔细观察一下语法还是很明确的:这是一个递归的定义。因为method1本身也是一个函数,所以它同样具有函数对象的属性和方法,所有对 Function类型的方法扩充都具有这样的递归性质。
      Function是所有函数对象的基础,而Object则是所有对象(包括函数对象)的基础。在JavaScript中,任何一个对象都是 Object的实例,因此,可以修改Object这个类型来让所有的对象具有一些通用的属性和方法,修改Object类型是通过prototype来完成 的:
    以下为引用的内容:
    Object.prototype.getType=function(){
           return typeof(this);
    }
    var array1=new Array();
    function func1(a,b){
          return a+b;
    }
    alert(array1.getType());
    alert(func1.getType());
    上面的代码为所有的对象添加了getType方法,作用是返回该对象的类型。两条alert语句分别会显示“object”和“function”。
    将函数作为参数传递
      在前面已经介绍了函数对象本质,每个函数都被表示为一个特殊的对象,可以方便的将其赋值给一个变量,再通过这个变量名进行函数调用。作为一个变量,它可以以参数的形式传递给另一个函数,这在前面介绍JavaScript事件处理机制中已经看到过这样的用法,例如下面的程序将func1作为参数传递给func2:
    以下为引用的内容:
    function func1(theFunc){
          theFunc();
    }
    function func2(){
          alert("ok");
    }
    func1(func2);
      在最后一条语句中,func2作为一个对象传递给了func1的形参theFunc,再由func1内部进行theFunc的调用。事实上,将函数作为参数传递,或者是将函数赋值给其他变量是所有事件机制的基础。
      例如,如果需要在页面载入时进行一些初始化工作,可以先定义一个init的初始化函数,再通过window.onload=init;语句将其绑定到页面载入完成的事件。这里的init就是一个函数对象,它可以加入window的onload事件列表。
      传递给函数的隐含参数:arguments
      当进行函数调用时,除了指定的参数外,还创建一个隐含的对象——arguments。arguments是一个类似数组但不是数组的对象,说它 类似是因为它具有数组一样的访问性质,可以用arguments[index]这样的语法取值,拥有数组长度属性length。arguments对象存储的是实际传递给函数的参数,而不局限于函数声明所定义的参数列表,例如:
    以下为引用的内容:
    function func(a,b){
         alert(a);
         alert(b);
         for(var i=0;i<arguments.length;i++){
               alert(arguments[i]);
         }
    }
    func(1,2,3);
      代码运行时会依次显示:1,2,1,2,3。因此,在定义函数的时候,即使不指定参数列表,仍然可以通过arguments引用到所获得的参 数,这给编程带来了很大的灵活性。arguments对象的另一个属性是callee,它表示对函数对象本身的引用,这有利于实现无名函数的递归或者保证 函数的封装性,例如使用递归来计算1到n的自然数之和:
    以下为引用的内容:
    var sum=function(n){
          if(1==n)return 1;
          else return n+sum(n-1);
    }
    alert(sum(100));
      其中函数内部包含了对sum自身的调用,然而对于JavaScript来说,函数名仅仅是一个变量名,在函数内部调用sum即相当于调用一个全局变量,不能很好的体现出是调用自身,所以使用arguments.callee属性会是一个较好的办法:(卡卡..是手册上类似的例子..)
    以下为引用的内容:
    var sum=function(n){
          if(1==n)return 1;
          else return n+arguments.callee(n-1);
    }
    alert(sum(100));
      callee属性并不是arguments不同于数组对象的惟一特征,下面的代码说明了arguments不是由Array类型创建:
    以下为引用的内容:
    Array.prototype.p1=1;
    alert(new Array().p1);
    function func(){
           alert(arguments.p1);
    }
    func();
      运行代码可以发现,第一个alert语句显示为1,即表示数组对象拥有属性p1,而func调用则显示为“undefined”,即p1不是arguments的属性,由此可见,arguments并不是一个数组对象。
      函数的apply、call方法和length属性
      JavaScript为函数对象定义了两个方法:apply和call,它们的作用都是将函数绑定到另外一个对象上去运行,两者仅在定义参数的方式有所区别:
    以下为引用的内容:
    Function.prototype.apply(thisArg,argArray);
    Function.prototype.call(thisArg[,arg1[,arg2…]]);
      从函数原型可以看到,第一个参数都被取名为thisArg,即所有函数内部的this指针都会被赋值为thisArg,这就实现了将函数作为另 外一个对象的方法运行的目的。两个方法除了thisArg参数,都是为Function对象传递的参数。下面的代码说明了apply和call方法的工作 方式:
    以下为引用的内容:
    //定义一个函数func1,具有属性p和方法A
    function func1(){
          this.p="func1-";
          this.A=function(arg){
                alert(this.p+arg);
          }
    }
    //定义一个函数func2,具有属性p和方法B
    function func2(){
          this.p="func2-";
          this.B=function(arg){
                 alert(this.p+arg);
          }
    }
    var obj1=new func1();
    var obj2=new func2();
    obj1.A("byA");    //显示func1-byA
    obj2.B("byB");    //显示func2-byB
    obj1.A.apply(obj2,["byA"]); //显示func2-byA,其中[“byA”]是仅有一个元素的数组,下同
    obj2.B.apply(obj1,["byB"]); //显示func1-byB
    obj1.A.call(obj2,"byA"); //显示func2-byA
    obj2.B.call(obj1,"byB"); //显示func1-byB
      可以看出,obj1的方法A被绑定到obj2运行后,整个函数A的运行环境就转移到了obj2,即this指针指向了obj2。同样obj2的函数B也可以绑定到obj1对象去运行。代码的最后4行显示了apply和call函数参数形式的区别。
      与arguments的length属性不同,函数对象还有一个属性length,它表示函数定义时所指定参数的个数,而非调用时实际传递的参数个数。例如下面的代码将显示2:
    以下为引用的内容:
    function sum(a,b){
          return a+b;
    }
    alert(sum.length);
    深入认识JavaScript中的this指针
      this指针是面向对象程序设计中的一项重要概念,它表示当前运行的对象。在实现对象的方法时,可以使用this指针来获得该对象自身的引用。
      和其他面向对象的语言不同,JavaScript中的this指针是一个动态的变量,一个方法内的this指针并不是始终指向定义该方法的对象的,在上一节讲函数的apply和call方法时已经有过这样的例子。为了方便理解,再来看下面的例子:
    以下为引用的内容:
    <script language="JavaScript" type="text/javascript">
    <!--
    //创建两个空对象
    var obj1=new Object();
    var obj2=new Object();
    //给两个对象都添加属性p,并分别等于1和2
    obj1.p=1;
    obj2.p=2;
    //给obj1添加方法,用于显示p的值
    obj1.getP=function(){
          alert(this.p); //表面上this指针指向的是obj1
    }
    //调用obj1的getP方法
    obj1.getP();
    //使obj2的getP方法等于obj1的getP方法
    obj2.getP=obj1.getP;
    //调用obj2的getP方法
    obj2.getP();
    //-->
    </script>
      从代码的执行结果看,分别弹出对话框显示1和2。由此可见,getP函数仅定义了一次,在不同的场合运行,显示了不同的运行结果,这是有 this指针的变化所决定的。在obj1的getP方法中,this就指向了obj1对象,而在obj2的getP方法中,this就指向了obj2对 象,并通过this指针引用到了两个对象都具有的属性p。
      由此可见,JavaScript中的this指针是一个动态变化的变量,它表明了当前运行该函数的对象。由this指针的性质,也可以更好的理解JavaScript中对象的本质:一个对象就是由一个或多个属性(方法)组成的集合。每个集合元素不是仅能属于一个集合,而是可以动态的属于多个集合。这样,一个方法(集合元素)由谁调用,this指针就指向谁。实际上,前面介绍的apply方法和call方法都是通过强制改变this指针的值来实现的,使this指针指向参数所指定的对象,从而达到将一个对象的方法作为另一个对象的方法运行。
      每个对象集合的元素(即属性或方法)也是一个独立的部分,全局函数和作为一个对象方法定义的函数之间没有任何区别,因为可以把全局函数和变量看作为window对象的方法和属性。也可以使用new操作符来操作一个对象的方法来返回一个对象,这样一个对象的方法也就可以定义为类的形式,其中的 this指针则会指向新创建的对象。在后面可以看到,这时对象名可以起到一个命名空间的作用,这是使用JavaScript进行面向对象程序设计的一个技巧。例如:
    以下为引用的内容:
    var namespace1=new Object();
    namespace1.class1=function(){
         //初始化对象的代码
    }
    var obj1=new namespace1.class1();
      这里就可以把namespace1看成一个命名空间。
      由于对象属性(方法)的动态变化特性,一个对象的两个属性(方法)之间的互相引用,必须要通过this指针,而其他语言中,this关键字是可以省略的。如上面的例子中:
    以下为引用的内容:
    obj1.getP=function(){
          alert(this.p); //表面上this指针指向的是obj1
    }
    这里的this关键字是不可省略的,即不能写成alert(p)的形式。这将使得getP函数去引用上下文环境中的p变量,而不是obj1的属性。
  • 1. 当一个对象为不可见时,在IE中是不可以设置它集中焦点的,但是在Firefox里可以
    2. 向表(Table)追加行:
    在FF、 Safari、Opera等浏览器中,用document.createElement创建行后用document.appendChild将行直接添加到表上。但是在IE里不可以,而且没有任何错误提示,这时候,需要为表添加表体(tbody),然后将新创建的行添加到表体(tbody),
    3. childNodes的不同:Firefox把回车后的空白当作文本节点,而ie不是
    4. innerText是IE专有的方法,textContent是Firefox专有的,innerHTML则两者都兼容
    5. 设置某个node对象的style class名称。
    ie中要设置某个node的class用”className”作为attr来set或者get。
    ff等其它的浏览器用”class”作为attr来set或者get。
    6. 事件对象。ie用eventff用evnt
    7. 事件作用对象。ie用objEvent.srcElement,ff用objEvent.target
    8,document.form.item 问题。
    ie中用document.formName.item("itemName")
    ff中改用 document.formName.elements["elementName"]
    9。集合类对象取用时使用 (),IE 能接受,MF 不能。解决方法:改用 [] 作为下标运算
    10,window.event 无法在 fF 上运行
    11,HTML 对象的 id 作为对象名的问题
    现有问题:在 IE 中,HTML 对象的 ID 可以作为 document 的下属对象变量名直接使用。在 fF 中不能。
    解决方法:用 getElementById("idName") 代替 idName 作为对象变量使用
    11,变量名与某 HTML 对象 id 相同的问题
    现有问题: fF中,对象 id 不作为 HTML 对象的名称,所以可使用与 HTML 对象 id 相同的变量名,IE 中不能
    解决方法:在声明变量时,一律加上 var ,以避免歧义,这样在 IE 中亦可正常运行。此外,最好不要取与 HTML 对象 id 相同的变量名,以减少错误
    12,event.x 与 event.y 问题
    现有问题:在IE 中,event 对象有 x, y 属性,fF中没有。
    解决方法:
    在fF中,与event.x 等效的是 event.pageX。但event.pageX IE中没有。
    故采用 event.clientX 代替 event.x。在IE 中也有这个变量。
    event.clientX 与 event.pageX 有微妙的差别(当整个页面有滚动条的时候),不过大多数时候是等效的。
    如果要完全一样,可以稍麻烦些:mX = event.x ? event.x : event.pageX;然后用 mX 代替 event.x
    13,父结点的问题
    在 ff中没有 parentElement parement.children 而用 parentNode parentNode.childNodes。childNodes的下标的含义在IE和fF中不同,fF使用DOM规范,childNodes中会插入空白文本节点。一般可以通过node.getElementsByTagName()来回避这个问题。
    14,const 问题
    现有问题:在 IE 中不能使用 const 关键字。如 const constVar = 32; 在IE中这是语法错误。
    解决方法:不使用 const ,以 var 代替。
    15,body 对象
    fF的body在body标签没有被浏览器完全读入之前就存在,而IE则必须在body完全被读入之后才存在
    16,nodeName 和 tagName 问题
    现有问题:在ff中,所有节点均有 nodeName 值,但 textNode 没有 tagName 值。在 IE 中,nodeName 的使用好象有问题。
    解决方法:使用 tagName,但应检测其是否为空
    17,元素属性
    IE下 input.type属性为只读,但是ff下可以修改
    18,document.getElementsByName() 和 document.all[name] 的问题
    现有问题:在 IE 中,getElementsByName()、document.all[name] 均不能用来取得 div 元素(是否还有其它不能取的元素还不知道)。
    19, 对空格符的处理。按照HTML的标准,空格字符是 。在ff中,如果你误写成&nbsp(少了一个分号)一定不会被FireFox认为是空格,FireFox会认为它是&nbsp。而在IE中,如果你误写成&nbsp(少了一个分号)IE智能地认为它是空格。
    20,对注释的处理。
    按照HTML的标准,注释的操作是放在<!--和-->之间的,而且注释中不能有--,否则会产生 HTML解析错误
  • 1、给DOM对象添加的属性是一个对象的引用。
    范例:    
    var MyObject = {};    
    document.getElementById('myDiv').myProp = MyObject;    
    解决方法:    
    在window.onunload事件中写上: document.getElementById('myDiv').myProp = null;    
      
      
    2、DOM对象与JS对象相互引用。
    范例:    
    function Encapsulator(element) {    
      this.elementReference = element;    
      element.myProp = this;    
    }    
    new  Encapsulator(document.getElementById('myDiv'));    
    解决方法:    
    在onunload事件中写上: document.getElementById('myDiv').myProp = null;    
      
      
    3、给DOM对象用attachEvent绑定事件。
    范例:    
    function doClick() {}    
    element.attachEvent("onclick", doClick);    
    解决方法:    
    在onunload事件中写上: element.detachEvent('onclick', doClick);    
      
      
    4、从外到内执行appendChild。这时即使调用removeChild也无法释放。
    范例:    
    var parentDiv =  document.createElement("div");    
    var childDiv = document.createElement("div");    
    document.body.appendChild(parentDiv);    
    parentDiv.appendChild(childDiv);    
    解决方法:    
    从内到外执行appendChild:    
    var parentDiv =  document.createElement("div");    
    var childDiv = document.createElement("div");    
    parentDiv.appendChild(childDiv);    
    document.body.appendChild(parentDiv);    
      
      
    5、反复重写同一个属性会造成内存大量占用(但关闭IE后内存会被释放)。
    范例:    
    for(i = 0; i < 5000; i++) {    
      hostElement.text = "asdfasdfasdf";    
    }    
    这种方式相当于定义了5000个属性!    
    解决方法:    
    其实没什么解决方法:P~~~就是编程的时候尽量避免出现这种情况咯~~    
      
      
    说明:    
    1、以上资料均来源于微软官方的MSDN站点,链接地址:    
    http://msdn.microsoft.com/librar ... e_leak_patterns.asp    
    大家可以到上面这个地址中看到详细的说明,包括范例和图例都有。只是我英文不太好,看不太懂,如果我上述有失误或有需要补充的地方请大家指出。    
      
    2、对于第一条,事实上包括 element.onclick = funcRef 这种写法也算在其中,因为这也是一个对对象的引用。在页面onunload时应该释放掉。    
      
    3、对于第三条,在MSDN的英文说明中好像是说即使调用detachEvent也无法释放内存,因为在attachEvent的时候就已经造成内存“LEAK”了,不过detachEvent后情况还是会好一点。不知道是不是这样,请英文好的亲能够指出。    
      
    4、在实际编程中,这些内存问题的实际影响并不大,尤其是给客户使用时,客户对此绝不会有察觉,然而这些问题对于程序员来说却始终是个心病 --- 有这样的BUG心里总会觉得不舒服吧?能解决则给与解决,这样是最好的。事实上我在webfx.eae.net这样顶级的JS源码站点中,在它们的源码里都会看到采用上述解决方式进行内存的释放管理。  
  • 【原文】:http://javascript.crockford.com/memory/leak.html

    【译文】:

    JScript的内存泄漏

    当一个系统不能正确的管理他的内存分配时,这个系统就存在内存泄漏,这对系统来说是一个bug。内存泄漏的现象可以有程序调用失败、执行减慢等。

    微软的Internet Explorer就存在一系列的内存泄漏,其中最严重的就算是执行Jscript时产生的泄漏。当一个DOM对象包涵有一个JavaScript对象(例如一个事件处理函数)的引用,同时如果这个JavaScript对象又包涵该DOM对象,那么这个循环引用就形成了。这种结构本质上没有问题。此时,因为该DOM对象和这个事件处理函数并没有别的引用存在,那么垃圾回收器(一种自动的内存资源管理器)本应该把它们都回收点,并内存释放。JavaScript的垃圾回收器能够检测到这种循环引用,并不会对他产生困惑。但是不幸的是,IE DOM的内存并不能被Jscript所管理。他有他自己的内存管理系统,然而这套系统并不知道循环引用,使得一切都变得混乱。这就导致了,当循环引用形成的时候,内存释放工作不能完成,也就是产生了内存泄漏。长时间的内存泄漏就将产生内存的匮乏,使得浏览器因缺乏必要内存而崩溃?

    我们可以来演示一下。在第一段程序-question1中,我们将动态创建以10个为一组共计10000个的DOM元素(<span>),创建10个然后删除在创建10个,如此循环。你在运行这段程序时打开Windows任务管理器,就可以观察到页面运行时PF(虚拟内存)使用率一直保持不变。PF使用率的变化可以视为内存分配是否无效的指标。

     

    Question1

    <html>
        <head>
            <title>Queue Test 1</title>
        </head>
        <body>
            <script>
                /*global setTimeout */
                (function (limit, delay) {
                    var queue = new Array(10);
                    var n = 0;
     
                    function makeSpan(n) {
                        var s = document.createElement('span');
                        document.body.appendChild(s);
                        var t = document.createTextNode(' ' + n);
                        s.appendChild(t);
                        return s;
                    }
     
                    function process(n) {
                        queue.push(makeSpan(n));
                        var s = queue.shift();
                        if (s) {
                            s.parentNode.removeChild(s);
                        }
                    }
     
                    function loop() {
                        if (n < limit) {
                            process(n);
                            n += 1;
                            setTimeout(loop, delay);
                        }
                    }
     
                    loop();
                })(10000, 10);
            </script>
        </body>
    </html>
     

    接下来我们运行第二段程序queuetest2。除了做与queuetest1相同的事情以外,它还未每个元素添加了一个点击事件响应函数。在MozilaOpera上,虚拟PF利用率和queuetest1是一样的,但是在IE上我们可以看见由于内存泄漏而产生的每秒一兆的虚拟内存的稳定增量,通常这种泄露都不会被注意到。但是由于Ajax的日益流行,使得页面在浏览器的停留时间增长,使得问题变得常见了。

    Question2

    <html>

        <head><title>Queue Test 2</title>

        </head>

        <body>

            <script>

                /*global setTimeout */

                (function (limit, delay) {

                    var queue = new Array(10);

                    var n = 0;

     

                    function makeSpan(n) {

                        var s = document.createElement('span');

                        document.body.appendChild(s);

                        var t = document.createTextNode(' ' + n);

                        s.appendChild(t);

                        s.onclick = function (e) {

                            s.style.backgroundColor = 'red';

                            alert(n);

                        };

                        return s;

                    }

     

                    function process(n) {

                        queue.push(makeSpan(n));

                        var s = queue.shift();

                        if (s) {

                            s.parentNode.removeChild(s);

                        }

                    }

     

                    function loop() {

                        if (n < limit) {

                            process(n);

                            n += 1;

                            setTimeout(loop, delay);

                        }

                    }

     

                    loop();

                })(10000, 10);

            </script>

        </body>

    </html>

     

    因为IE不能对循环引用进行回收,所以这个任务就落在了我们的肩上。如果我们明确的打破这个循环引用,那么IE就能够完成垃圾回收工作了。具微软的解释,引起内存泄漏的原因是闭包,然而这个结论肯定是非常错误的,并且这使得微软给开发者的建议也成了错误的建议。那么通过DOM来打破循环引用更简单,因为实际上不可能通过Jscript来实现。

    当我们处理完一个元素后,我们必须通过把它所有的事件处理函数制空来达到破坏循环引用的目的。我们所需要做的就是把每个事件的处理函数设为空就可以了。我们甚至可以清理函数来完成这一工作。

    清理函数将保存一份DOM元素的引用。它将循环检测这个元素的所有属性。如果发现了时间处理函数,就把它值为空。这样就破坏了循环引用,使得内存可以被回收释放。它同样也会检测该元素的子元素,打破他们的循环引用。这个清理函数,只在IE中有效果,对于MozillaOpera都无效。不管是用removeChild()或者是设置innerHTML属性的值,都应该在删除元素之前调用清理函数。

    function purge(d) {
        var a = d.attributes, i, l, n;
        if (a) {
            l = a.length;
            for (i = 0; i < l; i += 1) {
                n = a[i].name;
                if (typeof d[n] === 'function') {
                    d[n] = null;
                }
            }
        }
        a = d.childNodes;
        if (a) {
            l = a.length;
            for (i = 0; i < l; i += 1) {
                purge(d.childNodes[i]);
            }
        }
    }
    那么我们现在来运新第3个程序,queuetest3,在程序3里,元素在被删除之前都调用了清理函数。

    Question3

    <html>

        <head><title>Queue Test 3</title>

        </head>

        <body>

            <p>

                Queue Test 3 adds an event handler to each span, and removes it when

    finished. See <a href="http://www.crockford.com/javascript/memory/leak.html">http://www.crockford.com/javascript/memory/leak.html</a>

            </p>

            <script>

                /*global onunload, setTimeout */

                (function (limit, delay) {

                    var queue = new Array(10);

                    var n = 0;

     

                    function makeSpan(n) {

                        var s = document.createElement('span');

                        document.body.appendChild(s);

                        var t = document.createTextNode(' ' + n);

                        s.appendChild(t);

                        s.onclick = function (e) {

                            s.style.backgroundColor = 'red';

                            alert(n);

                        };

                        return s;

                    }

     

                    function purge(d) {

                        var a = d.attributes, i, l, n;

                        if (a) {

                            l = a.length;

                            for (i = 0; i < l; i += 1) {

                                n = a[i].name;

                                if (typeof d[n] === 'function') {

                                    d[n] = null;

                                }

                            }

                        }

                        a = d.childNodes;

                        if (a) {

                            l = a.length;

                            for (i = 0; i < l; i += 1) {

                                purge(d.childNodes[i]);

                            }

                        }

                    }

     

                    function process(n) {

                        queue.push(makeSpan(n));

                        var s = queue.shift();

                        if (s) {

                            purge(s);

                            s.parentNode.removeChild(s);

                        }

                    }

     

                    function loop() {

                        if (n < limit) {

                            process(n);

                            n += 1;

                            setTimeout(loop, delay);

                        }

                    }

                    onunload = function (e) {

                        purge(document.body);

                    };

     

                    loop();

                })(10000, 10);

            </script>

        </body>

    </html>

     

    更新:微软发布了该问题的补丁:929874。如果你有十足的信心确保你所有的用户都可以获得该更新,那么你将不再需要上面的清理函数。但不幸的是,这不可能如你所愿,所以可能清理工作在IE6被淘汰之前还是有必要的。

    这就是web的天性,有清理不完的bugs

     

  • 当你的弹出窗口被IE拦截后你该怎么办?
    很早以前用GMail的时候就发下,GMail会在他的弹出窗被拦截的时候提示用户,如果需要使用改功能请暂时允许弹出窗。很想把它的代码找到,可是去GMail的js里面找东西似乎不是一件什么好事。昨天用yahoo!mail的时候又遇到了。郁闷了。因为去Yahoo的js中找东西同样不是一个主意。
    自己想咯,结果静下来一下,其实很简单。。。。。
    <script>
        /*......*/
        var handle=window.open('.......');
        if(!handle){
            alert('我们的窗口被拦啦~~~')
        }
    </script>
    that's it....
    呵呵~

  • 来自:http://www.machinetown.cn/?p=163

    《JSONP》

    浏览器的安全机制使得XMLHttpRequest,iframe之类的控件不能进行跨域访问,从安全的角度讲,这也倒是无可厚非的事情,但是它的确限制了分布式web应用。

    要解决跨域访问限制带来的问题,通常我们有3种策略:
    本地服务器代理,flash,script标签

    我这里提出一种新的非标准方法用script实现跨域访问:JSON with Padding, or simply JSONP

    JSONP工作方式很简单,但是需要服务器的支持。首先要由客户端提交一段json格式的文本,然后你将其用小括号包起来,从而得到一个合法的js文档(也可能仅仅是一个js函数请求).

    客户端将jsonp关键字加入到请求文本的前面。
    很简单,通过一个空 的jsonp参数,返回的结果就是通过小括号包起来的json数据。

    让我们看看del.icio.us JSON API 的例子:

    例子中使用的script标签的src属性是这样的:
    http://del.icio.us/feeds/json/bob/mochikit+interpreter
    得到的结果:
    if(typeof(Delicious) == ‘undefined’) Delicious = {};
    Delicious.posts = [{
    “u”: “http://mochikit.com/examples/interpreter/index.html”,
    “d”: “Interpreter - JavaScript Interactive Interpreter”,
    “t”: [
    “mochikit”,”webdev”,”tool”,”tools”,
    “javascript”,”interactive”,”interpreter”,”repl”
    ]
    }]

    按照JSONP,上面的请求在语义上等同于下面的URL:
    http://del.icio.us/feeds/json/bob/mochikit+interpreter?jsonp=if(typeof(Delicious)%3D%3D%27undefined%27)Delicious%3D%7B%7D%3BDelicious.posts%3D

    这个uRL本身没什么意思,不过如果我们想要知道什么时候文档可用的时候呢?下面用过例子来跟踪一下:

    var delicious_callbacks = {};
    function getDelicious(callback, url) {
    var uid = (new Date()).getTime();
    delicious_callbacks[uid] = function () {
    delete delicious_callbacks[uid];
    callback();
    };
    url += “?jsonp=” + encodeURIComponent(”delicious_callbacks[” + uid + “]”);
    // add the script tag to the document, cross fingers
    };

    getDelicious(doSomething, “http://del.icio.us/feeds/json/bob/mochikit+interpreter”);

    这个试验中的URL是类似这样的:
    http://del.icio.us/feeds/json/bob/mochikit+interpreter?jsonp=delicious_callbacks%5B12345%5D

    delicious_callbacks[12345]([{
    “u”: “http://mochikit.com/examples/interpreter/index.html”,
    “d”: “Interpreter - JavaScript Interactive Interpreter”,
    “t”: [
    “mochikit”,”webdev”,”tool”,”tools”,
    “javascript”,”interactive”,”interpreter”,”repl”
    ]
    }])

    看看,因为我们使用了圆括号,所以JSONP请求能够传入到函数中。

    ps:我在官方文档中看到的参数不是jsonp,而是callback,Yahoo !Map中用的也是callback

  • 滚动鼠标中间实现图片大小缩放,其实代码很简单,主要是看你知不知道两个属性
    一个是DOM中的 event.wheelDelta 文章最后附上官方的解释
    另一个是CSS中 zoom 解释同样在最后附上
    实现的代码如下:

    <html>
    <body>
    <script language="javascript">
    function wheel(o){
         var zoom=parseInt(o.style.zoom)||100;
         zoom += event.wheelDelta/12;
         if (zoom>0) o.style.zoom=zoom+'%';
         return false;
     }
    </script>
    <img src="http://madinsect.blogbus.com/files/11931286130.gif
    " onmousewheel="return wheel(this)">
    </body>
    </html>

    ////////////////////////
    /*        解释        */
    ///////////////////////

    wheelDelta Property

    Internet Development Index

    Retrieves the distance and direction the wheel button has rolled.

    Syntax

    [ p = ] event.wheelDelta

    Possible Values

    pInteger that receives the distance and direction that the wheel button has rolled.

    The property is read-only. The property has no default value.

    Expressions can be used in place of the preceding value(s), as of Microsoft® Internet Explorer 5. For more information, see About Dynamic Properties.

    Remarks

    This property indicates the distance that the wheel has rotated, expressed in multiples of 120. A positive value indicates that the wheel has rotated away from the user. A negative value indicates that the wheel has rotated toward the user.

    This property is used with the onmousewheel event.

     

    zoom Attribute | zoom Property

    Internet Development Index

    Sets or retrieves the magnification scale of the object.

    Syntax

    HTML{ zoom : vMagnification }
    Scriptingobject.style.zoom [ = vMagnification ]

    Possible Values

    vMagnificationVariant that specifies or receives one of the following values.
    normalDefault. Magnification scale is normal. The object renders normal size.
    numberFloating-point number that specifies the magnification scale, where 1.0 is normal.
    percentageInteger, followed by a %. The value is a percentage of the magnification scale, where 100% is normal.

    The property is read/write for all objects except the following, for which it is read-only: currentStyle. The property has a default value of normal. The Microsoft Cascading Style Sheets (CSS) extension is not inherited.

    Expressions can be used in place of the preceding value(s), as of Microsoft® Internet Explorer 5. For more information, see About Dynamic Properties.

    Remarks

    Setting the value of the zoom property on a rendered object causes the content that surrounds the object to reflow.

    Even though the zoom property is not inherited, it affects its children. This effect is similar to the transformation caused by the background and filter properties.

  •     现在很多网站都做了例如用户登录等操作的用户体验优化-在网页上盖住一个层,在该层上浮现一个登陆框,这种方式已经屡见不鲜了。

        今天看了一篇很好的文章,上面对IE/WIN专有属性:hasLayout的研究 做了详细的说明。其中就设计到了下面获取窗口大小函数中用到的document.documentElement.clientHeight

    先帖出获取当前窗口大小的函数给新同志:
    function getWindowSize(){
        return {
                    Width:Math.max(document.body.scrollWidth,document.documentElement.clientWidth),
                    Height:Math.max(document.body.scrollHeight,document.documentElement.clientHeight)
                 }
    }

        文中说:“如果一个元素没有“layout”,那么clientWidth/clientHeight 总是返回0。这会让一些脚本新手感到困惑,而且这和 Mozilla 浏览器的处理方式也不一样。不过我们可以利用这一点在 IE5.0 中检测“layout”:如果 clientWidth 是零那么这个元素就没有 layout。”

    那么hasLayout到底是什么呢?这里只是引导大家自己去探究,只把译文的地址链接帖出来给大家吧~http://bbs.blueidea.com/viewthread.php?tid=2636904(本来帖原文地址的,可是试了试打不开)

  • 今天听人介绍了这个东东貌似还不错,(哎,一般都是我介绍东西给别人的,不行,该注意了)还没细看,转了在说。

    -*-*-*-*-*-*-
    JQuery是继prototype之后又一个优秀的Javascript框架。对prototype我使用不多,简单了解过。但使用上jquery之后,马上被她的优雅吸引住了。有人使用这样的一比喻来比较prototype和jquery:prototype就像Java,而jquery就像ruby.实际上我比较喜欢java(少接触Ruby罢了)但是jquery的简单的实用的确有相当大的吸引力啊!在项目里我把jquery作为自已唯一的框架类包。使用其间也有一点点心得,其实这些心得,在jquery的文档上面也可能有讲,不过还是记下来,以备忘罢。
    一,找到你了!
    还记得$()这个东西吧?prototype还是DWR都使用了这个函数代替document.getElementById()。没错,jquery也跟风了。为达到document.getElementById()的目的,jquery是这样写的:

    代码
    1. var someElement = $("#myId");  

    看起来比其他两个框架的要多了一个#,好,看看下面的用法:

     

    代码
    1. $("div p");(1)   
    2. $("div.container")(2)   
    3. $("div #msg");(3)   
    4. $("table a",context);(4)   

    在prototype里看过这样的写法吗?第一行代码得到所有<div>标签下的<p>元素。第二行代码得到class为container的<div>元素,第三行代码得到<div>标签下面id为msg的元素。第四行代码得到context为上下文的table里面所有的连接元素。
    如果你熟悉CSS,Xpath,你会觉得这些写法很眼熟!对了。正是。看出奥妙了吧。jquery就是通过这样的方式来找到Dom对象里面的元素。跟CSS的选择器相类似。
    二,Jquery对象?
    jquery提供了很多便利的函数,如each(fn),但是使用这些函数的前提是:你使用的对象是Jquer对象。使一个Dom对象成为一个Jquery对象很简单,通过下面一些方式(只是一部分):
    代码
    1. var a = $("#cid");(1)   
    2. var b = $("<p>hello</p>");(2)   
    3. var c = document.createElement("table"); var tb = $(c);   

    三,代替body标签的onload
    这个惯例,也许是除了$()之外,用得最多的地方了。下面一段代码:
    代码
    1. $(document).ready(function(){   
    2.   alert("hello");   
    3. });(1)   
    4.   
    5. <body onload="alert('hello');">(2)   
    6.   

    上面两段代码是等价的。但代码1的好处是做到表现和逻辑分离。并且可以在不同的js文件中做相同的操作,即$(document).ready(fn)可以在一个页面中重复出现,而不会冲突。基本上Jqeury的很多plugin都是利用这个特性,正因为这个特性,多个plugin共同使用起来,在初始化时不会发生冲突。
    不管怎么说,这个惯例可以分离javascript与HTML。推荐使用。
    四,事件机制
    我大量使用的事件可能就是button的onclick了。以前习惯在input 元素上写onclick = "fn()",使用jquery可以使javascript代码与html代码分离,保持HTML的清洁,还可以很轻松地绑定事件,甚至你可以不知道“事件”这个名词。
    代码
    1. $(document).ready(function(){   
    2.   $("#clear").click(function(){   
    3.      alert("i am about to clear the table");     
    4.    });   
    5.   $("form[0]").submit(validate);   
    6. });   
    7. function validate(){   
    8.   //do some form validation   
    9. }   

    五,同一函数实现set&get
    代码
    1. $("#msg").html();   
    2. $("#msg").html("hello");   

    上面两行代码,调用了同样的函数。但结果却差别很大。
    第一行是返回指定元素的HTML值,第二行则是将hello这串字符设置到指定元素中。jquery的函数大部分有这样的特性。
    六,ajax
    这是一个ajax横行的时代。多少人,了不了解ajax的都跟着用上一把。呵。使用jquery实现ajax同样简单异常
    代码
    1. $.get("search.do",{id:1},rend);   
    2. function rend(xml){   
    3.     alert(xml);   
    4. } (1)   
    5. $.post("search.do",{id:1},rend);   
    6. function rend(xml){   
    7.     alert(xml);   
    8. } (2)   
    9.   
    10. $("#msg").ajaxStart(function(){   
    11.    this.html("正在加载。。。。");   
    12. });(3)   
    13. $("#msg").ajaxSuccess(function(){   
    14.    this.html("加载完成!");   
    15. });(4)   

    这些都是较常用的方法,get和post用法一样。第一个参数是异步请求的url,第二个为参数,第三个回调方法。
    3,4的方法会在指定的Dom对象上绑定响应ajax执行的事件。当然,jquery的AJAX相关的函数不仅是这些,有兴趣可以去研究再多。
    七,渐入淡出
    代码
    1. $("#msg").fadeIn("fast");   
    2. $("#msg").fadeOut("slow");   

    没错,上面两行代码已经分别实现了一个id为Msg的jquery对象的渐入和淡出。做一个像Gmail一样的动态加载通知条,用jquery就那么简单。两个函数接受的参数除了快慢等,还可以接收整型,作为渐入或淡出的完成时间,单位为MS。
    八,plugin
    这也是一个插件的时代。
    jquery插件给我的感觉清一色的清洁,简单。如Jtip,要使用它的功能,只需要在你的元素的class上加上Jtip,并引入jtip.js及其样式即可以了。其他事情插件全包。我喜欢jquery的一个重要原因是发现她已经有了很多很好,很精彩的插件。

     

    写得很烂。可能大家看不出jquery的好处。嗯,光听是没用的,试用一下吧。你会发觉很有趣。
    暂时告一段落吧。待有新的发现再来分享。

    加一些Jquery的资源:
    http://www.visualjquery.com/index.xml 很好的API查询站点
    http://jquery.com/demo/thickbox/ 知道lightBox吧,看看Jquery是怎样实现相同的东西
    http://www.codylindley.com/blogstuff/js/jtip/ Jtip,实用的提示工具
    http://jquery.com/plugins/ 很多牛的插件。
    http://15daysofjquery.com/ jquery 的15天教程