预编译与作用域

预编译

js作为一种翻译性语言,编译一行,执行一行。但编译前,js还对整篇进行了一次预编译。为了了解预编译,我们需要认识js运行的大致流程。

js运行过程

  • 语法分析,就是检查你的代码有没有什么低级的语法错误;
  • 预编译 ,简单理解就是在内存中开辟一些空间,存放一些变量与函数 .
  • 解释执行,顾名思义便是执行代码了;

一般来说,预编译在script代码内执行前发生, 但是它大部分会发生在函数执行前

让我们找些实例分析。

全局预编译

<script>
var a = 1;
console.log(a);
function test(a) {
  console.log(a);
  var a = 123;
  console.log(a);
  function a() {}
  console.log(a);
  var b = function() {}
  console.log(b);
  function d() {}
}
var c = function (){
console.log("I at C function");
}
console.log(c);
test(2);
</script>

页面产生便创建了GO全局对象(Global Object)(也就是window对象);

  1. 第一个脚本文件加载;
  2. 脚本加载完毕后,分析语法是否合法;
  3. 开始预编译 查找变量声明,作为GO属性,值赋予undefined; 查找函数声明,作为 GO属性,值赋予函数体;
//查找变量
    GO/window = {
        a: undefined,
        c: undefined,
        test: function(a) {
            console.log(a);
            var a = 123;
            console.log(a);
            function a() {}
            console.log(a);
            var b = function() {}
            console.log(b);
            function d() {}
        }
    }

接下来开始解释执行语句

//抽象描述

    GO/window = {
        a: 1,
        c: function (){
            console.log("I at C function");
        }
        test: function(a) {
            console.log(a);
            var a = 123;
            console.log(a);
            function a() {}
            console.log(a);
            var b = function() {}
            console.log(b);
            function d() {}
        }
    }

在执行函数语句之前,也存在预编译过程

  1. 创建AO活动对象(Active Object);
  2. 查找形参和变量声明,值赋予undefined;
  3. 实参值赋给形参;
  4. 查找函数声明,值赋予函数体;

预编译之前面1、2两小步如下:

    AO = {
        a:undefined,
        b:undefined,
    }

第三步

        AO = {
            a:2,
            b:undefined,
        }

第四步

    AO = {
        a:function a() {},
        b:undefined
        d:function d() {}
    }

执行函数时

    AO = {
        a:function a() {},
        b:undefined
        d:function d() {}
    }
    --->
    AO = {
        a:123,
        b:undefined
        d:function d() {}
    }
    --->
    AO = {
        a:123,
        b:function() {}
        d:function d() {}
    }

预编译阶段发生变量声明和函数声明,没有初始化行为(赋值),匿名函数不参与预编译 ; 只有在解释执行阶段才会进行变量初始化 ;
预编译小结
预编译两个小规则

以上用图示表示:
a定义
a被执行
b被创建

  • 函数声明整体提升-(具体点说,无论函数调用和声明的位置是前是后,系统总会把函数声明移到调用前面)
  • 变量 声明提升-(具体点说,无论变量调用和声明的位置是前是后,系统总会把声明移到调用前,注意仅仅只是声明,所以值是undefined)

预编译前奏

  • imply global 即任何变量,如果未经声明就赋值,则此变量就位全局变量所有。(全局域就是Window)
  • 一切声明的全局变量,全是window的属性; var a = 12;等同于Window.a = 12; 函数预编译发生在函数执行前一刻。

案例分析
题1:

//预编译:GO -> {fn: function () {}}
//解释执行:{fn()}
function fn (a) {
//执行函数fn前预编译:AO -> {a: function () {}, b: undefined[因为函数b()为函数表达式],d: function () {}}
      console.log(a); //控制台显示function a () {}
      var a = 123;
      console.log(a); //控制台显示123
      function a () {};
      console.log(a); //控制台显示123
      console.log(b); //控制台显示undefined
      var b = function () {};
      console.log(b); //控制台显示function () {}
      console.log(d); //控制台显示function d () {}
      function d () {};
  }
  fn(1); 

题2:

//预编译:GO -> {test: functiong () {}}
//解释执行:{test()}
 function test (a, b) {
//执行函数test前预编译:AO -> {a: 1[因为外部实参传入], b: function () {},d:function () {}}
    console.log(a); //控制台显示1
    a = 3;
    console.log(b); //控制台显示function b () {}
    b = 2;
//解释执行函数test:{b: 2}
    console.log(b); //控制台显示2
    function b () {};
    function d () {};
    console.log(b);  //控制台显示2,因为b已是变量并被赋值
//解释执行函数test:{d: function () {}}
    console.log(d);
}
test(1);

浅谈作用域

在 JavaScript 中, 作用域为可访问变量,对象,函数的集合。
JavaScript 函数作用域: 作用域在函数内修改。

JavaScript 局部作用域

变量在函数内声明,变量为局部作用域。
函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域!


function bar() {
  var testValue = 'inner';
}
console.log(testValue);		// 报错:ReferenceError: testValue is not define

但是我们可以借助一些工具访问内部变量:

  • 通过 return 访问函数内部变量:
function bar(value) {
  var testValue = 'inner';
  
  return testValue + value;
}

console.log(bar('fun'));		// "innerfun"
  • 通过 闭包 访问函数内部变量:
function bar(value) {
  var testValue = 'inner';
  
  var rusult = testValue + value;
  
  function innser() {
     return rusult;
  };
  
  return innser();
}

console.log(bar('fun'));		// "innerfun"
  • 立即执行函数:
    <script type="text/javascript">
    
        (function() {
    
          var testValue = 123;
    
          var testFunc = function () { console.log('just test'); };
    
        })();
    
        console.log(window.testValue);		// undefined
        
        console.log(window.testFunc);		// undefined
        
    </script>

全局作用域

作用域,是指变量的生命周期(一个变量在哪些范围内保持一定值)。

全局变量:

生命周期将存在于整个程序之内。

能被程序中任何函数或者方法访问。

在 JavaScript 内默认是可以被修改的。

显示声明:
带有关键字 var 的声明;

<script type="text/javascript">

    var testValue = 123;

    var testFunc = function () { console.log('just test') };

    /**---------全局变量会挂载到 window 对象上------------**/

    console.log(window.testFunc)		// ƒ () { console.log('just test') }

    console.log(window.testValue)		// 123
    
</script>

我们写的函数如果不经过封装,也会是全局变量,他的生命周期也就是全局作用域;

隐式声明:
不带有声明关键字的变量,JS 会默认帮你声明一个全局变量。

<script type="text/javascript">

    function foo(value) {

      result = value + 1;	 // 没有用 var 修饰

      return result;
    };

    foo(123);				// 124
    
    console.log(window.result);			// 124 <=  到 window全局对象上了
    
</script>

现在,变量 result 到 window 对象上了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值