ES5 - 函数作用域 、闭包(附闭包经典面试题)

本文详细介绍了ES5中的函数作用域和闭包概念。函数作用域使得局部变量在函数内部使用,全局变量在任何地方都可访问。闭包则允许内部函数访问外部函数的变量,形成作用域链。通过示例展示了闭包的创建、工作原理及其在内存管理中的角色,强调了闭包在变量持久化和读取内部变量方面的应用。最后,提供了一个综合案例及闭包的经典面试题,帮助读者深入掌握闭包的运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ES5函数作用域


函数作用域: 在 JavaScript函数中声明的变量,会成为函数的局部变量。在函数外部不能访问。

全局作用域: 函数之外声明的变量,会成为全局变量。在函数内部可以访问。当函数嵌套的时候,内部函数与外部函数的这个变量就组成了闭包。

作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突

// ES5中没有块级作用域
{
  var a = 10
}
console.log(a); // 10
//全局作用域:global/window/本文件内
var v1 = 10;  //全局作用域
v2 = 20; // 所有末定义直接赋值的变量自动声明为拥有全局作用域
function foo() {
  //函数作用域、局部作用域
  var a = 3;
  console.log(v1, v2); 
  console.log(this);
}
foo()
console.log(a); // a is not defined

案例:

var a = 10;

function foo() {
  // 当函数内部有变量a时,会产生局部作用域,外界全局作用域中的a不会对函数内部造成影响
  // 如果把函数内部的变量a注释掉是,函数内部的a输出的就是全局作用域中的a
  console.log(a); //undefined
  var a = 100;
  console.log(a); // 100

  function fn() {
    console.log(a); //undefined
    var a = 200;
    console.log(a); // 200
  }
  fn()
}
foo()

  • 自由变量

    首先认识一下什么叫做 自由变量 。如下代码中,console.log(a)要得到a变量,但是在当前的作用域中没有定义a(可对比一下b)。当前作用域没有定义的变量,这成为 自由变量 。自由变量的值如何得到 —— 要到创建这个函数的那个父级作用域寻找,如果没有就一直向上级祖先元素寻找(这就是所谓的"静态作用域",静态作用域是指函数的作用域在函数定义时就已经确定了)

    var a = 100
    function fn() {
        var b = 200
        console.log(a) // 这里的a在这里就是一个自由变量  // 100
        console.log(b)
    }
    fn()
    
  • 作用域链

    如下代码,若是在本函数内找不到变量,便向父级(上一层)寻找,直到找到全局作用域还没有找到,就宣布放弃。这种一层一层的关系,就是作用域链 。

    var a = 100
    function F1() {
      var b = 200
      function F2() {
        var c = 300
        console.log(a) // 自由变量,顺作用域链向父作用域找 //100
        console.log(b) // 自由变量,顺作用域链向父作用域找 //200
        console.log(c) // 本作用域的变量  //300
      }
      F2()
    }
    F1()
    

闭包


闭包: 是指有权访问另一个函数作用域中的变量的函数。它由两部分构成:函数,以及创建该函数的环境。

闭包的生成有三个必要条件

  • 函数嵌套函数
  • 内部函数引用了外部函数中的数据(属性、函数)
  • 参数和变量不会被回收

创建闭包最常见方式,就是在一个函数内部创建另一个函数。 下面例子中的 example就是一个闭包:

function func() {
  var a = 1, b = 2;

  function example() {
    return a + b;
  }
  return example;
}
console.log(func()()); // 3

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


闭包的用途: 闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

function f1() {
  var n = 999;
  nAdd = function () { n += 1 }
  function f2() {
    console.log(n);
  }
  return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

解析: f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。


综合案例-作用域链-闭包-经典面试题

var a = 10
function fn() {
  var b = 20
  function bar() {
    console.log(a + b) //30
    // 父级作用域中有两个b可以选择,创建这个函数的父级作用域优先
  }
  return bar
}
var x = fn(), // 执行fn() 返回的是bar
b = 200
x() //执行x,就是执行bar函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值