一寸光阴一寸金,寸金难买寸光阴!

    

JavaScript语言编程核心(三)--- 执行上下文与作用域

JavaScript语言编程核心(三)--- 执行上下文与作用域

执行上下文(EC)

执行上下文:可以抽象为一个简单的对象,在一个函数或者eval( )被执行时创建,对象里面包含着一些代码运行所需的属性,统称为 上下文状态。一个上下文状态由三个结构组成:

  • 变量对象(variable Object);
  • 作用域链(scope chain);
  • this指向(thisValue);

执行上下文堆栈

一般可以认为有三种执行上下文:

  • 全局执行上下文;
  • 函数执行上下文;
  • eval执行上下文;

一个执行上下文可以触发另一个上下文,以栈的形式实现,称为执行上下文堆栈

触发其它上下文的叫做 caller,而被触发的上下文叫做 callee

1.当代码执行的时候,首先生成一个全局执行上下文(global EC)。

2.当执行到函数作用域时,全局执行上下文(caller)会触发该函数执行上下文(callee),将控制权传递给callee,而此时caller则暂缓执行。callee被push到栈的最顶层,拥有控制权,成为当前运行的执行上下文(active EC)。

3.当callee执行结束后,控制权会返还给callercallee从栈顶层被pop除,caller继续往下执行。

变量对象(VO)

变量对象是执行上下文中的一个数据作用域,存储着上下文中所定义的变量和函数声明(不包括函数表达式)。

全局上下文的变量对象就全局对象本身。

活动对象(AO)

当函数被caller触发时,该函数的变量对象(VO)则会被激活成为活动对象(AO),里面加入了形参和arguments对象。

作用域

作用域:通俗点可以理解为是一段代码或者一个变量的作用范围,是程序源代码中定义这个变量的区域,可以简单的理解为一个对象,其实,变量对象便是作用域的实体。比如,一个全局变量,就拥有全局的作用域,在JavaScript代码的所有地方都有定义,都可以进行访问,理解为一个全局对象中定义着这个全局属性。

在许多编程语言中,每个花括号中的代码都具有各自的作用域,而在花括号的外部,是不可见的,这称为 块级作用域(block scope)。但在JavaScript中,虽然语法上也是使用花括号,但并不存在块级作用域,在花括号内定义的变量,在外部都是可见的。取而代之的是函数作用域(function scope):在一个函数中声明的变量,在该函数,包括其内部嵌套函数中,都是有定义,可见的。但在函数的外部是不可见的。

因此,这里就引出了另一个概念-- 声明提前
根据函数作用域的定义,一个声明在函数体内的任何地方都是始终可见的,甚至在声明之前。因此,就算在声明一个变量之前,也是可以访问到这个变量的(但不涉及到赋值)。

    var scope = "global";
    (function(){
        // 并不是输出global,这是因为声明提前,等于在该语句前加了一句,var scope。但并不涉及赋值;
        console.log(scope);      // undefined;
        var scope = "local";
        console.log(scope);         // local;
    })();

作用域链

在定义一个函数作用域时,会随之产生一个与之关联的作用域链(scope chain)。这个作用域链是一个对象列表,列表内包含所有父级变量对象和自身的活动对象,另外,通过with语句catch语句也可动态的延长作用域链。

作用域链其实是由两部分组成的:函数内部属性 [[scope]] + 自身活动对象(AO);
[[scope]]属性中,保存着父级函数的作用域链。

当在函数内访问一个变量时,首先会从该函数自身的活动对象中查找,如果不存在,则会沿着作用域链往上一级一级查找父级变量对象。

闭包

由于在JavaScript中,函数属于第一级对象,即可以当成参数传入一个函数,也可以从函数中返回出一个函数。此时,便会出现一个问题,当一个父函数执行后返回另一个子函数后,便会被摧毁,而此时,返回的子元素的[[scope]]属性中仍然保存着父函数的作用域链,以此来解决访问父函数中变量的问题。

这种类型的作用域,成为静态(词法)作用域。

    var afun = (function(){
        var a = 1;
        return function afun(){
            alert(a);
        }
    })();
    var a = 2;
    afun();      // 1

静态作用域是闭包存在的一个必需条件,因此

闭包可以定义为:

一个函数,和以静态方式/词法方式进行存储的所有父作用域的一个集合体。所以,通过这些存储的作用域,函数可以很容易的找到自由变量。

Tips:从理论上讲,每一个函数都可以称为闭包,因为都包含了[[scopte]]属性。

当在一个父函数中定义了两个子函数时,此时两个子函数拥有相同的[[scope]]属性,而且是共享的。也就是说,改变一个闭包中的变量,会影响到另一个闭包中的变量。这也就是我们最常见的闭包问题的根源所在:

    var a = [];
    for(var i = 0;i<10;i++){
        a[i] = function(){
            console.log(i);
        }
    }
    // 而不是预期中的 0~9,其原因就是这9个函数共享一样的[[scope]]属性;
    a[0]();   // 10;
    a[1]();   // 10;
    a[9]();   // 10;

可以通过将参数i以参数的形式传入,让其不需要从[[scope]]属性中进行查找。

    var a = [];
    for(var i = 0;i<10;i++){
        a[i] = (function(x){
            return function(){
                console.log(x);
            }
        })(i);
    }
    a[0]();   // 0;
    a[1]();   // 1;
    a[9]();   // 9;

所有原创文章采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
您可以自由的转载和修改,但请务必注明文章来源并且不可用于商业目的。
本站部分内容收集于互联网,如果有侵权内容、不妥之处,请联系我们删除。敬请谅解!

支持一下呗!Y(・∀・)Y
  • QQ
  • AliPay
  • WeChat

仅有一条评论

  1. 乐乐 乐乐
    辛苦了! mg:

添加新评论

选择表情

  Timeline:今日文案

你看世界的眼光最要紧

updated on :

  关于博主

blog名-小优,平时喜欢跑步,喜欢听一些温柔,轻松的音乐,喜欢接触新事物,对自己的能力有一个很好的认知,人生在于折腾,一寸光阴一寸金,寸金难买寸光阴!我就是我,颜色不一样的烟火!

  近期评论

  •  wang: 站点名称:尼古拉斯博地址:https://jstop1.com描述:今天又是阳光明媚的一天!
  •  金石热点网: 文章写的不错,加油~
  •  lin: 试试
  •  wa:
  •  duanyuchen: 评论了还是不显示,,,
  •  duanyuchen: ~
  •  new:
  •  bigbigli: 来捧一下场
  •  望月阁·阁主~小潮: 尊敬的博主:您好!希望在您的站点申请友情链接。信息如下博客标题:望月阁·博客天地博客地址:ht...
  •  丧狼: 已添加贵链名称:Mourning Wolf地址:http://slxzy.52chye.cn/...

多情之人 往往最容易被伤 —— by 小优

人生应该树立目标,否则你的精力会白白浪费。

山涧的泉水经过一路曲折,才唱出一支美妙的歌。

如若不是为了一个人,谁肯枯守一座城。城市和爱情,总是有着这样那样的关系。我们会因为一个人,去到那座城,因为那是一座爱的城;我们也会因为一个人,离开一座城,那是一座绝望的伤城。

常求有利别人,不求有利自己。