Skip to content

作用域 #12

Open
Open
@cy0707

Description

@cy0707

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根本就没定义,所以会报错。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions