Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

作用域 #12

Open
cy0707 opened this issue Oct 8, 2016 · 0 comments
Open

作用域 #12

cy0707 opened this issue Oct 8, 2016 · 0 comments

Comments

@cy0707
Copy link
Owner

cy0707 commented Oct 8, 2016

http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html
http://www.laruence.com/2009/05/28/863.html
https://www.zhihu.com/question/36393048
读了上面三篇文章,对一直很困惑的作用域有了进一步的了解。真的很感谢博主们用心的写的博文和知友的回答。现在把里面能理解到相关知识都整合一起,方便以后理解。

有几个结论很重要

  1. JavaScript里一切都是对象
  2. 需要分析函数在代码中被调用的位置,而不是函数声明的位置。只有仔细分析这个调用位置才能知道this代表什么。

javascript的预编译( JS的预编译是以段为处理单元的)

    <script>
//并不会弹出function的。因为这是两个script标签。
         alert(typeof eve); //结果:undefined
    </script>
    <script>
         function eve() {
              alert('I am Laruence');
         }
    </script>

JS在执行每一段JS代码之前, 都会首先处理var关键字和function定义式(函数定义式和函数表达式).
在调用函数执行之前, 会首先创建一个变量对象, 然后搜寻这个函数中的局部变量定义,和函数定义, 将变量名和函数名都做为这个变量对象的同名属性, 对于局部变量定义,变量的值会在真正执行的时候才计算, 此时只是简单的赋为undefined.

对于函数的定义:函数声明和函数表达式两种的区别

     alert(typeof eve); //结果:function
     alert(typeof walle); //结果:undefined
     function eve() { //函数定义式
          alert('I am Laruence');
     };
     var walle = function() { //函数表达式
     }
     alert(typeof walle); //结果:function

这就是函数定义式和函数表达式的不同, 对于函数定义式, 会将函数定义提前. 而函数表达式, 会在执行过程中才计算.(这就是说函数的声明的定义函数的方式,有一个重要的特征就是函数声明提升,其意思是在执行代码之前,就会读取函数的声明)

JavaScript的作用域链

注意到, 因为函数对象的[[scope]]属性是在定义一个函数的时候决定的, 而非调用的时候。

 function func(lps, rps){
        var name = 'laruence';
}

此时我们只有一个执行环境那就是全局执行环境。里面有一个func对象,其值是未定义的。func()函数未调用之前会创建一个执行环境-------而每个执行环境都有与之关联的变量对象(变量对象就是执行环境中包含了所有变量和函数的对象。另外变量对象是后台的,保存在内存中的,代码无法直接访问的)----------这个变量对象的有一个特殊的内部属性[scope]--------这个属性就是的就是作用域链的值。(这些关系都是静态的话,还没有开始执行,这只是这个函数拥有的关系)。

 function func(lps, rps){
        var name = 'laruence';
}
func();

我们调用func()函数----------会创建一个执行环境-------而每个执行环境都有与之关联的活动对象(变量对象就是执行环境中包含了所有变量和函数的对象。另外活动对象是后台的,保存在内存中的,代码无法直接访问的)----------这个活动对象的有一个特殊的内部属性[scope]--------这个属性就是
的就是作用域链的值。此时全部执行环境的活动变量处于第二位,当前的执行环境的活动对象处于第一位。

在发生标识符解析的时候, 就会逆向查询当前scope chain列表的每一个活动对象的属性,如果找到同名的就返回。找不到,那就是这个标识符没有被定义。

作用域链例子

function returnfunc (propertyName) { 
    return function (obj) { 
        return obj[propertyName]; 
      };
 } 
var savefunc = returnfunc("name"); 
var result = savefunc({name:"Picasso"});
alert(result); 

以上代码的最开始的作用域链和执行环境:
zyy-05
我们先开始调用returnfunc()函数,马上会创建一个包含returnfunc()变量对象的行环境,作用域链开始变化,如下图:
zyy-06
随后returnfunc()函数会返回它内部的匿名函数,当匿名函数被返回后,整个作用域链和执行环境又发生了变化:
zyy-07

我们看到匿名函数(闭包)被添加到了最作用域链的最前端,returnfunc()的执行环境被销毁,但我们注意到returnfunc()函数的活动对象仍然在被引用(匿名函数仍在访问propertyName参数),因此returnfunc()函数的变量对象仍然在内存中,成为活动对象。这就是为什么匿名函数就能访问returnfunc()函数定义的所有变量和全局环境定义的变量,毕竟returnfunc()的活动对象仍然保持“激活”状态。

知道匿名函数被销毁,returnfunc()函数活动对象才被销毁

js预编译的例子(这个例子很重要,要重点理解)

    var name = 'laruence';
    function echo() {
         alert(name);
         var name = 'eve';
         alert(name);
         alert(age);
    }
    echo();
/*
其结果为:
    undefined
    eve
    [脚本出错]
*/

因为js预编译----- 在调用函数执行之前, 会首先创建一个变量对象, 然后搜寻这个函数中的局部变量定义,和函数定义, 将变量名和函数名都做为这个变量对象的同名属性, 对于局部变量定义,变量的值会在真正执行的时候才计算, 此时只是简单的赋为undefined. 这句话很重要,要再提醒一遍。

开始执行echo()函数的时候,js预编译的时候,name设置了undefine,执行的echo()函数的时候,函数内部定义的变量name赋值了,即找到了,所以弹出eve,而age根本就没定义,所以会报错。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant