zoukankan      html  css  js  c++  java
  • 深入剖析js命名空间函数namespace

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>把生成命名空间的方法绑定在jQuery上</title>
        <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
        <script type="text/javascript">
        // 把生成命名空间的方法绑定在jQuery上  
        jQuery.namespace = function() {  
            var a=arguments, o=null, i, j, d;    
            for (i=0; i<a.length; i=i+1) {        
                d=a[i].split(".");        
                o=jQuery;        
                for (j=(d[0] == "jQuery") ? 1 : 0; j<d.length; j=j+1) {            
                    o[d[j]]=o[d[j]] || {};            
                    o=o[d[j]];        
                    }    
                }    
                return o;
            };  
        // 定义命名空间  
        jQuery.namespace( 'jQuery.first.hello' );  
        jQuery.first.hello = function() {  
            alert( 'My first hello function' );  
        };  
        //调用
        jQuery.first.hello();  
        </script>
    </head>
    <body>
     
    </body>
    </html>

    生成命名空间的方法绑定到jquery上

    阿里员工写的开源数据库连接池的druid的源代码时,发现了其中在jquery的原代码中又定义了一个命名空间的函数:$.namespace(),其代码如下:

    <script type="text/javascript" src="js/jquery-1.11.1.min.js"></script> <script type="text/javascript"> $.namespace("druid.index"); druid.index=function(){ var i,j; // 定义变量 return { login:function(){ //login 方法的实现 }, submit:function(){ // submit 方法的实现 } } } //使用命名空间的函数 druid.index.login(); druid.index.submit();

     这样的话,就不会在全局变量区,引入很多的函数,将所有要使用的函数已经变量都放入了命名空间druid.index中,避免了不同js库中的函数名的冲突。

    但是namespace函数的定义如何理解呢?

    $.namespace = function() {  
           var a=arguments, o=null, i, j, d;  
           for (i=0; i<a.length; i=i+1) {  
              d=a[i].split(".");  
              o=window;  
              for (j=0; j<d.length; j=j+1) {  
                 o[d[j]]=o[d[j]] || {};  
                 o=o[d[j]];  
              }  
           }  
        return o;  
    };

    思考了很久,思考的过程明白一个额外的知识点:window这个引用的是不可覆盖的。比如我们看下面的代码:

    console.log(window);
    window = {};
    console.log(window);
    window = null;
    console.log(window);
    window = undefined;
    console.log(window);

    打印的结果都是 window, 而不会是 null 或者 undefined。也就是说window这个名称,实质上是个引用或者说指针,他指向heap上的全局window对象,stack上的window引用指向heap上的全局window对象,这个指向关系是不可覆盖,不可修改的。上面我修改了stack上的window,视图让他指向Null对象,但是修改是无效的。

    我们利用firebug来调试看看命名空间到底是如何实现的,我们一步一步的接近目标,先看如下代码:

    (function(){  
        var o = window;
        console.log(o);    // 打印Window    
        o.druid={};
        console.log(o);    // 打印Window
        console.log(o.druid);    // 打印 Object {}
    })(); 

    firebug中显示的对象为:

    上面这个结果应该很好理解,因为 o指向了window,所以o.index = {}; 也就相当于 window.index = {}; 在window上定义了一个名叫index的对象。

    下面我们在上面的代码上加码,在前进一步,接着看:

    (function(){  
        var o = window;
        console.log(o);    // 打印Window    
        o.druid={};
        console.log(o);    // 打印Window
        console.log(o.druid);    // 打印 Object {}
    
        o = o.druid;
        console.log(o);    // 打印 Object {}
        console.log(window);    // 打印Window
        console.log(o.druid);    // 打印  undefined
    })();

     对应firebug中对象和上一步一样,没有变化:

    上面的代码中:o = o.druid; 之后,因为 o 是指向 window,为什么console.log(o);  打印 Object {};而 console.log(window); 打印输出Window呢?这里的原因是,没有理解引用的含义。o 和 window 都是stack上的一个变量,他们都指向heap上的全局window对象,我们修改 o 这个引用,让它指向另外的一个空对象,而这并不会同时修改stack上的window这个引用的指向。也就是就像两条绳子 a, b 都指向一条船,我让其中的一条绳子b指向第二条船,并不会影响绳子a还指向第一条船。

    o = o.druid; 执行之后,o 不再执行window对象了,而是指向了window.druid对象,那么最后的console.log(o.druid);为什么打印输出 undefined 呢?很简单,因为 o 已经指向了 window.druid; 而window.druid是个空对象,其下并没有个druid的属性,所以自然就打印输出 undefined 了。

    也就是说最后的console.log(o.druid); 就相当于 console.log(window.druid.druid);

    好,理解了上面的代码,我们在加上一段代码:

    (function(){  
        var o = window;
        console.log(o);    // 打印Window    
        o.druid={};
        console.log(o);    // 打印Window
        console.log(o.druid);    // 打印 Object {}
    
        o = o.druid;
        console.log(o);    // 打印 Object {}
        console.log(window);    // 打印Window
        console.log(o.druid);    // 打印  undefined
    
        o.index = {};
        console.log(o.index);    // 打印 Object {}
        o = o.index;
        console.log(o.index);    //  undefined
    })(); 

    对应的firebug中显示的对象为:

    我们看到了已经形成了我们需要的命名空间:window.druid.index ,其实命名空间是使用对象链条来实现的。

    因为 o = o.druid; 之后,o 已经指向了 window.druid ,那么 o.index = {}; 就相当于 window.druid.index = {};

    而 后面的 o = o.index; 又将 o 对象变成了一个空对象,不再指向 window.druid,打印一个空对象的 index 属性自然就输出 undefined.

    到这里已经就可以完全理解namespace函数的定义了。

    其实核心知识点的有三条:

    1)利用了 window 这个特殊引用的不可覆盖性,不可修改;

    2)命名空间其实是对象链条来模拟的;

    3)理解引用的含义:引用是个在stack上的变量,可以修改它指向不同的对象,要访问或者说修改他指向的对象,必须使用 “.” 点操作符,比如 o.index ={}; 而单纯的修改 o ,比如 o = {}; 并不会修改他指向的对象,因为 没有访问到他指向的对象,怎么能修改到他指向的对象呢?

  • 相关阅读:
    (转载)SAPI 包含sphelper.h编译错误解决方案
    C++11标准的智能指针、野指针、内存泄露的理解(日后还会补充,先浅谈自己的理解)
    504. Base 7(LeetCode)
    242. Valid Anagram(LeetCode)
    169. Majority Element(LeetCode)
    100. Same Tree(LeetCode)
    171. Excel Sheet Column Number(LeetCode)
    168. Excel Sheet Column Title(LeetCode)
    122.Best Time to Buy and Sell Stock II(LeetCode)
    404. Sum of Left Leaves(LeetCode)
  • 原文地址:https://www.cnblogs.com/hubing/p/4527721.html
Copyright © 2011-2022 走看看