欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

函数作用域查找、闭包和匿名函数整理

程序员文章站 2022-08-17 17:49:46
一、函数作用域查找1、定义说明1)、函数当前作用域查找不到,可以访问外层函数作用域的活动对象(参数、局部变量、定义在外层函数体里的函数)2)、外层的外层函数。。。一直到全局 第一条说明:定义在外层函数体里的函数,包括当前函数,当前函数调用自己的时候,就是递归调用。 2、原理执行环境、作用域链、作用域 ......

一、函数作用域查找
1、定义说明
1)、函数当前作用域查找不到,可以访问外层函数作用域的活动对象(参数、局部变量、定义在外层函数体里的函数)
2)、外层的外层函数。。。一直到全局

第一条说明:定义在外层函数体里的函数,包括当前函数,当前函数调用自己的时候,就是递归调用。

2、原理
执行环境、作用域链、作用域、活动对象
1)、调用内层函数,会创建一个执行环境,执行环境会关联一个作用域链
2)、调用内层函数时,所有的外层函数都已经调用完毕或者外层函数调用中,所有只要把所用外层函数作用域(包括最外层全局)的活动对象,关联到当前内层函数的作用域链上。
3)、最后创建内层函数作用域的活动对象,并且关联到作用域链的最前端。

活动对象注释:
函数参数,函数体里面定义的局部变量,函数体里面定义的函数

3、代码示例

var str1 = '全局变量';
function func(arg) {
  var str2 = '外层局部变量';
  function funcinner1() {
    console.log('外层函数的其他函数');
  }
  function funcinner2(arginner2) {
    var str3 = '内层函数变量';
    console.log(str3);
    console.log(arginner2);

    console.log(arg);
    console.log(str2);
    funcinner1();
    console.log(str1);
  }
  return funcinner2;
}
var result = func('外层函数参数')
result('内层函数参数');

执行结果:
内层函数变量
内层函数参数
外层函数参数
外层局部变量
外层函数的其他函数
全局变量

 

二、闭包
1、定义
定义在函数体里的内部函数,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

2、通俗解释
内层函数作为外层函数执行结果被返回,当外层函数执行完毕后,定义在其内部的局部变量、参数和声明的其他内部函数并没有被回收,而是可以通过返回的函数的执行被获取,即形成了闭包。

3、形成闭包的条件
1)、嵌套函数
2)、内层函数访问外层函数的活动对象
3)、内层函数子在外层函数之外的地方被调用

第三条件解释:一般是内层函数作为外层函数的返回值,外层函数之外的地方,可以先调用外层函数,拿到对应返回值,再通过返回值调用内层函数。

4、特点:
1)、作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
2)、一个闭包就是当一个函数返回时,一个没有释放资源的栈区。

5、原理
函数作用域查找

6、代码示例

function outer() {
  var scope = 10;
  return function inner() {
    scope += 10;
    console.log(scope);
  }
}
var fn = outer();
fn();

执行结果:
20
//inner函数作为outer函数执行结果被返回,当outer函数执行完毕后,定义在其内部的变量scope并没有被回收,而是可以通过函数fn的执行被获取,这里的inner函数,即形成了闭包。

7、好处:
1)、变量长期驻扎在内存中;
2)、避免全局变量的污染;

避免全局变量的污染说明:
上面代码示例中,我们也可以把scope定义成全局变量,但是这样我们就没法控制仅允许inner函数可以修改scope的值,因为全局变量,所有地方都可以访问。

8、坏处:
1)、内存消耗
通常来说,函数的活动对象会随着执行期上下文一起销毁,但是,由于闭包引用另外一个函数的活动对象,因此这个活动对象无法被销毁,这意味着,闭包比一般的函数需要更多的内存消耗。

进一步说明:尤其在ie浏览器中需要关注。由于ie使用非原生javascript对象实现dom对象,因此闭包会导致内存泄露问题。
2)、性能问题
使用闭包时,会涉及到跨作用域访问,每次访问都会导致性能损失。

进一步说明:因此在脚本中,最好小心使用闭包,它同时会涉及到内存和速度问题。不过我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响。

10、闭包常用案例
1)、延迟输出
2)、累加器

代码1:
//延迟输出
for (var i = 0; i < 5; ++i) {
  (function (i) {
    settimeout(function () {
      console.log(i + ' '); 
    }, 100); 
  })(i);
}

执行结果:
0 
1 
2 
3 
4

代码2:
//累加器
//initnum: 初始值, step: 步长
function gettotalizer(initnum, step) {
  var num = initnum;
  return function() {
    num = num + step;
    return num;
  }
}

var add = gettotalizer(10, 5);
console.log(add());
console.log(add());
console.log(add());
console.log(add());

执行结果:
15
20
25
30

 

三、匿名函数
1、定义:
匿名函数,就是没有函数名的函数

2、三种调用方法
1)、可以定义一个匿名函数,并且立即调用。
2)、可以定义一个匿名函数,赋值给一个变量,通过这个变量调用函数。
3)、匿名函数作为函数返回值返回,通过返回值调用匿名函数。

3、匿名函数与闭包关系
1)、匿名函数和闭包没有实际关系
2)、闭包的条件,需要返回一个内层函数,这个内层函数访问外层函数的活动对象,这个内层函数在外层函数之外的地方被调用。
3)、上面内层函数只能通过函数的返回值被调用,不能通过方法名直接调用,这种情况下,把这个内层函数声明成匿名函数更合理,通常也是这么用的。

4、代码示例

//1、通过表达式自我执行
(function() {
  console.log('执行');
})();

//2、将匿名函数赋给变量
var result = function () { 
  console.log('执行');
};
result();

//3、匿名函数作为函数返回值
function func() {
  var i = '局部变量i';
  return function () {     
    return i;
  };
}
console.log(func()());