【你不知道的javaScript 上卷 笔记4】javaScript 中闭包的一些运用

什么是闭包

闭包是javaScript语言的一种特性,在 javaScript 中以函数作为承接单元。当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

function foo() {
  var a = 2;
    function bar() { 
    console.log( a ); // 2
    }
    bar(); 
}
foo();

bar() 产生了闭包,但是由于它内嵌在foo函数里面执行,bar中对a的引用遵循了词法作用域的查找规则,其实也是闭包查询规则的一部分。

function foo() {
  var a = 2;
    function bar() { 
    console.log( a ); // 2
    }
    return bar; 
}
(foo())();

稍微修改一下上面的代码,将bar return 出来,在foo函数外执行,bar依旧可以获取到a,而这时候bar它记住了foo的内部词法作用域,并且可以按照词法作用域查询规则找到a。由此可想bar能够引用foo的内部作用域,说明foo的内部作用域并没有被回收,而正因为闭包的存在,引擎没有回收foo的内部作用域的内存空间。

回调函数

function foo() { 
  var a = 2;
  function baz() { 
    console.log( a ); // 2
  }
    bar( baz ); 
}
function bar(fn) {
    fn(); // fn是一个闭包!
}

对函数类型的值进行传递,会形成一个闭包,所以常见的回调函数采用了闭包,如:

function wait(message) {
    setTimeout( function timer() { console.log( message );
}, 1000 ); }
wait( "Hello, closure!" );

setTimeout中的内部函数timer是一个具有 wait的作用域的闭包。

在定时器、事件监听器、 Ajax 请求、跨窗口通信、Web Workers 或者任何其他的异步(或者同步)任务中,只要使 用了回调函数,实际上就是在使用闭包。

模块

function CoolModule() {
  var something = "cool";
  var another = [1, 2, 3];
  function doSomething() { 
    console.log( something );
  }
  function doAnother() {
      console.log( another.join( " ! " ) );
  }
  return {
    doSomething: doSomething, 
    doAnother: doAnother
  }; 
}
var foo = CoolModule(); 
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

CoolModule()通过return 出一个对象,包含其两个内部函数,而两个内部函数是包含着CoolModule的内部作用域的闭包。

模块的特点:

  • 为创建内部作用域而调用了一个包装函数;

  • 装函数的返回 值必须至少包括一个对内部函数的引用,这样就会创建涵盖整个包装函数内部作用域的 闭包。

单例模块

var foo = (
  function CoolModule() { 
    var something = "cool";
        var another = [1, 2, 3];
    function doSomething() { 
      console.log( something );
    }
    function doAnother() {
      console.log( another.join( " ! " ) );
    }
    return {    
      doSomething: doSomething, 
      doAnother: doAnother
    }; 
  })();
foo.doSomething(); // cool 
foo.doAnother(); // 1 ! 2 ! 3

通过内部函数修改内部私有变量的值

var foo = (function CoolModule(id) { 
  function change() {
    // 修改公共 API
    publicAPI.identify = identify2; }
  function identify1() { 
    console.log( id );
  }
  function identify2() {
    console.log( id.toUpperCase() );
  }
  var publicAPI = {
        change: change,
      identify: identify1
   };
    return publicAPI; 
})( "foo module" );
  foo.identify(); // foo module foo.change();
  foo.identify(); // FOO MODULE

模块依赖加载器

var MyModules = (
  function Manager() {
    var modules = {};
    function define(name, deps, impl) {
    for (var i=0; i<deps.length; i++) {
      deps[i] = modules[deps[i]];
    }
    modules[name] = impl.apply( impl, deps ); 
  }
  function get(name) { 
    return modules[name];
  }
  return {
    define: define,
    get: get 
  };
})();
MyModules.define( "bar", [], function() { 
    function hello(who) {
      return "Let me introduce: " + who;
    }
    return {
        hello: hello
    }; 
} );
MyModules.define( "foo", ["bar"], function(bar) {
  var hungry = "hippo";
  function awesome() {
    console.log( bar.hello( hungry ).toUpperCase() );
  }
  return {
    awesome: awesome
  }; 
} );
var bar = MyModules.get( "bar" ); 
var foo = MyModules.get( "foo" );
console.log(
bar.hello( "hippo" )
); // Let me introduce: hippo 
foo.awesome(); // LET ME INTRODUCE: HIPPO

ES6 模块机制

// bar.js
function hello(who) {
    return "Let me introduce: " + who;
}
export hello; 

//foo.js
// 仅从 "bar" 模块导入 hello() 
import hello from "bar";
var hungry = "hippo";
function awesome() { 
  console.log(
    hello( hungry ).toUpperCase() );
}
export awesome;

//baz.js
module foo from "foo"; 
module bar from "bar";
console.log(
    bar.hello( "rhino" )
); // Let me introduce: rhino 
foo.awesome(); // LET ME INTRODUCE: HIPPO

经典的循环

for (var i=1; i<=5; i++) { 
  setTimeout( function timer() {
        console.log( i ); }, i*1000 );
}

以上会依次输出 6,6,6,6,6,6,因为 setTimeout中的闭包会在循环结束后依次执行,而每一个闭包所引用的作用域都是一致的,i变量在全局作用域中。如果想要输出我们想要的结果可以加入更多的闭包如:

for (var i=1; i<=5; i++) { 
  (function(j) {
        setTimeout( 
        function timer() { console.log( j );
      }, i*1000 );
   })(i);
}

i作为变量传入闭包函数中,每个闭包函数中通过隐试的创建了自己的作用域变量j

更加简单的方法使用let

for (let i=1; i<=5; i++) { 
  setTimeout( function timer() {
        console.log( i ); }, i*1000 );
}

let 可以创建块级作用域,这样每一个循环都是一个作用域块,每个作用域互不干扰。

总结

可以说闭包在javaScript运用广泛,一个包含其封装函数内部作用域引用的函数就是一个闭包。

猜你喜欢

转载自www.cnblogs.com/seny-33/p/12194451.html