美文网首页
JavaScript 作用域

JavaScript 作用域

作者: 樱木夜访流川枫 | 来源:发表于2018-08-05 20:58 被阅读0次

概览

背景知识:JavaScript内存管理、JavaScript作用域。

内容

1 闭包定义

闭包:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行

function foo() {
  var a = 2;

  function bar() {
     console.log(a);
  }

  return bar;
}

var fn = foo();  // parent函数不会被垃圾回收
fn()  // 2

细节:1 bar函数定义在foo函数内部,bar函数本身被当作一个值类型进行传递,并且它在定义的词法作用域之外执行。
2 foo()执行完后不会被垃圾回收,bar()将一直持有对该作用域的引用,而这个引用就是闭包

以下依然是闭包:

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

   bar(baz);
}

function bar(fn) {
  fn();  // 闭包
}

总结:无论通过何种手段将内部函数传递到所在的词法作用域外,它都会持有对原始定义词法作用域的引用,无论在何处执行这个函数都会使用到闭包

闭包 定时器:

function wait(message) {
   setTimeout(function timer() {
    console.log(message);
   }, 1000);
}

wait();

分析:wait()执行1000ms后,它不会被垃圾回收,timer()依然wait()作用域的闭包。

闭包 事件监听器:

function addEvent(selector) {
  $('selector').click(function cb() {
    // todo
  })
}

addEvent(btn);

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

循环与闭包

块级作用域与闭包的结合功能强大。

内容

思考:

for (var i = 0; i < 5; i++) {
  setTimeout(function Timer() {
    console.log(i);
  });
}

分析:尽管循环中的函数是在各个迭代中分别定义,但是它们都被封闭在共享的全局作用域中,实际上只有一个i

1 利用IIFE

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

2 利用let、const:

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

3 模块

模块也是一种利用闭包的代码模式。

模块模式的两个必要条件

1 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)。
2 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问和修改私有的状态。

一个具有函数属性的对象本身并不是真正的模块。
一个从函数调用返回的,只有数据属性而没有闭包函数的对象并不是真正的模块。

相关文章

网友评论

      本文标题:JavaScript 作用域

      本文链接:https://www.haomeiwen.com/subject/sdkivftx.html