【JavaScript】深入了解JavaScript中的匿名函数、立即执行函数和闭包 您所在的位置:网站首页 js立即执行函数和闭包的区别 【JavaScript】深入了解JavaScript中的匿名函数、立即执行函数和闭包

【JavaScript】深入了解JavaScript中的匿名函数、立即执行函数和闭包

2024-07-17 14:58| 来源: 网络整理| 查看: 265

深入了解JavaScript中的匿名函数、立即执行函数和闭包 JS中函数两种命名方式:声明式和表达式匿名函数匿名函数作用:1.用作函数表达式2.作为返回值3.用作定义对象方法4.作为回调函数5.用于立即执行函数6.用于DOM元素注册事件 立即执行函数立即执行函数形式1. 将匿名函数包裹在一个括号运算符中,后面再跟一个括号。2. 匿名函数后面跟一个括号,再将整个包裹在一个括号运算符中。 立即函数的作用闭包经典案例分析 闭包闭包特点闭包的创建闭包形式1.直接以匿名函数的形式作为外部函数的返回值(普遍用法)2.在外部函数内定义一个内部函数,并返回内部函数名3.在外部函数内定义一个立即执行函数 闭包的应用场景示例:

JS中函数两种命名方式:声明式和表达式 函数声明式 function fn(){ console.log("函数声明式"); } 函数表达式 var fn = function(){ console.log("函数表达式"); }

(function(){}()),(function(){})()这两种是js中立即执行函数的写法,函数表达式后加上()可以被直接调用,但是把整个声明式函数用()包起来的话,则会被编译器认为是函数表达式,从而可以用()来直接调用,如(function fn(){/*...*/})()。 但是如果这个括号加在声明式函数后面,如function fn(){/*...*/}(),则会报错,很多博客说这种写法()会被省略,但实际是会出错,因为不符合js的语法,所以想要通过浏览器的语法检查,就必须加点符号,比如()、+、!等,详细见下方。

匿名函数

JavaScript并不是面向对象的,所以不支持封装,但是可以 通过匿名函数实现封装。 匿名函数:没有函数名的函数,不能单独定义与使用,否则会js语法错误,至少用()包裹起来。

function(){ console.log("匿名函数"); } 匿名函数作用: 1.用作函数表达式 var sum = function(num1, num2){ return num1 + num2; }; console.log(sum(2, 3)); 2.作为返回值 function sum(num1, num2){ return function(){ return num1 + num2; } } console.log(sum(2, 3)); //function console.log(sum(2, 3)()); //5 3.用作定义对象方法 var obj = { name: 'umaru', age: 17, fu: function() { console.log(this.name + ' ' + this.age); } }; obj.fu(); 4.作为回调函数 setTimeout(function() { console.log('匿名函数作为回调函数'); }, 1000); 5.用于立即执行函数

在内容形成局部变量和局部函数,防止全局污染。

(function() { console.log('立即执行函数是基于匿名函数创建的'); }()); 6.用于DOM元素注册事件 var btn = document.querySelector("#btn"); //给按钮注册点击事件 btn.onclick = function(){ console.log('Click event'); } 立即执行函数

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

立即执行函数形式 1. 将匿名函数包裹在一个括号运算符中,后面再跟一个括号。 (function () { console.log('立即执行函数'); })(); // !!!特别说明:若此立即执行函数后面立马又跟着一个立即执行函数,一定要在结尾加分号,否则后面的立即执行函数会报错! // 上一个立即执行函数不加分号,下行代码将报错:TypeError: (intermediate value)(...) is not a function (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 fn = function (a, b, c) { console.log(a + b + c); // 6 }(1, 2, 3); 2. 匿名函数后面跟一个括号,再将整个包裹在一个括号运算符中。 (function () { console.log('立即执行函数'); }()); (function (a, b, c) { // 形参 console.log(a + b + c); // 6 }(1, 2, 3)); // 实参 立即函数的作用

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

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

匿名函数特点:

页面加载时立即制行获取到返回值执行完成之后立即释放 闭包经典案例分析 这是第一条 这是第二条 这是第三条 var liList=document.getElementsByTagName('li'); for(var i=0;i console.log(i); } };

       很多人觉得这样的执行效果是点击第一个li,则会输出1,点击第二个li,则会输出二,以此类推。但是真正的执行效果是,不管点击第几个li,都会输出3。   因为 i 是贯穿整个作用域的,而不是给每个 li 分配了一个 i,用户触发的onclick事件之前,for循环已经执行结束了,而for循环执行完的时候i=3。   但是如果我们用了__立即执行函数给每个 li 创造一个独立作用域,__就可以改写为下面的这样,这样就能实现点击第几条就能输出几的功能。

var liList=document.getElementsByTagName('li'); for(var i=0;i liList[ii].onclick=function(){ console.log(ii); } })(i) };

其实ES6语法中的let也可以实现上述的功能。

var liList=document.getElementsByTagName('li'); for(let i=0;i console.log(i); } } 闭包

闭包函数:声明在一个函数中的函数,叫做闭包函数

闭包:内部函数总是可以访问其所在外部函数中的声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。

创建闭包的常见方式:就是在一个函数内部创建另一个函数。闭包可以有函数名,也可以是匿名函数(没有函数名);

闭包特点 让外部访问函数内部变量成为可能局部变量会常驻在内存中可以避免使用全局变量,防止全局变量污染会造成内存泄漏(有一块内存空间被长期占用,而不被释放) 闭包的创建

闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,__每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址。__但凡是当前活动对象中有被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。

闭包内存泄漏为: key = value,key 被删除了 value 常驻内存中;

局部变量闭包升级版(中间引用的变量)=> 自由变量;

闭包形式 1.直接以匿名函数的形式作为外部函数的返回值(普遍用法) function outer() { var n = 1; return function() { n++; // 访问 outer 函数作用域中的变量 n,形成闭包 console.log(n); } } outer()(); 2.在外部函数内定义一个内部函数,并返回内部函数名 function outer() { var n = 1; function inner() { n++; // 访问 outer 函数作用域中的变量 n,形成闭包 console.log(n); } return inner; } outer()(); 3.在外部函数内定义一个立即执行函数 function outer() { var n = 1; (function() { n++; // 访问 outer 函数作用域中的变量 n,形成闭包 console.log(n); })(); } outer(); 闭包的应用场景 保护函数局部变量的安全在内存中维持一个变量通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)

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

结论: 闭包找到的是同一地址中父级函数中对应变量最终的值。

示例: 在返回的闭包后面紧跟一个 () ,立即执行 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) 请继续看以下代码,同时使用了1和2的两种方式 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; // 解除对闭包的引用,以便释放内存 function foo() { var a = 2; return function fun1() { console.log(a) } } var fun2 = foo() fun2() // 2

在上面的例子中,fun1能够访问foo的内部作用域,我们把fun1作为一个值返回。在foo()执行后,把foo()的返回值 fun1 赋值给fun2并调用fun2。打印出了结果2. 此时,我们可以说fun1记住并访问了所在的词法作用域 或者说 fun2访问了另一个函数作用域中的变量(fun2在全局作用域中声明,访问了foo的内部作用域) 由于引擎有自动的垃圾回收机制,在foo()执行后(不再使用),通常foo的整个内部作用域会被销毁,对内存进行回收。而闭包的神奇之处正是可以阻止这件事情的发生,因为fun1依然持有对该作用域的引用,这个引用就叫做闭包。 无论使用何种方式对函数类型的值进行传递,当函数在别处调用时,都可以看到闭包。

function outerFn(){ var i = 0; function innerFn(){ i++; console.log(i); } return innerFn; } var inner = outerFn(); //每次外部函数执行的时候,都会开辟一块内存空间,外部函数的地址不同,都会重新创建一个新的地址 inner(); inner(); inner(); var inner2 = outerFn(); inner2(); inner2(); inner2(); //1 2 3 1 2 3 var i = 0; function outerFn(){ function innnerFn(){ i++; console.log(i); } return innnerFn; } var inner1 = outerFn(); var inner2 = outerFn(); inner1(); inner2(); inner1(); inner2(); //1 2 3 4 function fn(){ var a = 3; return function(){ return ++a; } } alert(fn()()); //4 alert(fn()()); //4 function outerFn(){ var i = 0; function innnerFn(){ i++; console.log(i); } return innnerFn; } var inner1 = outerFn(); var inner2 = outerFn(); inner1(); inner2(); inner1(); inner2(); //1 1 2 2 (function() { var m = 0; function getM() { return m; } function seta(val) { m = val; } window.g = getM; window.f = seta; })(); f(100); console.info(g()); //100 闭包找到的是同一地址中父级函数中对应变量最终的值 function a() { var i = 0; function b() { alert(++i); } return b; } var c = a(); c(); //1 c(); //2 function f() { var count = 0; return function() { count++; console.info(count); } } var t1 = f(); t1(); //1 t1(); //2 t1(); //3 ES6柯里化 var add = function(x) { var sum = 1; var tmp = function(x) { sum = sum + x; return tmp; } tmp.toString = function() { return sum; } return tmp; } alert(add(1)(2)(3)); //6 var lis = document.getElementsByTagName("li"); for(var i=0;i lis[i].onclick = function(){ console.log(i); }; })(i); //事件处理函数中闭包的写法 } function m1(){ var x = 1; return function(){ console.log(++x); } } m1()(); //2 m1()(); //2 m1()(); //2 var m2 = m1(); m2(); //2 m2(); //3 m2(); //4 var fn=(function(){ var i=10; function fn(){ console.log(++i); } return fn; })() fn(); //11 fn(); //12 var fn=(function(){ var i=10; function fn(){ console.log(++i); } return fn; })() fn(); //11 fn(); //12 function love1(){ var num = 223; var me1 = function() { console.log(num); } num++; return me1; } var loveme1 = love1(); loveme1(); //输出224 function fun(n,o) { console.log(o); return { fun:function(m) { return fun(m,n); } }; } var a = fun(0); //undefined a.fun(1); //0 a.fun(2); //0 a.fun(3); //0 var b = fun(0).fun(1).fun(2).fun(3); //undefined 0 1 2 var c = fun(0).fun(1); c.fun(2); c.fun(3); //undefined 0 1 1 function fn(){ var arr = []; for(var i = 0;i return i; } } return arr; } var list = fn(); for(var i = 0,len = list.length;i var arr = []; for(var i = 0;i return function (){ return i; }; })(i); } return arr; } var list = fn(); for(var i = 0,len = list.length;i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有