• Javascript浅谈之变量及变量对象


    一、变量介绍

          JavaScript编程的时候总避免不了声明变量和函数,这是构成JS代码的必不可少的基本元素,但是解释器是如何声明并且在什么地方查找这些函数和变量?引用这些对象的时候究竟发生了什么?

      1.变量的声明

      JavaScript中任何时候,变量只能通过使用var关键字才能声明。

    //下面都是正确的变量声明
    var iNum = 12;
    var sName = "萨菲罗斯";
    var foo = function(){ console.log("简单的函数");};
    
    //下面这种忽略关键字“var”的赋值方式其实并不是声明变量,只是简单的给全局变量window添加了一个属性而已
    count = 12;

      这里要纠正一个错误,就是如果不使用var关键字,则声明的是全局变量,其实这种说发是错误的,它其实这仅仅是给全局对象添加了一个新的属性而已。

      分辨是变量还是属性有个非常建议的方法,变量相对于简单属性来说,变量有一个特性(attribute):{DontDelete},这个特性的含义就是不能用delete操作符直接删除变量属性。

    var sVar = "这是一个变量";
    sAttr = "这是一个属性";
    console.log(window.sVar); //>>这是一个变量
    console.log(window.sAttr); //>>这是一个属性
    delete window.sVar; // >>false
    delete window.sAttr; //>>true
    
    console.log(sVar); //>>这是一个变量
    console.log(sAttr); //>>ReferenceError: sAttr is not defined

      从上面例子中可以清楚看出,sAttr只是全局变量的一个属性而已。至于为什么sVar和sAttr都能通过作为window的属性进行访问,这点接下来变量对象中会详加解释。

      2. hoisting 机制(变量提升)

      变量声明有个很重要特点: hoisting 机制 —— 变量声明永远都会被提升至作用域的最顶端

    var name = "jink";
    (function(){
          console.log("name :", name);  //>>name : undefined
          var name = "fn inner jink";
          console.log("name :", name);  //>>name : fn inner jink
    })();

      是不是挺疑惑为什么前一个输出,name的值居然不是“jink”。这里涉及到两个非常重要的知识点:1.变量声明的hoisting机制;2.变量的寻找原理,这需要涉及到作用域链的知识。我们在这里暂时只分析前者,对于作用域链这个,我后面将会有文章专门介绍。

      其实hoisting机制非常简单,就是把变量的声明提到作用域的顶端。

    var name = "jink";
    (function(){
          var name; //由于作用域链缘故,最顶端变量值优先被使用
          console.log("name :", name);  //>>name : undefined
          name = "fn inner jink";
          console.log("name :", name);  //>>name : fn inner jink
    })();

    二、变量对象

      其实通过前面例子我们突然发现,变量和执行上下文好像有着非常密切的关系。其实在ECMAScript的内部实现中,它们两者的确存在非常紧密的关系:变量自己应该清楚它的数据存储在哪,并且知道如何访问它。这种机制被称为“变量对象(variable object)”。 当程序进入某个执行上下文的伊始,都会创建一个对象变量,用来管理执行上下文中所有变量。

      变量对象(缩写为VO)是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的以下内容:
        (1)变量 (var, 变量声明);
        (2)函数声明 (FunctionDeclaration, 缩写为FD);
        (3)函数的形参
      举例来说,我们可以用普通的ECMAScript对象来表示一个变量对象:

    VO = {};  //VO就是执行上下文的属性(property):
    activeExecutionContext = {
        VO: {
        // 上下文数据(var, FD, function arguments)
        }
    };

      只有全局上下文的变量对象允许通过VO的属性名称来间接访问(因为在全局上下文里,全局对象自身就是变量对象,稍后会详细介绍),在其它上下文中是不能直接访问VO对象的,因为它只是内部机制的一个实现
      当我们声明一个变量或一个函数的时候,和我们创建VO新属性的时候一样没有别的区别(即:有名称以及对应的值)。

      由于变量对象和执行上下文相关,且对象变量在不同执行上下文中表现的方式也不一样。

      1.全局上下文

      说道全局上下文,有个概念不得不提,那就是全局对象。 全局对象(Global object) 是在进入任何执行上下文之前就已经创建了的对象。这个对象只存在一份,它的属性在程序中任何地方都可以访问,全局对象的生命周期终止于程序退出那一刻。

      全局对象初始创建阶段将Math、String、Date、parseInt作为自身属性,等属性初始化,同样也可以有额外创建的其它对象作为属性(其可以指向到全局对象自身)。例如,在DOM中,全局对象的window属性就可以引用全局对象自身(当然,并不是所有的具体实现都是这样): 

    global = {
        Math: <...>,
        String: <...>
        ...
        ...
        window: global //引用自身
    };

       当访问全局对象的属性时通常会忽略掉前缀,这是因为全局对象是不能通过名称直接访问的。不过我们依然可以通过全局上下文的this来访问全局对象,同样也可以递归引用自身。例如,DOM中的window。综上所述,代码可以简写为:

    var sVar = "这是一个变量";
    sAttr = "这是一个属性";
    //window.sVar === global.window.sVar === global.sVar === sVar
    console.log(window.sVar); //>>这是一个变量
    //至于sAttr,仅仅是全局变量的一个属性而已
    console.log(window.sAttr); //>>这是一个属性

    由上得到一个结论:在全局上下文中变量对象就是全局对象自己

    2.函数上下文

      在函数执行上下文中,VO是不能直接访问的,此时由活动对象(activation object,缩写为AO)扮演VO的角色。
      VO(functionContext) === AO;
      活动对象是在进入函数上下文时刻被创建的,它通过函数的arguments属性初始化。arguments属性的值是Arguments对象,它是活动对象的一个属性。

    AO = {
        arguments: <ArgO>,
        other//函数内部声明的变量和函数    
    };

      由于执行上下文中的变量在内部会有一个变量统一管理,这样只要知道这个VO,我们就可以像对象一样引用这个变量,很遗憾,这个VO在函数上下文中是不可见的,所以函数内部声明的变量对外面而已是不可见的。我只需要知道,在函数上下文中,变量对象就是活动对象,活动对象中包括函数参数和函数内部声明的变量和函数即可

    三、总结

      还记得前面的hoisting机制吧,因为变量对象在进入上下文后立马创建,它会将整个上下文中申明的变量统统记录下来,无论变量在上下文中的何处,然后给默认初始值undefined。当代码执行时,发现一个变量,立马到变量对象中寻找,如果是赋值,就简单的赋值;如果是引用,则看看变量存在不,不存在报错,存在则返回变量值。这个过程对于我们而言是不可见的,但给我们印象好像所有变量声明都通同提升到上下文顶端了。

      这章中涉及到一个比较重要的概念:变量对象。其实我们在实际情况中根本用不到这个对象,仅仅只是借助它来理解JavaScript中的一些概念。结合它你就能很容易理解hoisting机制还有后面的作用域链相关知识。

      感谢大家阅读,有什么疑问记得留言,我会及时回复与大家讨论。

  • 相关阅读:
    forEach 不能跳出循环;用some 或者every 代替
    echarts图表不重新渲染
    vue 的el-tree获取选中节点的集合执行多次问题
    vue 2.6版本 手动配置json文件显示隐藏
    echart category series 数据多个 长度不对应 对应的数据一定要用字符串 不要用数字
    nginx前端配置后端
    UCOS多任务下有效的喂狗的方式
    判断数据类型
    PDFJS插件带添加header以及携带授权
    vue中控制浏览器前进和后退
  • 原文地址:https://www.cnblogs.com/jink/p/3512959.html
Copyright © 2020-2023  润新知