博客列表 >第十章深入理解闭包

第十章深入理解闭包

刘静的博客
刘静的博客原创
2020年08月29日 22:11:41634浏览

第十章深入理解闭包

理解闭包

作用域:全局作用域和函数作用域

闭包就是fn2,既能够读取其它函数内部变量的函数

[^定义在一个函数内部的函数]:

闭包最大的特点:就是它可以记住诞生的环境,比如fn2记住了它诞生的环境是fn1,所以在fn2中可以得到fn1中的内部变量

[^本质上:闭包就是函数内部和函数外部链接的一座桥梁。]:

  1. var a = 123;
  2. function fn1(){
  3. console.log(a);
  4. var b = 234;
  5. function fn2(){
  6. console.log(b);
  7. }
  8. return fn2;
  9. }
  10. var result = fn1();
  11. result();//b is not defined

闭包的用途

1.通过闭包制作计数器

[^作用:读取函数内部的变量,这些变量始终在内存中,使用闭包小心内存的泄露]:

  1. function a(){
  2. var start = 0;
  3. function b(){
  4. return start++;
  5. }
  6. return b;
  7. }
  8. var inc = a();
  9. console.log(inc());
  10. console.log(inc());
  11. console.log(inc());
  12. // 释放当前的变量
  13. inc = null;

2.闭包能够封装对象的私有属性和方法

  1. function Person(name){
  2. //私有的属性
  3. var age;
  4. //私有的方法
  5. function setAge(n){
  6. age = n;
  7. }
  8. function getAge(){
  9. return age;
  10. }
  11. return {
  12. name:name,
  13. setAge:setAge,
  14. getAge:getAge
  15. }
  16. }
  17. var p1 = Person('mjj');
  18. p1.setAge(18);
  19. console.log(p1.getAge());
  20. p1 = null;

闭包的注意点

1.使用闭包使得函数中的变量始终在内存中,内存消耗很大,所以不能滥用闭包.否则会造成页面的性能问题.在ie中可能导致内存泄露.

2.变量用完不会被销毁,函数用完会销毁,私有属性会被存储起来

3.每个父函数调用完成,都会形成新的闭包,父函数中的变量始终会在内存中,相当于缓存,小心内存的消耗问题

4.闭包需要三个条件:1.函数嵌套 ;2.访问所在的作用域 ;3.在所在作用域外被调用

立即执行函数(IIFE)

IIFE: ()是表达式 跟在函数后面 表示调用函数 fn()

立即执行函数:定义函数之后,立即调用该函数,这种函数叫做立即执行函数

[^注意: 如果function出现在行首 一律解释成函数声明语句]:

简称:自执行函数

  1. //1.常用的两种写法
  2. (function(){
  3. })()
  4. !(function(){
  5. }());
  6. //2.通常情况下,写自执行函数的时候
  7. !(function(){})();

自执行函数的应用

计数器

  1. // 1.全局变量,会污染这个全局的变量
  2. var a = 0;
  3. function add(){
  4. return ++a;
  5. }
  6. console.log(add());
  7. console.log(add());
  8. console.log(add());
  9. console.log(add());
  10. console.log(add());
  11. // 2.自定义属性 有些代码可能会无意修改add.count
  12. function add(){
  13. return ++add.count;
  14. }
  15. add.count = 0;
  16. console.log(add());
  17. console.log(add());
  18. console.log(add());
  19. console.log(add());
  20. // 立即执行函数也叫闭包,可以封装私有的属性,同时可以减少对全局变量的污染
  21. var add = (function (){
  22. // 私有属性
  23. var count = 0;
  24. return function (){
  25. return ++count;
  26. }
  27. })();
  28. var add2 = (function(){
  29. var count = 1;
  30. })();
  31. console.log(add);
  32. console.log(add());
  33. console.log(add());
  34. console.log(add());
  35. console.log(add());
  36. console.log(add());
  37. console.log(add());

对循环和闭包的理解错误

  1. function foo(){
  2. var arr = [];
  3. for(var i = 0; i < 10; i++){
  4. arr[i] = function(){
  5. return i;
  6. }
  7. }
  8. return arr;
  9. }
  10. var bar = foo();
  11. console.log(bar[0]());//10
  12. console.log(bar[5]());//10

解决方案:

1.使用闭包解决循环中变量的问题,相当于把变量保存在内存中,,每次执行的时候从内存中获取

  1. function foo(){
  2. var arr = [];
  3. for(var i = 0; i < 10; i++){
  4. arr[i] = (function(n){//第一种写法
  5. return function(){
  6. return n;
  7. }
  8. })(i);
  9. (function(n){//第二种写法
  10. arr[n] = function(){
  11. return n;
  12. }
  13. })(i);
  14. }
  15. return arr;
  16. }
  17. var bar = foo();
  18. console.log(bar);
  19. console.log(bar[0]());
  20. console.log(bar[1]());
  21. console.log(bar[2]());
  22. console.log(bar[3]());
  23. console.log(bar[4]());

2.解决方法 let块级作用域 ES6

  1. function foo(){
  2. var arr = [];
  3. for(let i = 0; i < 10; i++){
  4. arr[i] = function(){
  5. return i;
  6. }
  7. }
  8. return arr;
  9. }
  10. var bar = foo();
  11. console.log(bar[0]());
  12. console.log(bar[1]());
  13. console.log(bar[2]());
  14. console.log(bar[3]());

[^MJJ名言:在编程,如果实际和预期结果不符,就按照代码执行顺序一步一步地把执行环境图示画出来,会发现很多时候我们都是在想当然]:

闭包的10种表示形式

1.返回值 最常见的一种形式

  1. var fn = function(){
  2. var name = 'mjj';
  3. return function(){
  4. return name;
  5. }
  6. }
  7. var fnc = fn();
  8. console.log(fnc());

2.函数赋值 一种变形的形式是将内部函数赋值给一个外部的变量

  1. var fn2;
  2. var fn = function(){
  3. var name = 'mjj';
  4. var a = function (){
  5. return name;
  6. }
  7. fn2 = a;
  8. }
  9. fn();
  10. console.log(fn2());

3.函数参数

  1. function fn2(f) {
  2. console.log(f());
  3. }
  4. function fn() {
  5. var name = 'mjj';
  6. var a = function() {
  7. return name;
  8. }
  9. fn2(a);
  10. }
  11. fn();

4.IIFE

  1. function fn3(f) {
  2. console.log(f());
  3. }
  4. (function() {
  5. var name = 'alex';
  6. var a = function() {
  7. return name;
  8. }
  9. fn3(a);
  10. })();

5.循环赋值

  1. function foo() {
  2. var arr = [];
  3. for (var i = 0; i < 10; i++) {
  4. (function(i){
  5. arr[i] = function(){
  6. return i;
  7. }
  8. })(i);
  9. arr[i] = (function(n) {
  10. return function() {
  11. return n;
  12. }
  13. })(i);
  14. }
  15. return arr;
  16. }
  17. var bar = foo();
  18. console.log(bar[3]());

6.getter和setter函数来将要操作得变量保存在函数内部,防止暴露在外部

  1. var getValue, setValue;
  2. (function() {
  3. var num = 0;
  4. getValue = function() {
  5. return num;
  6. }
  7. setValue = function(v) {
  8. if (typeof v === 'number') {
  9. num = v;
  10. }
  11. }
  12. })();
  13. console.log(getValue());
  14. setValue(10);
  15. console.log(getValue());

7.迭代器(计数器)

  1. var add = function() {
  2. var num = 0;
  3. return function() {
  4. return ++num;
  5. }
  6. }();
  7. console.log(add());
  8. console.log(add());
  9. //['alex','mjj','阿黄']
  10. function setUp(arr) {
  11. var i = 0;
  12. return function() {
  13. return arr[i++];
  14. }
  15. }
  16. var next = setUp(['alex', 'mjj', '阿黄']);
  17. console.log(next());
  18. console.log(next());
  19. console.log(next());

8.区分首次

  1. var firstLoad = (function() {
  2. var list = [];
  3. return function(id) {
  4. if (list.indexOf(id) >= 0) {
  5. //list已经有id
  6. return false;
  7. } else {
  8. // 首次调用
  9. list.push(id);
  10. return true
  11. }
  12. }
  13. })();
  14. console.log(firstLoad(10));
  15. console.log(firstLoad(10));
  16. console.log(firstLoad(30));
  17. console.log(firstLoad(40));
  18. console.log(firstLoad(40));

9.缓存机制

9.1 未加入缓存

  1. function mult(){
  2. // arguments
  3. var sum = 0;
  4. for(var i = 0; i < arguments.length; i++){
  5. sum = sum + arguments[i];
  6. }
  7. return sum;
  8. }
  9. console.log(mult(1,2,3,1,1,2,3)); //求和
  10. console.log(mult(1,2,3,1,1,2,3)); //求和
  11. console.log(mult(1,2,3,1,1,2,3,4)); //求和

9.2 有缓存机制

  1. // 模拟一个对象的key 看该对象中是否有相同的key,如果有则直接获取value返回
  2. {
  3. key:value
  4. 1,2,3,1,1,2,3: 18,
  5. 1,2,3,1,1,2,3,4: 22
  6. }
  7. var mult = function(){
  8. // 缓存对象
  9. var cache = {};
  10. var calculate = function (){
  11. var sum = 0;
  12. for(var i = 0; i < arguments.length; i++){
  13. sum = sum + i;
  14. }
  15. return sum;
  16. }
  17. return function (){
  18. // 对cache对象进行操作
  19. // 1,2,3,1,1,2,3
  20. var args = Array.prototype.join.call(arguments,',');
  21. if(args in cache){
  22. return cache[args];
  23. }
  24. console.log(cache);
  25. return cache[args] = calculate.apply(null,arguments);
  26. }
  27. }();
  28. console.log(mult(1,2,3,1,1,2,3));
  29. console.log(mult(1,2,3,1,1,2,3));
  30. console.log(mult(1,2,3,1,1,2,3,10,20));
  31. console.log(mult(1,2,3,1,1,2,3,10,20,1));

10.img图片对象上报

new Image() 进行数据上报

低版本浏览器在进行数据上报会丢失30%左右的数据

  1. // 使用闭包来做图片上传
  2. var report = function (src){
  3. var imgs = [];
  4. return function (src){
  5. var img = new Image();
  6. imgs.push(img);
  7. img.src = src;
  8. }
  9. }();
  10. report('http://xxx.com/getUserInfo');
声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议