• javascript变量的作用域


    javascript变量的作用域

    不太会写冠冕堂皇的开场白,直接进入主题。

    我们看一道题,出处为javaeye的某贴——这世界就是这样,有些人喜欢制造问题,有人喜欢解决问题。制造问题的人为解决问题的人带来就业机会……

    var a=100;  
    var b=true;  
    function test(){  
        alert(a);  
        alert(b);  
        b=false;  
        alert(b);  
        var a=200;  
        alert(a/2);  
        alert(++Math.PI);  
        alert(Math.PI++);  
    }  
    test();
    

    为什么第一个alert为undefined,而第二个为true。这问题也可以延伸为——alert(b)时怎么就会找外部的b,而alert(a)时就不会往外面找?!

    我们都明白局部变量的优先级大于全局变量,或者说内围作用域的变量的优先级比外围的高。当JS引擎在当前作用域找不到此变量时,它就往外围的作用域找。不过,在这之前,有一个严肃的问题是,究竟当前作用域存不存在这个变量。像javascript这样的解释型语言,基本分为两个阶段,编译期(下面为符合大多数语言的称呼习惯,改叫预编译)与运行期。在预编译阶段,它是用函数来划分作用域,然后逐层为其以 var 声明的变量(下略称为var变量)函数定义开辟内存空间,再然后对var变量进行特殊处理,统统赋初始值为undefined,如下图:

    由上图,我们便可以推知,当前网页拥有两个a,一个b,一个test函数。如果在运行期用到除此以外的东东,如c函数或d变量啦,就会报未定义错误(用eval等非正常手段生成变量与函数的情况除外),此外,它们最多出现未赋值警告。

    javascript的运行期是在为var变量与函数定义分配空间后立即执行,并且是逐行往下执行的。

    • 第1行它为外围作用域的a赋值为100
    • 第2行它为外围作用域的b赋值为true
    • 第3行进行test的作用域,我们简称为内围作用域。
    • 第4行就立即调用内围作用域的a,这时它还没有来得及赋值呢!不过它已经声明过了,因此默认为其赋值为undefined(在预编译阶段,见图),于是alert为undefined
    • 第5行就调用b时,JS引擎就拿起我画的图看了(笑),发现test的作用域内没有b,眼睛往外望,发现b了,而b在第二行就赋值为true,于是alert为true。
    • 第6行为一个赋值操作,把外围的b变量改赋为false。于是到第7行时,alert为false。以下说法不说了。

    作为对比,我们改写一下例子:

    var a=100;  
    var b=true;  
    function test(){  
        alert(a);  
        alert(b);  
        var b=false;  
        alert(b);  
        var a=200;  
        alert(a/2);  
        alert(++Math.PI);  
        alert(Math.PI++);  
    }  
    test();
    

    这时在test函数的作用域内,b也被声明了。

    掌握预编译为var变量与函数定义分配空间这一事实后,许多问题就迎刃而解。我们看犀牛书上的一个例子。

    var scope = "global";  
    function f() {  
        alert(scope);              
        var scope = "local";       
        alert(scope);              
    }  
    f(); 
    

    答案呼之欲出!

    我们来看更复杂的例子。

    Object.prototype.test = 'wrong';
    var test = 'right';
    (function f() {
        alert(test);
    })();
    

    这个问题的难点在于,运行期时,又生成一同名变量,它是附着于Object.prototype,究竟哪一个距离F()的作用域近一些呢?!测试结果是var test。于是我们有了下图:

    于是我们明白了,原来定义在函数外面的var变量并不位于window作用域的下一层。

    我们继续加深难度。

    (function f() {
        alert(test);
    })();
    Object.prototype.test = 'ccc';
    Object.test = "bbb"
    window.test = "aaa";
    

    报未定义错误,因为预编译期时没有符合要求的var变量,而在运行期时,和它同名的变量在调用时(第2行)也尚未建立起来!

    如果这样呢?!

    Object.test = "bbb";
    Object.prototype.test = 'ccc';
    window.test = "aaa";
    (function f() {
        alert(test);
    })();
    

    估计有很多人猜是bbb,其实是ccc!有人就不解了,不是对象的属性的优先级比其原型属性的优先级高吗???

    无错,的确如此,不过对象的属性是这样定义的:

    var o = {test:"eee"}
    

    Object.test = “XXX”这样定义,为类属性,或称静态属性。类属性优先级都是比实例属性低的

    通过这图也教育我们,一定要用局部变量啊,要不,一层层往上爬,效率是多么低啊。另外,这图也告诉我们,window是一个多么高级的存在啊(微软最爱听),Object都比它低一等,更别提什么继承问题啦!(在FF与IE中)

    //颠覆常识的存在
    alert(window instanceof Object);
    

    搞定这个我们看最难的一题。类属性,实例属性,原型属性,极晚绑定的属性都考到了!不过实例属性与类属性已超出本文的讨论范围,恕不讨论了。这些也不太难,很容易google到的,自己google吧。

    function foo(){
      foo.abc = function(){alert('def')}
      this.abc = function(){alert('xyz')}
      abc = function(){alert('@@@@@')};
      var abc = function(){alert('$$$$$$')}
    }
    foo.prototype.abc = function(){alert('456');}
    foo.abc = function(){alert('123');}
    var f = new foo();
    f.abc();
    foo.abc();
    abc();
    

    蓝色理想的人说这题出得不好,有错。我说,有错才好,这样才能考出水平!十秒内做出正确答案,说明学会了。

    答案:

    最后归纳一下,JS引擎有两个设置变量的机会。第一次在预编译时期,所有var变量会分配到各自的作用域中,值一律为undefined。第二次在运行期,由于是逐行执行,因此是可变的。我们可以通过eval与Function动态生成新的变量,它们的作用域都是可制定的,其他赋值语句,只是把变量固定于顶层作用域(window)中,或是仅仅是重新赋值。我们也可以用delete来删除对象的属性,迫使其往外走同名变量。with闭包会在其引用的对象的属性被删除后,在闭包的外围寻找与此属性同名的变量。有关with闭包请看我另一篇博文。

    如果您觉得这文章对您有帮助,可以打赏点钱给我,鼓励我继续写博,我的支付宝

     
     

    当前标签: javascript

    共30页: 1 2 3 4 5 6 7 8 9 下一页 末页 
    最流行的JavaScript代码规范 司徒正美 2014-02-19 07:57 阅读:911 评论:0  
     
    javascript操作referer 司徒正美 2014-02-09 14:27 阅读:576 评论:0  
     
    r.js合并实践 司徒正美 2014-01-23 13:45 阅读:1099 评论:3  
     
    IE6的checkbox, radio是通过defaultChecked决定是否选中 司徒正美 2014-01-17 15:23 阅读:597 评论:0  
     
    2013年最后的收成:avalon1.0正式发布 司徒正美 2014-01-13 10:25 阅读:4759 评论:45  
     
    还要多少年, 前端开发才能像后端那样轻松 司徒正美 2014-01-07 14:36 阅读:1563 评论:7  
     
    JavaScript Promise:去而复返 司徒正美 2013-12-28 13:48 阅读:2069 评论:2  
     
    JavaScript 18岁生日快乐 司徒正美 2013-12-27 23:14 阅读:797 评论:0  
     
    web界面上的字体兼容方案 司徒正美 2013-12-18 17:47 阅读:1003 评论:3  
     
    迷你MVVM框架 avalonjs 0.99发布 司徒正美 2013-12-18 14:46 阅读:1022 评论:3  
     
    将一个符合URL格式的字符串变成链接 司徒正美 2013-12-18 11:50 阅读:712 评论:0  
     
    通过window.crypto.getRandomValues获得一个大于零的随机数 司徒正美 2013-12-07 09:46 阅读:823 评论:0  
     
    jquery slideDown效果 司徒正美 2013-11-22 15:45 阅读:1093 评论:1  
     
    使用avalon实现SKU组合查询功能 司徒正美 2013-11-18 10:47 阅读:1364 评论:1  
     
    使用Object.observe 实现数据绑定 司徒正美 2013-11-16 21:41 阅读:617 评论:1  
     
    IE bug之location.href没有referer 司徒正美 2013-11-13 12:29 阅读:703 评论:0  
     
    Shadow DOM的事件绑定 司徒正美 2013-11-08 09:07 阅读:655 评论:0  
     
    IE回车的一个怪异行为 司徒正美 2013-11-01 10:40 阅读:921 评论:3  
     
    为什么框架很容易就走上大而全的不归之路? 司徒正美 2013-10-11 22:52 阅读:3783 评论:9  
     
    移动Web单页应用开发实践——页面结构化 司徒正美 2013-10-01 09:25 阅读:1913 评论:1  
     
     
     
    标签: javascript
  • 相关阅读:
    201202
    Android牟利之道(一)界面嵌入有米广告
    SoketException log
    ERROR: 9patch image about.9.png malformed.
    Conversion to Dalvik format failed with error 1
    absolute绝对定位(相对于整个html流)以及不为人知的(fixed)绝对定位(fixed相对于浏览器窗口=不动的div)
    about getElementsByTagName()的那点事
    js 不用onload的loding
    absolute fixed效果 复制网页打开就是代码 http://www.cnblogs.com/0banana0/archive/2011/05/25/2056643.html
    关于datetime的那点事
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3572682.html
Copyright © 2020-2023  润新知