javascript的作用域链及闭包

作用域

作用域就是变量和函数的可访问范围。JavaScript没有块级作用域,只有函数作用域:变量在声明他们的函数体及其子函数内是可见的。
变量没有在函数内声明或没有带var就是全局变量,拥有全局作用域,window对象的所有属性拥有全局作用域,在代码任何地方都可以访问。
函数内部声明并且以var修饰的变量就是局部变量,只能在函数体内使用,函数的参数虽然没有使用var单仍然是局部变量。

var a=3; //a是全局变量
function fun(b){ //b是局部变量
    c=2; //c是全局变量
    var d=5; //d是局部变量
    function subFn(){
        var e=d; //父函数的局部变量对子函数可见
        for(var i=0;i<3;i++){
           alert(i);
        }
        alert(i);//3: 在for循环内声明,循环外function内仍然可见,没有块作用域
    }
}
alert(c); //2:在function内声明但不带var修饰,仍然是全局变量

真正解释执行之前,JavaScript解释器会预解析代码,将变量、函数声明部分提前解释

alert(a); //undefined
var a=3;
alert(a); //3
alert(b); //不会有任何输出

上面代码在执行var a =3;前声明部分就已经预解析(但是不会执行赋值语句)。所以上面代码和下面的代码是一样的。

var a;
alert(a); //undefined
a=3;
alert(a); //3
alert(b); //不会有任何输出

执行环境

执行环境(execution econtext)是javascript中最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象(Variable Object,VO),环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。
全局执行环境是最外围的一个执行环境。在web浏览器中全局执行环境是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的(全局执行环境直到应用程序退出–例如关闭网页或浏览器时才会被销毁)。

作用域链

当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain,sc),来保证对执行环境有权访问的变量和函数的有序访问。作用域的第一个对象始终是当前执行代码所在环境的变量对象(VO)

function a(x,y){
    var b=x+y;
    return b;
}

在函数a创建的时候它的作用域链填入全局对象,全局对象中有所有全局变量。
这里写图片描述

如果执行环境是函数,那么将其活动对象(activation object, AO)作为作用域链第一个对象,第二个对象是包含环境,下一个是包含环境的包含环境。。。。。

function a(x,y){
    var b=x+y;
    return b;
}
var tatal=a(5,10);

这时候 var total=a(5,10);语句的作用域链如下
这里写图片描述

在函数运行过程中标识符的解析是沿着作用域链一级一级搜索的过程,从第一个对象开始,逐级向后回溯,直到找到同名标识符为止,找到后不再继续遍历,找不到就报错。

闭包

背景

由于种种原因我们需要读取函数内的局部变量。正常情况下是办不到,但是可以在函数内部,在定义一个函数。

function a(){
    var n=0;
  function b(){
       alert(++n);
  }
    return b;
}
var res=a();
b(); //1

在上面的代码中,函数b就被包括在函数a内部,这时a内部的所有局部变量,对b都是可见的。
既然b可以读取a中的局部变量,那么只要把b作为返回值,我们不就可以在a外部读取它的内部变量了!

闭包的概念

上一节代码中的b函数,就是闭包。闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成”定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的用途
  • 使a函数的局部变量的值始终保持在内存中。使javascript的垃圾回收机制GC不会收回a所占用的资源。
  • 读取函数内部的变量。
闭包的应用场景

保护函数内的变量安全。函数a中的n只有函数b可以访问,无法通过其他途径访问,因此保护了n的安全性。
- 在内存中维持一个变量。函数a的n一直存在内存中,因此每次执行c(),都会给i自加1。
- 通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)

function Constructor(...) {  
  var that = this;  
  var membername = value; 
  function membername(...) {...}
}
使用闭包的注意点
  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
  • 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

参考文献

  1. JavaScript作用域链
  2. 学习Javascript闭包(Closure)
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值