热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java中的匿名函数结构与JavaScript中的匿名函数、立即执行函数及闭包的对比分析

在Java中,匿名函数作为一种无名的函数结构,无法独立调用;而在JavaScript中,不仅有类似的匿名函数,还有立即执行函数(IIFE)和闭包等高级特性。立即执行函数同样基于匿名函数实现,但会在定义时立即执行,而闭包则通过嵌套函数来捕获外部变量,实现数据封装和持久化。这些不同的函数形式在实际开发中各有应用场景,理解其特点有助于更好地利用语言特性进行编程。

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

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

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

匿名函数

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

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

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元素注册事件

var btn = document.querySelector("#btn");

//给按钮注册点击事件

sub.Onclick= function(){

console.log('Click event');

}

立即执行函数

立即执行函数(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[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(&#39;n = &#39;, n);

}

return inner;

}

var inner = outer();

inner(); // 2

inner(); // 3

inner = null; // 解除对闭包的引用,以便释放内存

闭包的应用场景

保护函数局部变量的安全

在内存中维持一个变量

通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)

使用闭包的注意点

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



推荐阅读
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • 本文介绍了如何利用JavaScript或jQuery来判断网页中的文本框是否处于焦点状态,以及如何检测鼠标是否悬停在指定的HTML元素上。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文详细介绍了Java中org.eclipse.ui.forms.widgets.ExpandableComposite类的addExpansionListener()方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。这些示例来源于多个知名开源项目,具有很高的参考价值。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
  • 在金融和会计领域,准确无误地填写票据和结算凭证至关重要。这些文件不仅是支付结算和现金收付的重要依据,还直接关系到交易的安全性和准确性。本文介绍了一种使用C语言实现小写金额转换为大写金额的方法,确保数据的标准化和规范化。 ... [详细]
  • JavaScript中属性节点的类型及应用
    本文深入探讨了JavaScript中属性节点的不同类型及其在实际开发中的应用,帮助开发者更好地理解和处理HTML元素的属性。通过具体的案例和代码示例,我们将详细解析如何操作这些属性节点。 ... [详细]
  • 在前两篇文章中,我们探讨了 ControllerDescriptor 和 ActionDescriptor 这两个描述对象,分别对应控制器和操作方法。本文将基于 MVC3 源码进一步分析 ParameterDescriptor,即用于描述 Action 方法参数的对象,并详细介绍其工作原理。 ... [详细]
author-avatar
香樟树1016
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有