zoukankan      html  css  js  c++  java
  • 深入理解javascript的作用域--函数声明为什么会前置

     标签: javascript函数对象
    这篇博文解决了以下迷惑
    • 函数声明为什么前置
    • 函数声明前置和变量前置优先级问题
    • 为什么js文件开头就可以使用Math,String等库,而不需要导入头文件

    1.变量对象VO

    变量对象(Variable Object, 缩写为VO)是一个抽象 
    概念中的“对象”,它用于存储执行上下文中的: 
    1. 变量 
    2. 函数声明 
    3. 函数参数

    js解释器就是通过变量对象(VO)来找到我们定义的变量和函数的。

    举个例子:

    var a = 10;
    function test(x) {
    var b = 20;
    }
    test(30);

    针对例子的浏览器js引擎的VO记录:
    //全局作用域
    VO(globalContext) = {
    a : 10,//变量
    test : <ref to function>//函数声明
    };
    //test函数的函数作用域
    VO(test functionContext) = {
    x : 30,//函数参数
    b: 20//函数声明
    };

    2.js开头不需要加入头文件谜解

    在js代码执行前,js引擎为我们初始化了如下全局作用域的VO。

    在全局上下文中

    //在全局上下文中 ,vo===this===global

    VO(globalContext) === [[global]];
    [[global]] = {
    Math : <...>,
    String : <...>,
    isNaN : function() {[Native Code]}
    ...
    ...
    window : global // applied by browser(host)
    };

    那么String(10)就相当于[[global]].String(10);了。这就是js代码执行前就可以调用Math等库的原因。

    String(10); //[[global]].String(10);
    window.a = 10; // [[global]].window.a = 10
    this.b = 20; // [[global]].b = 20;
    GlobalContextVO (VO === this === global)

    其中有趣的是,我们发现global的window指向global自身,所以我们可以在浏览器中尝试window.window.window…一直循环调用下去,可以证明window是一个无限循环调用。

    3.函数中的VO–AO

    在函数上下文中,我们在进入函数上下文的时候创建vo,这时候称呼他为ao(activation object)。 
    AO可以看作是VO的激活对象。

    VO(functionContext) === AO;
    AO = {
    arguments : <Arg0>
    };
    arguments = {
    callee,
    length,
    properties-indexes
    };

    函数的AO经历两个阶段

    1. 变量的初始化阶段
    2. 代码执行阶段

    3.1变量初始化阶段

    VO按照如下顺序填充: 
    函数参数(若未传⼊入,初始化该参数值为undefined) 
    函数声明(若发⽣生命名冲突,会覆盖) 
    变量声明(初始化变量值为undefined,若发⽣生命名冲突,会忽略。)

    举个例子:

    function test(a, b) {
    var c = 10;
    function d() {}
    var e = function _e() {};
    (function x() {});
    b = 20;
    }
    test(10);

    VO扫描创建过程: 
    添加所有传入参数:a:undefined,b:undefined 
    添加所有函数声明,名字重复就覆盖:d:func 
    添加所有变量声明,名字重复就忽略:c:undefined,e:undefined

    因此它的VO:

    AO(test) = {
    a: 10,
    b: undefined,
    c: undefined,
    d: <ref to func "d">
    e: undefined
    };

    再来个例子:

    function foo(x,y,z){
        function x(){};
        alert(x);
    }
    foo(100);

    结果alert:function x(){}

    分析:扫描传入参数:x:undefined,y:undefined,z:undefined 
    扫描函数声明:x:func覆盖undefined 
    扫描变量声明:无

    最终x是func。

    function foo(x,y,z){
        function func(){};
        var func;
        consoel.log(func);
    }
    foo(100);

    结果console.log:func(){}

    分析:扫描传入参数:x:undefined,y:undefined,z:undefined 
    扫描函数声明:x:func覆盖undefined 
    扫描变量声明:x名字冲突不更改,直接忽略。

    最终x还是func。

    3.2代码执行阶段

    还是这个例子

    function test(a, b) {
    var c = 10;
    function d() {}
    var e = function _e() {};
    (function x() {});
    b = 20;
    }
    test(10);

    之前分析之后的VO:

    AO(test) = {
    a: 10,
    b: undefined,
    c: undefined,
    d: <ref to func "d">
    e: undefined
    };

    十分好理解的为每个VO变量添加上数值

    VO['c'] = 10;
    VO['e'] = function _e() {};
    VO['b'] = 20;

    结果是:

    AO(test) = {
    a: 10,
    b: 20,
    c: 10,
    d: <reference to FunctionDeclaration "d">
    e: function _e() {};
    };

    3.3练习

    题目:

    alert(x); // function
    var x = 10;
    alert(x); // 10
    x = 20;
    function x() {}
    alert(x); // 20
    if (true) {
    var a = 1;
    } else {
    var b = true;
    }
    alert(a); // 1
    alert(b); // undefined

    解析: 
    变量初始化阶段: 
    寻找函数声明:x:func 
    寻找变量声明:x不覆盖(还是func) a:undefined,b:undefined

    所以第一行alert(x)返回function

    代码执行阶段: 
    x:10 
    alert结果就是10 
    x:20 
    alert结果就是20

    if语句没有产生新作用域,true永远为真,赋值a为1

    最后两句打印a为1,b永远得不到执行,打印为undefined

  • 相关阅读:
    选择Nodejs的N个理由
    linux下查看内存的命令
    Nginx反向代理和负载均衡部署指南
    linux下创建,删除,移动文件命令
    linux文件创建、查看、编辑命令
    Linux下rar命令详解
    linux下tar命令详解
    linux下使用tar命令
    Eclispe怎么给工作空间下的项目分组
    MAT(Memory Analyzer Tool)工具入门介绍
  • 原文地址:https://www.cnblogs.com/zhq--blog/p/6980273.html
Copyright © 2011-2022 走看看