闭包知识点笔记,包含十大使用出场景
前言
最近在学习javascript的一个重要知识点,闭包,下文从几个点解释了个人对闭包的理解,使用闭包的注意点,以及闭包的十大使用场景。整理成笔记,方便自己以后复习。同时也欢迎大家指正不足~
-
变量作用域:由于变量作用域得关系,函数外部不能访问在函数中定义的变量;但如果在全局定义函数,会被任意修改;此时就考虑到了使用闭包,来访问函数内部的变量,
-
在一个函数fn1中定义一个另一个函数fn2,并返回该函数,此时fn2就是闭包;此时就能访问函数fn1中的变量
function fn1(){ var b = "我是变量b"; function fn2(){ console.log(b); } return fn2; } var result = fn1(); result(); -
闭包最大的特点是它可以记住诞生的环境,fn2记住了它的诞生环境是fn1,所以在函数fn2中可以访问fn1中声明的变量;本质上,闭包就是函数内部和函数外部链接的桥梁
-
闭包的用途
-
用作计数器
// 1.制作计数器 function count() { var start = 0; function fn2() { return start++; } return fn2; } var inc = count(); console.log(inc());//返回0 console.log(inc());//返回1;而不是返回0 //释放变量 inc = null;在闭包中使用的变量会一直存在在内存中,不会自动释放,所以在重新调用count函数的时候,会直接在内存中找到start变量,在原来的值上直接加1,所以会第二次调用会返回1,而不是0;因为函数不会被自动释放,为了避免内存泄漏,需要手动释放函数的引用变量,置为null
-
封装对象的私有属性和私有方法
//2.封装对象的私有属性和私有方法 function Person(name) { var age;//私有属性 //私有方法 function setAge(n) { age = n; } //私有方法 function getAge() { return age; } //返回一个对象 return{ name:name, setAge:setAge, getAge:getAge } } var p = Person("张三"); p.setAge(21); console.log(p.name,p.getAge())//返回张三 21
-
-
闭包的注意点
- 使用闭包使得函数中的变量始终存在在内存中,内存消耗很大,所以不能滥用闭包,否则造成页面的性能问题
- 闭包形成的三个条件:函数嵌套、访问所在的作用域、在所在 作用域外被调用
-
立即执行函数
两种声明语句
- (function(){})();
- (function(){}());
- 如果在自执行函数声明语句后没有加分号表示结束,可以在声明语句前加+ - !,作用和加分号是一样的,同样表示语句结束
-
立即执行函数的作用
// 立即执行函数也可以写闭包 var inc =(function count(){ var start = 0; return function fn2() { return start++; } })(); console.log(inc());//0 console.log(inc());//1 console.log(inc());//2 console.log(inc());//3 -
想要循环打印出0,1,2,一个错误的例子
function foo() { var arr=[]; for(var i=0;i<10;i++){ arr[i] = function(){ return i; } } return arr; } var bar = foo(); console.log(bar[1]())//按道理应该是打印出1,但是实际上打印出10问题在于,在执行数组赋值前,i已经循环完,最后的值是10,然后再赋值给数组,不是一个i存进数组里
-
解决方法
-
使用自执行函数形成闭包
function foo() { var arr=[]; for(var i=0;i<10;i++){ arr[i] = (function(n){ return function(){ return n; }; })(i) } return arr;
} var bar = foo(); console.log(bar1)//打印出1 console.log(bar2)//打印出2 console.log(bar9)//打印出9
- ```js function foo() { var arr=[]; for(var i=0;i<10;i++){ (function(n){ arr[n] = function(){ return n; }; })(i) } return arr; } var bar = foo(); console.log(bar[1]())//打印出1 console.log(bar[2]())//打印出2 console.log(bar[9]())//打印出9- 还有一种方法是使用let定义i,不过跟闭包没关系,就不讲了...
-
-
闭包的十种使用场景
-
返回值(最常见的一种形式)
function fn1(){ var b = "我是变量b"; function fn2(){ return b; } return fn2; } var result = fn1(); console.log(result()); -
函数赋值
var fun2; function fn1(){ var b = "我是变量b"; var a= function (){ return b; } fn2 = a; } fn1(); console.log(fn2()); -
函数参数
function fn2(f){ console.log(f()); } function fn(){ var b = "我是变量b"; var a = function(){ return b; } fn2(a); } fn();//打印出我是变量b -
IIFE(立即执行函数)
function fn2(f){ console.log(f()); } (function(){ var b = "我是变量b"; var a = function(){ return b; } fn2(a); })() //相当于执行fn()打印出我是变量b -
循环赋值
function foo() { var arr=[]; for(var i=0;i<10;i++){ arr[i] = (function(n){ return function(){ return n; }; })(i) } return arr; } var bar = foo(); console.log(bar[1]())//打印出1 console.log(bar[2]())//打印出2 console.log(bar[9]())//打印出9 -
getter和setter,将函数内部的变量保存在内部,防止被任意修改
var getValue,setValue; (function(){ var num = 0; getValue = function (){ return num; } setValue = function (n){ num = n; } })(); console.log(getValue())//打印出0; setValue(21); console.log(getValue())//打印出21; -
迭代器
function printName(arr){ var i = 0; return function(){ return arr[i++]; } } var name = printName(['张三','李四','王五']); console.log(name());//打印出张三 console.log(name());//打印出李四 -
区分首次
var isFirst = (function(){ var list = []; return function(num){ if(list.indexof(num) > -1){ return false; }else{ list.push(num); return false; } } })(); console.log(isFirst(21));//打印出true console.log(isFirst(21));//打印出false -
缓存机制
var mult = function(){ //缓存对象 var cache = {}; var calculate = function(){ var sum = 0; for(var i = 0;i < arr.length; i++){ sum = sum + i; } return sum; } return function(){ //对cache对象进行操作 var arrs = Array.prototype.join.call(arr,","); } if(arrs in cache){ return cache[arrs] } console.log(cache) return cache[arrs] = calculate.apply(null,arr); } console.log(mult(1,2,3,4));//打印出10 -
图片上传
var report = function(src){ var imgs = []; return function (src){ var img = new Image(); imgs.push(img); img.src = src; } }(); report(图片的路径);
-
-
总结
- 注意闭包的形成条件
- 闭包会使得父级函数中的变量一直保存在内存中
- 注意闭包造成的内存泄露
- 十大使用场景
- 学习学习,复习复习!!!