`

js全局变量与局部变量 预解析与作用域链详解

    博客分类:
  • js
阅读更多
js全局变量与局部变量 预解析与作用域链详解【转】
局部变量和全局变量同名时,会隐藏这个全局变量;
4.变量

关键字: 4.变量
4.1 变量的类型
  JS是松散类型的语言

4.2 变量的声明
  var 声明是变量的标准声明
  var 声明的变量是永久性的,不能用delete运算符删除

  全局对象,调用对象初始化时,JS解析器会在相应的代码段里寻找var声明的变量,
  然后在全局对象,调用对象中创建相应的属性,此时它是未赋值的(undefined),
  当程序执行到相应的var声明代码段时才会给相应对象的属性赋值

  重复的声明:根据以上var声明作用的理解,重复的var声明不会造成任何错误,
  var声明只是方便创建全局对象,调用对象的属性,代码只是赋值用的

  遗漏的声明:给未声明的变量赋值,JS会隐式声明全局变量(在全局对象中增加相应的属性),然后给其赋值

4.3 变量的作用域
  全局变量,局部变量
  局部变量和全局变量同名时,会隐藏这个全局变量
  4.3.1 没有块级作用域
    函数中声明的变量,无论在哪里声明的,在整个函数中它们都是有定义的
  4.3.2 未声明的变量和未赋值的变量
    alert(u);会产生一个错误--使用未声明的变量会产生一个错误
    var u;alert(u);会跳出undefined---使用未赋值的变量,使用的它的默认值undefined

4.4 基本类型和引用类型
  第三章已经讲过

4.5 垃圾收集
  同java

4.6 作为属性的变量
  4.6.1 全局对象
    JS解释器开始运行时,在执行任何JS代码前,会创建一个全局对象,这个对象的属性就是JS全局变量,
    并初始化为undefined
    var声明一个JS全局变量时,实际上就是定义了一个全局对象的属性,
  
    JS解释器还会用预定义的值和函数来初始化全局对象的许多属性,如:Infinity parseInt Math
  
    非函数内部可以用this来引用这个全局对象
  
    客户端的JS中,Window对象代表浏览器窗口,它包含该窗口中的所有JS代码的全局对象,具有自我引用的window属性
  4.6.2 局部变量:调用对象
    函数的局部变量存放在调用对象的属性
    调用对象是一个完全独立的对象,所以可以防止覆盖同名的全局变量
  4.6.3 JS的执行环境
    JS解释器执行一个函数时,会创建一个执行环境
    JS允许同时存在多个全局执行环境,如:JS客户端的ifame的情况
  
4.7 深入理解变量作用域
  每个JS执行环境都有一个和它相关联的作用域链它是一个对象列表或对象链.

  查询x:变量名解析(variable name resolution)的过程,它开始查看作用域链的每一个对象,
  如果有,返回值,如果没有继续查询下一个对象,以些类推.

  作用域链的优先级:嵌套函数的调用对象>调用对象>全局对象


  根据以上理解说明JS初始化的过程:

  在JS解释器执行任何代码之前,创建全局对象
    用预定义的值和函数来初始化全局对象中的属性,eg.Math,Infinity,parseInt
    搜索函数外的var声明,创建全局对象相应的属性,初始化为undefined
  创建全局的执行环境,作用域链只有一个对象-全局对象
  依次执行代码
    遇到var声明赋值语句给全局对象相应的属性赋值
    遇到未声明赋值语句,在全局对象中增加相应的属性,并赋值
    遇到函数调用,创建调用对象
      搜索函数中的var声明和参数,创建调用对象相应的属性,初始化为undefined
      创建函数执行环境,作用域链--第一对象:调用对象;第二对象:全局对象
      依次执行代码
        遇到var声明赋值语句给调用对象相应的属性赋值
        遇到未声明赋值语句,在全局对象中增加相应的属性,并赋值
        遇到函数调用,创建嵌套函数的调用对象
          搜索嵌套函数中的var声明和参数,创建嵌套函数的调用对象相应的属性,初始化为undefined
          创建嵌套函数执行环境,作用域链--第一对象:嵌套函数的调用对象;第二对象:调用对象;第三对象:全局对象
        
    依此类推
  
    eg1.
      var scope="global";
      function f(){
        alert(scope);
        var scope="local";
        alert(scope);
      }
      f();
      过程:
      创建全局对象,搜索函数外的var声明语句,在全局对象中创建scope属性,scope=undefined
      创建全局的执行环境,作用域链只有一个对象:全局对象
      依次执行代码:
        var scope="global"时,变量名解析开始,在全局对象属性中查找scope属性
        把"global"赋给scope
        遇到函数调用:创建调用对象
          搜索函数中的var声明语句和参数,在调用对象中创建scope的属性,scope=undefined
          创建函数执行环境,作用域链:调用对象>全局对象
          依次执行代码:
            alert(scope),查询scope,变量名解析,先搜索调用对象,找到scope属性,其值为undefined,执行
            var scope="local",查询scope,变量名解析,先搜索调用对象,找到scope属性,scope="local"
            alert(scope),查询scope,变量名解析,先搜索调用对象,找到scope属性,其值为"local",执行
    
    eg2.
      var scope="global";
      function f(){
        alert(scope);
        scope="local";
        alert(scope);
      }
      f();
    过程:
      创建全局对象,搜索函数外的var声明语句,在全局对象中创建scope属性,scope=undefined
      创建全局的执行环境,作用域链只有一个对象:全局对象
      依次执行代码:
        var scope="global"时,变量名解析开始,在全局对象属性中查找scope属性
        把"global"赋给scope
        遇到函数调用:创建调用对象
          搜索函数中的var声明语句和参数,没有找到var声明语句
          创建函数执行环境,作用域链:调用对象>全局对象
          依次执行代码:
            alert(scope),查询scope,变量名解析,先搜索调用对象,没找到scope属性,再搜索全局对象,找到scope属性,其值为"global"执行
            scope="local",查询scope,变量名解析,先搜索调用对象,没找到scope属性,,再搜索全局对象,找到scope属性,scope="local"
            alert(scope),查询scope,变量名解析,先搜索调用对象,没找到scope属性,再搜索全局对象,找到scope属性,其值为"local",执行
    eg3.
      scope1="global";
      alert(scope1);
      function f(){
        alert(scope2);
        scope2="local";
      }
      f();
    过程:
      创建全局对象,没有找到var声明语句,没有自定义的全局对象属性
      创建全局的执行环境,作用域链只有一个对象:全局对象
      依次执行代码:
        scope1="global"时,变量名解析开始,作用域链是没有找到scope1属性,在全局对象属性中创建scope1属性,并赋值为"global"
        alert(scope1)时,变量名解析开始,作用域链是找到scope1属性,其值为"global",执行
        遇到函数调用:创建调用对象
          搜索函数中的var声明语句和参数,没有找到var声明语句
          创建函数执行环境,作用域链:调用对象>全局对象
          依次执行代码:

            alert(scope2),查询scope2,变量名解析,作用域链是没有找到scope2属性,报错scope2 is not defined



函数

引申一下,function. 还记得上面提到的预解析,在javascript的预解析中,除了对var 变量的预定义,还包括了提取对函数的定义,所以可以在script的任何地方定义函数,在任何地方调用。不限于它之前.

但函数的定义方式,包括了一种叫字面量定义法, 用var的方法声明function.看下面
alert(typeof y3); //结果?
var y3 = function (){ console.log('1'); }

还记得这个约定吧:调用必须出现在声明之后,为什么呢,如果理解了上面,其实这里答案已经明了。javascript 引擎在预解析 var 时 会给他们一个初始值 undefined,这样一来,如果我们在它的声明之前调用它,javascript 引擎还没拿到它的真实值,自然会报"xxx is not a function" 的错.这也理清了为什么同为函数声明,一个却关系到声明和调用的顺序,一个却无这样的约束。
结论

它是函数,是js执行,动态修改的结果,依然遵循了变量的预解析规则(在上面alert的时候,它还并没有拿到字面量函数的信息)。

如果是两个混合呢。看下面, 同时存在了为y4的变量和function。
alert(typeof y4); //结果?
function y4(){
console.log('y4')
}
var y4;

因为 javascript 在预解析时function的声明优先级高的缘故,所以y4自然为function类型, 但是在当y4 赋值之后(此时js引擎处于执行过程中),它对js的赋值操作将会覆盖function的声明。所以:
alert(typeof y5);
var y5 = 'angle';
function y5(){
console.log('ghost');
}
alert(y5);

第一次alert结果,因为它处于js 执行过程中的顶端,所以为 function。 第二次再alert时, 它的值已经被重写为5(不要被function的定义位置在下所迷惑。)

从js的解析和执行分开来想,才发现眼前豁然开朗,很多问题的答案都很自然得浮出水面,正如那篇文章作者所说,

> "一旦理解了执行环境、调用对象、闭包、词法作用域、作用域链这些概念,JS语言的很多现象都能迎刃而解。"

参考:http://blog.csdn.net/zhangw428/article/details/6060972
分享到:
评论

相关推荐

    C语言 全局变量和局部变量详解及实例

    C语言 全局变量和局部变量详解 核心内容: 1、局部变量和全局变量 变量按照作用域分为:全局变量和局部变量 全局变量的作用域:从定义位置开始到下面整个程序结束。 局部变量的作用域:在一个函数内部定义的...

    JS 作用域与作用域链详解

    在函数内声明的变量具有函数作用域(function scope),属于局部变量 局部变量优先级高于全局变量 代码如下: var name=”one”; function test(){  var name=”two”;  console.log(name); //two } test(); 函数...

    详解Vue 全局变量,局部变量

    局组件和局部组件 1.先定义组件 Vue....4.组件是一个独立的作用域, 也可以看成一个特殊的vue实例, 可以有data, methods,computed等等 注意: 组件的data是函数, 函数中需要返回一个对象作为组件的data 全局组件案例

    Javascript变量作用域详解

    JS中变量的作用域分为全局变量和局部变量,函数内定义的称为局部变量,函数外的称为全局变量。(“函数外的称为全局变量”是相对的,另此处讨论的前提是用var显式声明的变量,函数内不用var定义的变量默认是全局变量...

    【JavaScript源代码】详解JavaScript闭包问题.docx

     一、变量的作用域 在介绍闭包之前,我们先理解JavaScript的变量作用域。变量的作用域分为两种:全局变量和局部变量。 var n = 999; //全局变量 function f1() { a = 100; //在这里a也是全局变量 alert(n); }...

    JavaScript作用域示例详解

    作用域是JavaScript最...在JavaScript中,变量的作用域有全局作用域和局部作用域两种。 一、JavaScript中无块级作用域 在Java或C#中存在块级作用域,即:大括号也是一个作用域。 public static void main () { if

    javascript 作用于作用域链的详解

    javascript 作用于作用域链的详解 一、JavaScript作用域 任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的...

    PHP的变量类型和作用域详解

    PHP中变量的作用域可以分为:超全局(全局变量的特殊类型,在局部范围里可直接使用),全局,局部,静态(是局部变量的特殊类型)在PHP中,全局变量实际上是静态全局变量,如果不用unset显式的释放,那么等脚本运行结束...

    C/C++中作用域详解

    全局变量不受作用域的影响(也是说,全局变量的生命期一直到程序的结束)。如果在一个文件中使用extern关键字来声明另一个文件中存在的全局变量,那么这个文件可以使用这个数据。  (2) 局部变量:  局部变量出现...

    JS作用域链详解

    本文实例为大家分享了JS作用域链的相关内容,供大家参考,具体内容如下 1、所有全局变量和函数都是作为window对象的属性和方法创建的。 2、在某个方法函数的作用域中,所有代码执行完之后,该作用域被销毁,保存在...

    javascript作用域链与执行环境详解

    作用域链取决于函数被声明时的位置,解析标识符的时候就先找当前作用域,再向外查找,直到全局,这样一个顺序;和函数在哪里调用无关; 执行环境就是函数可访问的数据和变量的集合,也就是函数的作用域链上的所有...

    详解JavaScript作用域、作用域链和闭包的用法

    作用域可分为全局作用域和局部作用域。 1.1 全局作用域 全局作用域是指最外层函数外面定义的变量和函数的集合。 换言之,这些最外层函数外面定义的变量和函数在任何地方都能访问。 举个例子: // 最外层定义变量 ...

    python函数的作用域及关键字详解

    搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。 local和enclosing是相对的,enclosing变量相对上层来说也是local。 作用域产生 在Python中,只有模

    Python中的变量和作用域详解

    python中的作用域分4种情况: L:local,局部作用域,即函数中定义的变量; E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的; G:globa,全局变量,就是模块级别定义...

    C++中 静态局部变量实例详解

    C++中 静态局部变量实例详解 以前经常使用和了解static作用在全局变量前,以至于甚至把static当做全局变量的代名词,但是其实static还可以作用于局部变量前 静态局部变量的意义: 1.分配空间在全局数据栈上 2.作用...

    Python中的作用域规则详解

    在Python 2.0及之前的版本中,Python只支持3种作用域,即局部作用域,全局作用域,内置作用域;在Python 2.2中,Python正式引入了一种新的作用域 — 嵌套作用域;在Python 2.1中,嵌套作用域可以作为一个选项被开启...

    JavaScript中闭包的写法和作用详解

    变量的作用域无非就是两种:全局变量和局部变量。 Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。 其中内部函数中可以访问外部函数的变量,是因为内部函数的作用域链中包含了外部函数的作用域; ...

    JavaScript中闭包的详解

    闭包是什么 在 JavaScript 中,闭包是一个让人很难弄懂的概念。ECMAScript 中给闭包的...全局变量的作用域就是全局性的,在 js 的任何地方都可以使用全局变量。在函数中使用 var 关键字声明变量,这时的变量即是局部变

    Python作用域与名字空间原理详解

    Python具有静态作用域,变量的作用域由它定义的位置决定,而与调用的位置无关。  a = 2  def f():  a = 2 第一行的a的作用域是全局作用域,作用于定义位置后面的所有位置。 第四行的a的作用域是局部作用域,...

Global site tag (gtag.js) - Google Analytics