js的作用域和全局变量

1.定义

局部变量:变量在函数内声明,只能在函数内部访问。
全局变量:变量在函数外定义,整个代码都可以调用的变量。
var:函数内部用var来声明局部变量
注意:
1.都加var,在方法内则是局部变量,在方法外则是全局变量。
2.在方法内,加var为局部变量,不加var则是全局变量(在执行当前方法之后)

2.经典例子

(1)输出结果为:100,10,100

1 var a = 10;
2 function test(){
3     a = 100;
4     console.log(a);
5     console.log(this.a);
6     var a;
7     console.log(a);
8 }
9 test();

分析:Javascript在执行前会对整个脚本文件的声明部分做完整分析(包括局部变量),从而确定变量的作用域。
(1)所以在函数test执行前,由于第6行声明了局部变量a,所以函数内部的a都指向已经声明的局部变量,所以第4行输出100。
(2)第5行输出this.a,我们都知道,函数内部的this指针指向的是函数的调用者,在这里函数test被全局对象调用,所以this指针指向全局对象(这里即window),所以this.a = window.a,一开始生命了全局变量a=10,所以第5行输出结果为10。
(3)第7行输出结果为100,因为局部变量a在第3行已经被赋值了100,所以直接输出局部变量a的值。

(2)输出结果:undefined 10

1 var a = 100;
2 function test(){
3     console.log(a);
4     var a = 10;
5     console.log(a);
6 }
7 test()

分析:Javascript在执行前会对整个脚本文件的声明部分做完整分析(包括局部变量),但是不能对变量定义做提前解析
(1)在这个函数中,执行第3行前,可以认为已经声明了变量a,但是并没有定义(这里即赋值),所以第3行输出结果为undefined,而不是null
(2)执行第4行a =10后,变量a的值就为10,所以第5行输出结果为10。

(3)输出结果:100,10,10

1 var a = 100;
2 function test(){
3     console.log(a);
4     a = 10;
5     console.log(a);
6 }
7 test();
8 console.log(a)

分析:在函数内部,一般用var声明的为局部变量,没用var声明的一般为全局变量。
(1)在test函数执行后,a=10声明了一个全局变量,所以第3行的a应该输出全局变量的值,而在函数执行之前已经声明过一个全局变量并赋值100,所以这里第上输出100。
(2)第4行给全局变量a 重新赋值10,所以全局变量a的值变成10,所以第5行输出10。
(3)在函数test外部,第8行输出全局变量a的值,因为全局变量被重新赋值为10,所以输出结果即为10。

3.js中作用链

1.变量提升
在生成执行环境时,会有两个阶段。第一个阶段是创建的阶段,JS 解释器会找出需要提升的变量和函数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存入内存中,变量只声明并且赋值为 undefined,所以在第二个阶段,也就是代码执行阶段,我们可以直接提前使用。
在提升的过程中,相同的函数会覆盖上一个函数,并且函数优先于变量提升

2.执行处理过程大致如下:

var outerVar1 = "var in global code";
 function outerFunc(arg1, arg2) {
     var innerVar1 = "var in function code";
     function innerFunc() { return outerVar1 + "-" + innerVar1 + "-" + (arg1 + arg2); }
     return innerFunc();
 }
 var outerVar2 = outerFunc(10, 20);

1.引擎启动,初始化Global Object,即window对象,全局的调用对象,建立作用域链,假设为scope_1,作用域链中只包含全局的上下文环境,即Global Object。

2.语法分析阶段,扫描JavaScript代码,获取代码中变量和函数定义,其扫描过程如下:
(1)发现变量outerVar1,在Global Object的varDecls中添加outerVar1属性,值为undefined。
(2)发现函数outerFunc的定义,在Global Object的funcDecls中添加outerFunc,并创建函数对象(这里应该创建的是函数的原型对象),将scope_1传递给outerFunc的函数对象,即outerFunc内部的[[scope]]属性。另外注意,创建过程并不会对函数体中的JavaScript代码做特殊处理,可以理解为只是将函数JavaScript代码保存中函数对象的内部属性上,在函数执行时再做进一步处理。也就是说,这一步大概处理的就是记录函数定义,赋值[[scope]]属性记录当前定义的作用域,而没有进一步对outerFunc里面的代码进行进一步的语法分析。
(3)发现变量outerVar2,在Global Object中的varDecls中添加属性,值为undefined。

3.全局环境下语法分析结束,执行outerVar1赋值语句,赋值为"var in global code"。

4.执行outerFunc,获取返回值。
(1)创建调用对象,假设为avt_obj_1。同时将avt_obj_1链接起outerFunc的[[scope]]属性,构成一个新的作用域链,假设为scope_2,scope_2中的第一个对象为act_obj_1,act_obj_1指向scope_1。
(2)处理参数列表,在act_obj_1中设置属性arg1、arg2,值分别为10和20。创建arguments对象并进行设置,将arguments设置为act_obj_1的属性。
(3)对outerFunc函数体进行语法分析,注意这里在全局语法分析的时候并没有对outerFunc函数体进行语法分析:
<1>发现变量innerVar1,在act_obj_1中的varDecls添加innerVar1属性,值为undefined。
<2>发现函数innerFunc的定义,使用这个定义创建函数对象,并将scope_2传递给innerFunc,作为innerFunc的[[scope]]属性,在act_obj_1的funcDecls添加innerFunc。
(4)outerFunc函数语法分析结束,执行innerVar1赋值语句,赋值为"var in function code"。
(5)执行innerFunc,执行函数的处理流程是一致的:
<1>创建调用对象,假设为act_obj_2;同时将avt_obj_2链接起innerFunc[[scope]]属性,构成一个新的作用域链,假设为scope_3,scope_3中的第一个对象为act_obj_2,act_obj_2指向scope_2。
<2>处理参数列表,因为innerFunc没有参数,所以只需要创建arguments对象并设置为act_obj_2的属性。
<3>对innerFunc进行语法分析,识别变量和函数,但没有发现变量定义和函数声明。
<5>执行innerFunc函数。对任何一个变量引用,**从scope_3开始进行链式搜索,以scope_3->scope_2->scope_1的顺序进行搜索,**结果发现outerVar1在scope_1中的Global Object发现;innerVar1、arg1、arg2在scope_2中的act_obj_1中找到。
检查scope_3和act_obj_2的引用,发现没有其他引用,则丢弃,让引擎进行垃圾回收。
<6>返回innerFunc执行的值。
(6).检查没有对act_obj_1和scope_2的引用,则丢弃act_obj_1和scope_2。
(7)返回结果。

5.将outerFunc的返回结果赋值给outerVar2。

3.作用链的理解:
每次进入一个新的执行环境,都会创建一个用于搜索变量和函数的作用域链。作用域链是函数被创建的作用域中对象的集合。作用域链可以保证对执行环境有权访问的所有变量和函数的有序访问。

作用域链的最前端始终是当前执行的代码所在环境的变量对象(如果该环境是函数,则将其活动对象作为变量对象),下一个变量对象来自包含环境(包含当前还行环境的环境),下一个变量对象来自包含环境的包含环境,依次往上,直到全局执行环境的变量对象。全局执行环境的变量对象始终是作用域链中的最后一个对象。
在这里插入图片描述
总结:
1.执行环境决定了变量的生命周期,以及哪部分代码可以访问其中变量
2.执行环境有全局执行环境(全局环境)和局部执行环境之分。
3.每次进入一个新的执行环境,都会创建一个用于搜索变量和函数的作用域链
4.函数的局部环境可以访问函数作用域中的变量和函数,也可以访问其父环境,乃至全局环境中的变量和环境。
5.全局环境只能访问全局环境中定义的变量和函数,不能直接访问局部环境中的任何数据。
6.变量的执行环境有助于确定应该合适释放内存。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值