前言:
这是一篇关于闭包函数的总结和笔记javascript
但愿对你们有点帮助 写的很差的地方,也请你们多多指教
一: js中的命名函数,匿名函数,自调用函数和回调函数
1.命名函数: 函数若是有名字,就是命名函数java
function f1( ) { console.log("我就是命名函数"); }
2.匿名函数: 函数若是没有名字,就是匿名函数面试
(function () { console.log("我就是匿名函数"); })
3.自调用函数: 本身调用本身缓存
var f2 = function () { console.log("你好"); }; f2();
fn里面存储的是函数体代码,经过fn()的方式调用. 匿名函数自调用: 在匿名函数后面加括号,里面外面均可以; 也能够在函数前用 ! + -;
缘由:js只能识别表达式(由运算符组成的式子) 与 语句( 程序流程控制 );让匿名函数能够调用原理:将匿名函数变成表达式闭包
4.回调函数: 回调函数就是一个参数将这个函数做为参数传到另外一个函数里面当那个函数执行完以后,再执行传进去的这个函数。模块化
function f3(fn) { fn(); //函数调用 --- 说明fn这个变量中存储的是一个函数 }; function f4() { console.log("函数能够做为参数使用") } f3(f4); // 调用f3,将f4做为参数使用
二: 函数的语法
1.函数声明函数
function f5() { console.log("我是函数声明"); }; f5()
2.表达式声明this
var f6 = function () { console.log("我是表达式声明"); }; f6();
三: 匿名函数的四种常见场景
1.注册事件code
document.querySelector("#btn").onclick = function () { console.log("我是匿名函数调用的一种场景"); };
2.定时器对象
setInterval(function () { console.log("我是定时器中的匿名函数"); },1000);
3.变量存储
var f7 = function () { console.log("我是变量存储中的函数"); };
4.对象方法
var obj = { name: "拉克丝", say: function () { console.log(this.name); } }; obj.say();
四: 闭包函数
闭包有三个特性: 函数嵌套函数; 函数内部能够引用外部的参数和变量; 参数和变量不会被垃圾回收机制回收
闭包的好处是:1.但愿一个变量长期驻扎在内存中 2.避免全局变量的污染 3.私有成员的存在
01 全局变量的累加
var a = 1; function f7( ){ a++; alert(a); } f7(); // 2 f7(); // 3 f7(); // 4
02 局部变量
function f8( ){ var a = 1; a++; alert(a); } f8(); // 2 f8(); // 2 f8(); // 2
03 局部变量的累加
function outer( ){ var x = 10; return function ( ) { //函数嵌套函数 x++; alert(x); } } //外部函数只调用一次,获得的同一个变量 var y = outer(); //外部函数赋值给变量y 调用一次外部函数,获得一个闭包函数 y(); // 11 //调用闭包函数,返回内部变量 y(); // 12 y(); // 13 outer()(); // 11 outer()(); // 11 outer()(); // 11
04 经典例子
function fn() { var num = 3; return function () { var n = 0; console.log(++n); console.log(++num); } } var fn1 = fn(); fn1() // 1 4 fn1() // 1 5 fn1() // 1 6 fn()() // 1 4 fn()() // 1 4 fn()() // 1 4
05 模块化代码,减小全局变量的污染
var abc = (function ( ) { //abc 为外部匿名函数的返回值 var a = 1; return function ( ) { a++; alert(a); } }()); abc(); // 2 // 调用一次abc函数,其实就是调用里面内部函数的返回值 abc(); // 3 abc() // 4
06 私有成员的存在
var aaa = (function ( ) { var a = 1; function bbb( ) { a++; alert(a); } function ccc( ) { a++; alert(a); } return { b: bbb, c: ccc } })(); aaa.b(); // 2 aaa.c(); // 3 aaa.b(); // 4 aaa.c(); // 5
07 使用匿名函数实现累加
使用匿名函数实现局部变量驻留在内存中,从而实现累加
function aaa( ) { var a = 10; return function ( ) { //匿名函数 a++; return a; }; } var y = aaa(); alert(y()); // 11 alert(y()); // 12 alert(y()); // 13
08 闭包做为参数传递
var num = 15; var fn1 = function (x) { if(x > num){ console.log( x ); } } void function (fn2) { var num = 100; fn2(30); }(fn1); // 30 // void 至关于匿名函数的自调用 // fn2是当作参数传进来的 不是在void里面声明的 是在全局做用域里声明的 因此拿的是num=15 因此打印出来30
对上题有疑惑的,能够看下面这个
var num = 15; var fn1 = function (x) { if(x > num){ console.log( x ); // 30 } } void function (fn2) { var num = 100; fn2(30); function fn3(x) { console.log( num ); // 100 if(x < num){ console.log( x ); //30 } } fn3(30) }(fn1);
闭包的应用场景
一.点击li标签,出现对应的索引
<ul id="ul1"> <li>你好我是循环闭包中的第0个</li> <li>你好我是循环闭包中的第1个</li> <li>你好我是循环闭包中的第2个</li> <li>你好我是循环闭包中的第3个</li> <li>你好我是循环闭包中的第4个</li> </ul>
1.js中添加索引的作法
var ul1 = document.getElementById("ul1"); for(var i = 0; i < ul1.children.length; i++){ ul1.children[i].setAttribute("index",i); ul1.children[i].onclick = function () { console.log(this.getAttribute("index")) } };
2.jQuery中index作法
$(function () { var index = null; $("li").on("click",function () { index = $(this).index(); console.log(index); }); })
3.闭包的处理
var ul1 = document.getElementById("ul1"); //错误的作法 function showLiNum(ul1) { for(var i = 0; i < ul1.children.length; i++){ ul1.children[i].onclick = function () { console.log(i); } } } showLiNum(ul1) //当点击以前,循环已结束;因此i的值为别.children.length;
正解:利用一个闭包,创建一个匿名函数;将每一个i存在内存中.onclick函数用的时候提取出外部匿名函数的值
function showLiNum(ul1) { for(var i = 0; i < ul1.children.length; i++){ (function (i) { ul1.children[i].onclick = function () { console.log(i); } }(i)); } } showLiNum(ul1)
也能够这样
function showLiNum(ul1) { for(var i = 0; i < ul1.children.length; i++){ ul1.children[i].onclick = (function (i) { return function () { console.log(i); } }(i)); } } showLiNum(ul1);
二.定时器
开启定时器,分别打印1,2,3,4,5
错误的写法:
for(var i = 1; i <= 5; i++){ setTimeout(function () { console.log(i); },1000) }
正确写法
for(var i = 1; i <= 5; i++){ (function (i) { setTimeout(function () { console.log(i); },i*1000); }(i)) } //至关于同时启动3个定时器,i*1000是为5个定时器分别设置了不一样的时间,同时启动, //可是执行时间不一样,每一个定时器间隔都是1000毫秒,实现了每隔1000毫秒就执行一次打印的效果。
经典面试题
最后上一道闭包中的经典面试题,若是你能弄懂这个,那说明闭包弄懂的差很少啦
var name = "The Window"; var obj = { name : "The object", getNameFunc : function(){ return function(){ return this.name; } } } alert( obj. getNameFunc()() ) var name = "The Window"; var obj = { name : "The object", getNameFunc : function(){ var that = this; return function(){ return that.name; } } } alert( obj. getNameFunc()() )
javascript是动态(或者动态类型)语言,this关键字在执行的时候才能肯定是谁。因此this永远指向调用者,即对'调用对象'的引用; "谁调用,指向谁"。第一部分执行代码object.getNameFunc()以后,返回了一个新的匿名函数,此时在全局做用域调用匿名函数,它不在是object的属性或者方法,此时调用者是window,所以输出是 The Window。第二部分,当执行函数object.getNameFunc()后返回的是:function( ){return that.name;}此时的that=this。而this指向object,因此that指向object。他是对object的引用,因此输出The Object。总结:关于js中的this,记住谁调用,this就指向谁;要访问闭包的this,要定义个变量缓存下来。通常var that(_this) = this。