JavaScript中的匿名函数、立即执行函数和闭包

匿名函数是没有函数名的,不能单独使用;

立即执行函数是基于匿名函数实现的,也没有函数名,会在定义后立即执行;

闭包是有权访问另一个函数作用域中的变量的函数。匿名函数、立即执行函数只要满足 有权访问另一个函数作用域中的变量 这一个条件,就成了闭包。

匿名函数

匿名函数:没有函数名的函数

匿名函数不能单独定义与使用

function foo() {
  console.log(‘普通函数‘);
}
// 去掉函数名 foo
function () { // SyntaxError: Function statements require a function name
  console.log(‘匿名函数‘);
}
匿名函数的应用场景
  • 用于函数表达式
  • 作为返回值
  • 用于定义对象方法
  • 作为回调函数
  • 用于立即执行函数
  • 用于DOM元素注册事件
  • 其他 ...

用于函数表达式

var sum = function (num1, num2) {
  return num1 + num2;
};
console.log(sum(2, 3));

作为返回值

function sum(sum1, sum2) { 

  return function() {
    return sum1 + sum2;
  }

}
console.log(sum(2, 3));    // [Function]
console.log(sum(2, 3)());  // 5

用于定义对象方法

var obj = {
  name: ‘uakora‘,
  age: 27,
  foo: function() {
    console.log(this.name + ‘ ‘ + this.age);
  }
};
obj.foo(); // uakora 27

作为回调函数

setTimeout(function() {
  console.log(‘匿名函数作为回调函数‘);
}, 1000);

用于立即执行函数

(function() {
  console.log(‘立即执行函数是基于匿名函数创建的‘);
}());

用于DOM元素注册事件

<input type="button" value="Click me!" id="btn">
<script>
    var btn = document.querySelector("#btn");
    //给按钮注册点击事件
    sub.onclick = function(){
        console.log(‘Click event‘);
    }
</script>

立即执行函数

立即执行函数(IIFE,Immediately-Invoked Function Expression),是一种在定义后就会立即执行的函数,其实质是一种语法。

立即执行函数的形式

两种常用形式

常用形式一:将匿名函数包裹在一个括号运算符中,后面再跟一个括号

(function () {
  console.log(‘立即执行函数‘);
})(); // !!!特别说明:若此立即执行函数后面立马又跟着一个立即执行函数,一定要在结尾加分号,否则后面的立即执行函数会报错!

// 上一个立即执行函数不加分号,下行代码将报错:TypeError: (intermediate value)(...) is not a function
(function (a, b, c) { // 形参
  console.log(a + b + c);  // 6
})(1, 2, 3); // 实参

常用形式二:匿名函数后面跟一个括号,再将整个包裹在一个括号运算符中

(function () {
  console.log(‘立即执行函数‘);
}());

(function (a, b, c) { // 形参
  console.log(a + b + c);  // 6
}(1, 2, 3)); // 实参

其他形式

可以用 !、+、-、~ 运算符代替常用形式一中的第一个括号

!function (a, b, c) {
  console.log(a + b + c);  // 6
}(1, 2, 3);

+function (a, b, c) {
  console.log(a + b + c);  // 6
}(1, 2, 3);

-function (a, b, c) {
  console.log(a + b + c);  // 6
}(1, 2, 3);

~function (a, b, c) {
  console.log(a + b + c);  // 6
}(1, 2, 3);
立即执行函数的作用

立即执行函数最本质的作用是:创建一个独立的作用域。利用这一功能,可以

  • 初始化数据和页面(只执行一次)
  • 模块化开发中,定义私有变量,防止污染全局(独立作用域)
  • 解决闭包中的状态保存问题;(常见的一个函数内部返回多个函数,调用这些函数,打印父函数内部变量的问题)

案例分析

var liList = document.getElementsByTagName(‘li‘);
for(var i = 0; i < liList.length; i++) {
  liList[i].onclick = function() {
    alert(i); // 结果:alert 出来的总是 6, 而不是0、1、2、3、4、5
  }
}

期望结果:0、1、2、3、4、5

实际结果:alert 出来的总是 6

原因分析:没有形成独立作用域,每次点击执行函数都是去访问同一个 i 值(且是循环结束后的值 6)

解决方案:使用立即执行函数为每个 li 元素的点击事件函数创建独立作用域

var liList = document.getElementsByTagName(‘li‘);
for (var i = 0; i < 6; i++) {
  (function (i) {
    liList[i].onclick = function () {
      alert(i); // 0、1、2、3、4、5
    }
  })(i);
}

闭包

??闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。闭包可以有函数名,也可以是匿名函数(没有函数名);

闭包形式一:直接以匿名函数的形式作为外部函数的返回值(普遍用法)

function outer() {
  var n = 1;

  return function() {
    n++; // 访问 outer 函数作用域中的变量 n,形成闭包
    console.log(n);
  }
}

outer()();

闭包形式二:在外部函数内定义一个内部函数,并返回内部函数名

function outer() {
  var n = 1;

  function inner() {
    n++; // 访问 outer 函数作用域中的变量 n,形成闭包
    console.log(n);
  }

  return inner;
}

outer()();

闭包形式三:在外部函数内定义一个立即执行函数

function outer() {
  var n = 1;

  (function() {
    n++; // 访问 outer 函数作用域中的变量 n,形成闭包
    console.log(n);
  })();

}

outer();
闭包的作用

作用一:可以直接访问函数作用域中的变量

作用二:可以将函数作用域中的变量如全局变量一样始终保存在内存中 (其实能否一直保存局部变量,跟闭包的执行形式有关

说明:作用二的解析为个人理解

以下示例为证明观点:闭包能否一直保存局部变量,跟闭包的执行形式有关

闭包执行方式一:在返回的闭包后面紧跟一个 () ,立即执行

function outer() {
  var n = 1;

  function inner() {
    n++;
    console.log(n);
  }

  return inner;
}

outer()();  // 2
outer()();  // 2

闭包执行方式二:将返回的闭包赋值给一个全局变量,全局变量运行 () 操作,执行闭包

function outer() {
  var n = 1;

  function inner() {
    n++;
    console.log(n);
  }

  return inner;
}

var inner = outer();
inner();    // 2
inner();    // 3  (是在 2 的基础上加 1)

以上两种执行方式单独使用,只能说明 第二种执行方式,能使闭包一直保存局部变量,但还不能证明闭包的第一种执行方式不能一直保存局部变量,因为每次执行 outer()函数时,变量 n 都被重新初始化了。

请继续看以下代码,同时使用了闭包的两种执行方式:

function outer() {
  var n = 1;

  function inner() {
    n++;
    console.log(n);
  }

  return inner;
}

var inner = outer();

outer()();  // 2
outer()();  // 2  (Flag1)

inner();    // 2  (Flag2)
inner();    // 3

可以看到,Flag1 跟 Flag2 的值都是 2,假设 outer()() 这样的闭包执行方式会一直保存局部变量在内存的话,那 Flag2 处的值应该是 3。

显然假设不成立。

从而说明:闭包并非就一定可以一直保存局部变量在内存,还跟执行方式有关

主动释放内存
function outer() {
  var n = 1;

  function inner() {
    n++;
    console.log(‘n = ‘, n);
  }

  return inner;
}

var inner = outer();
inner();    // 2
inner();    // 3

inner = null;  // 解除对闭包的引用,以便释放内存
闭包的应用场景
  • 保护函数局部变量的安全
  • 在内存中维持一个变量
  • 通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)

使用闭包的注意点

??由于闭包会携带 包含它的函数 的作用域,因此会比其他函数占用跟多的内存。过度使用闭包可能会导致内存占用过多

原文地址:https://www.cnblogs.com/uakora/p/12695429.html

时间: 2024-12-23 15:53:34

JavaScript中的匿名函数、立即执行函数和闭包的相关文章

JavaScript中的匿名函数及函数的闭包以及作用域

1. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

JavaScript中的匿名函数及函数的闭包

1.匿名函数 函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数:就是没有函数名的函数. 1.1 函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式 第一种:这也是最常规的一种 function double(x){ return 2 * x; } 第二种:这种方法使用了Function构造函数,把参数列表和函数体都作为字符串,很不方便,不建议使用. var double = new Function('x', 'return 2 * x;'); 第三种

JavaScript中的匿名函数及函数的闭包(转)

https://www.cnblogs.com/wl0000-03/p/6050108.html 1.匿名函数 函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数:就是没有函数名的函数. 1.1 函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式 第一种:这也是最常规的一种 function double(x){ return 2 * x; } 第二种:这种方法使用了Function构造函数,把参数列表和函数体都作为字符串,很不方便,不建议使用. var

Javascript中使用WScript.Shell对象执行.bat文件和cmd命令

Javascript中使用WScript.Shell对象执行.bat文件和cmd命令 http://www.cnblogs.com/ZHF/p/3328439.html WScript.Shell(Windows Script Host Runtime Library)是一个对象,对应的文件是C:/WINDOWS/system32/wshom.ocx,Wscript.shell是服务器系统会用到的一种组件.shell 就是“壳”的意思,这个对象可以执行操作系统外壳常用的操作,比如运行程序.读写注

在main函数之外执行函数的情况

1.onexit函数 #include "stdafx.h" #include "iostream" #include <stdlib.h> using namespace std; int func() //必须为int返回值 { cout<<"This is after main function"<<endl; system("pause"); return 0; } int main

javascript中的匿名函数整理笔记

以下为总结在开源的JavaScript框架中能看到很多这样语法结构(function(){})()比如我最近看的jQuery,及chediter.刚开始的时候我看到这样的结果有点奇怪,它是怎么执行的,并且这是什么样的语法结构,最近偶尔看闭包的时候,才发现原来这是JavaScript种匿名函数(看到这个有点汗,java的匿名类见过,就从来没想到JavaScript中会有匿名函数,也是学的不够牢固).现在我们了解到以上是JavaScript匿名函数的语法结构,怎么声明函数,匿名函数JavaScrip

JavaScript中以构造函数的方式调用函数

转自:http://www.cnblogs.com/Saints/p/6012188.html 构造器函数(Constructor functions)的定义和任何其它函数一样,我们可以使用函数声明.函数表达式或者函数构造器(见以前的随笔)等方式来构造函数对象.函数构造器和其它函数的区别在与它们的调用方式不同. 要以构造函数的方式调用函数,只需要在调用时在函数名称前加new 关键字,比如:function whatsMyContext(){ return this; }; 调用:new what

JavaScript中bind、call、apply函数用法详解

在给我们项目组的其他程序介绍 js 的时候,我准备了很多的内容,但看起来效果不大,果然光讲还是不行的,必须动手.前几天有人问我关于代码里 call() 函数的用法,我让他去看书,这里推荐用js 写服务器的程序猿看<javascript编程精粹> 这本书,crockford大神果然不是盖的.之后我在segmentfault上又看到了类似的问题,那边解答之后干脆这里记一笔. 首先,关于 js 定义类或对象的方法,请参看w3school 的这里的这里,写的非常详细和清晰,我不再赘言了. 为了介绍 b

Javascript中bind、call、apply函数用法

js 里函数调用有 4 种模式:方法调用.正常函数调用.构造器函数调用.apply/call 调用. 同时,无论哪种函数调用除了你声明时定义的形参外,还会自动添加 2 个形参,分别是 this 和arguments. arguments 不涉及到上述 3 个函数,所以这里只谈 this.this 的值,在上面 4 中调用模式下,分别会绑定不同的值.分别来说一说: 方法调用: 这个很好理解,函数是一个对象的属性,比如 var a = { v : 0, f : function(xx) { this