zoukankan      html  css  js  c++  java
  • Javascript执行流总结

    面对各种各样的JavaScript代码,我们有时候难免会犯错。可当自己仔细研究一下,哦原来是这么回事。有时候怎么会想为什么Javascript程序会是这样执行的呢?为什么没有得到自己预期的答案呢?自己到底是哪一步想错了呢?这时候就想如果我是JS执行流我会怎么执行?仔细想想这些问题,原来这其中还包含着很多自己不熟悉的知识点,今天就来总结下,同时也好加深印象。
    首先我们来熟悉几个名称:

    • 执行上下文栈(Execution Context Stack)
      每一种代码的执行都需要依赖自身的上下文。函数的每一次调用,都会进入函数执行中的上下文,并且来计算函数中变量等的值。一个执行上下文可以激活另一个上下文,就好比一个函数调用了另一个函数(或者全局的上下文调用了一个全局函数),然后一层一层调用下去。逻辑上来说,这种实现方式是栈,我们可以称之为上下文堆栈。
      激活其它上下文的某个上下文被称为 调用者(caller) 。被激活的上下文被称为被调用者(callee) 。当一个caller激活了一个callee,那么这个caller就会暂停它自身的执行,然后将控制权交给这个callee. 于是这个callee被放入堆栈,称为进行中的上下文[running/active execution context]. 当这个callee的上下文结束之后,会把控制权再次交给它的caller,然后caller会在刚才暂停的地方继续执行。在这个caller结束之后,会继续触发其他的上下文。一个callee可以用返回(return)或者抛出异常(exception)来结束自身的上下文。
      当一段程序开始时,会先进入全局执行上下文环境[global execution context], 这个也是堆栈中最底部的元素。此全局程序会开始初始化,初始化生成必要的对象[objects]和函数[functions]. 在此全局上下文执行的过程中,它可能会激活一些方法(当然是已经初始化过的),然后进入他们的上下文环境,然后将新的元素压入堆栈。在这些初始化都结束之后,这个系统会等待一些事件(例如用户的鼠标点击等),会触发一些方法,然后进入一个新的上下文环境。ECMAScript运行时系统就是这样管理代码的执行。
    • 执行上下文(Execution Context)
      一个执行的上下文可以抽象的理解为object。每一个执行的上下文都有一系列的属性(变量对象(variable object),this指针(this value),作用域链(scope chain) )
    • 变量对象(variable object)
      变量对象是与执行上下文相关的 数据作用域(scope of data) 。它是与上下文关联的特殊对象,用于存储被定义在上下文中的变量(variables)和函数声明(function declarations) 。
    • 活动对象(activation object)
      在一个函数上下文中,变量对象被表示为活动对象。它包含变量(variables)和函数声明(function declarations) 普通参数(形参) 与特殊参数(arguments)对象(具有索引属性的参数映射表)。

    知道了这几个名称后我们拿几个实例来分一下Javascript到底是如何执行的?

    if (!("a" in window)) {
        var a = 1;
    }
    alert(a);
    

    这里如果不加思考,想当然的认为如果window不包含属性a,就声明一个变量a,然后赋值为1。所以答案为1。
    可是正确答案是undefined,再仔细想想为什么?如果你就是JS程序你会如何运行?
    详细分析:
    当一段程序开始时,会先进入全局执行上下文环境并初始化生成必要的对象,在全局执行上下文环境中申明了变量a,根据变量声明提升,此时该上下文环境中的变量对象VO = {a:undefined}(全局上下文中VO还包括其他对象如Math,Date等对此题不构成影响,暂不考虑)。全局上下文环境初始化好后开始执行代码,首先判断变量a是否属于window对象,我们知道变量a已经在window对象中,所以,if条件语句不会被执行,直接执行alert,
    该题可简化为:

    var a;
    if (!("a" in window)) {
        a = 1;
    }
    alert(a);  //undefined
    

    第二题

    function a(x) {
        return x * 2;
    }
    var a;
    alert(a);
    

    我们知道变量对象中及包括函数声明也包括变量申明,在此题中有同名的函数和变量,此时函数声明会覆盖变量声明。但不会覆盖已经赋值的同名变量声明。所以此题中最后结果是函数
    第三题

    function b(x, y, a) {
        arguments[2] = 10;
        alert(a);
    }
    b(1, 2, 3);
    

    在此题中,b函数被激活会开辟一个新的内存栈并添加到执行上下文栈中,执行流进入b函数上下文,此时变量对象表示为活动对象
    AO = {
    x : 1,
    y : 2,
    a : 3,
    arguments:{0:1,1:2,2:3}
    }
    Arguments对象是活动对象的一个属性,它包括如下属性:

    1. callee — 指向当前函数的引用
    2. length — 真正传递的参数个数
    3. properties-indexes (字符串类型的整数) 属性的值就是函数的参数值(按参数列表从左到右排列)。 properties-indexes内部元素的个数等于arguments.length. properties-indexes的值和实际传递进来的参数之间是共享的。这个共享其实不是真正的共享一个内存地址,而是2个不同的内存地址,使用JavaScript引擎来保证2个值是随时一样的.

    所以当代码执行到arguments[2] = 10;时a也会变为10。

    参考文献:

  • 相关阅读:
    vs2015 打开xaml:右击-打开方式->xml编辑器
    交互式计算机图形学(基于webGL)资源使用
    利用pushState开发无刷页面切换
    js 阻止冒泡 stopPropagation
    PHP中“简单工厂模式”实例讲解
    ajax传递特殊字符串问题 +%@
    GitHub自学
    ajax 请求超时 取消请求
    mongo操作之分页
    我使用过的Linux命令之date
  • 原文地址:https://www.cnblogs.com/jesse131/p/6231017.html
Copyright © 2011-2022 走看看