美文网首页
JS之闭包与IIFE

JS之闭包与IIFE

作者: 一只记录成长的小鱼 | 来源:发表于2018-10-14 17:04 被阅读0次

本篇文章主要讨论了:

  1. JavaScript引擎
  2. 全局对象
  3. 闭包
  4. 循环 + 闭包
  5. IIFE + 闭包

1.JavaScript引擎

  • 在执行代码前JS引擎就已经读完了全部代码,如果找到语法错误会直接提示有syntax error
  • 所有函数声明会存储在内存中
  • 变量初始化不会被运行,但是在词法作用域(lexically-scoped)内声明的变量(用var声明的)名字(赋值不会)会被存储于内存中

2.全局对象

  • 所有的变量和函数实际上都是全局对象的属性和方法
  • 浏览器的全局对象是’window’对象
  • node.js的全局对象是'global'对象

3.闭包

  • 函数能引用到母函数(parent function)变量的行为,即能访问到母函数的作用域,即使是在母函数结束运行后。
function makeHelloFunction() {
  const message = 'Hello!';

  function sayHello() {
    console.log(message);
  } return sayHello;
}
const sayHello = makeHelloFunction();
console.log('typeof message,', typeof message);
//console.log(message); //会出现ReferenceError
console.log(sayHello.toString());
sayHello();

结果如图:

运行结果
sayHello()被执行了,但是是在它被声明的词法作用域外部被执行的。sayHello()拥有一个词法作用域覆盖着makeHelloFunction()的内部作用域,闭包为了能使sayHello()在以后任意的时刻还可以引用这个作用域而保持它的存在。sayHello()依然拥有对母函数makeHelloFunction()作用域的引用,而这个引用称为闭包。

这个函数在它被编写时的词法作用域之外被调用。闭包使这个函数可以继续访问它在编写时被定义的词法作用域。

加法器是最直观的能观察到闭包的例子:

function makeAddThree() {
  var starter = 3;

function add(num) {
  return starter + num;
}

return add;
}


 const addThree = makeAddThree();

 console.log(addThree(0));   //  3

addThree()就是利用makeAddThree()作用域对add()形成的闭包来生成的。

4.循环 + 闭包

最老实巴交的for循环:

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

我们一般会期待这段代码的输出结果是分别打印数字“1”,“2“,”3“,...”5”,一次一个,一秒一个,但实际上我们得到的是“6”被打印五次,1秒1个。因为for循环的终结条件式i<=5,第一次满足这个条件时的i是6,所以输出结果反映的是i在循环终结后的最终值。超时的回调函数都将在循环完成之后立即运行。虽然所有这5个函数在每次循环迭代中分离地定义,由于作用域的工作方式,他们都闭包在同一个共享的全局作用域上,而它事实上只有一个i

举一反三:

function makeFunctionArray(){
  const arr = [];

  for(var i = 0; i < 5; i++) {
    arr.push(function() {console.log(i);
    });
  }
  return arr;
}
const arr = makeFunctionArray();
arr[0]();

结果为5,因为当调用arr0时循环已经结束,i=5i有作用于函数makeFunctionArray()闭包,所以被内部函数arr.push()添加进arr[]的五个i全为5。

同理,如果加上console.log,打印出的结果也为5。

function makeFunctionArray(){
  const arr = [];

  for(var i = 0; i < 5; i++) {
    arr.push(function() {console.log(i);
    });
  }
  console.log(i);
  return arr;
}
const arr = makeFunctionArray();

arr[0]();
//结果是5 5

因为i是用var声明的,var的有词法作用域作用到for循环块的}截止,而此处i被调用的位置已经结束了循环,所以console打印5的结果

但是注意!如果把for循环里的var换成let,我们会得到ReferenceError,因为let的块级作用域只作用到for循环的 ),因此在外部作用域被调用时JS引擎会当其不存在,而显示ReferenceError
但是同时,arr[0]();的结果也变了:

function makeFunctionArray(){
  const arr = [];

  for(let i = 0; i < 5; i++) {
    arr.push(function() {console.log(i);
    });
  }
  //console.log(i);
  return arr;
}
const arr = makeFunctionArray();

arr[0](); //结果为0

5.立即执行函数表达式IIFE + 闭包

IIFE(Immediately Invoked Function Expression)

  • 会产生闭包
  • 不会写入或者修改全局对象
const sayHello = (function makeHelloFunction() {
  const message = 'Hello!';

  function sayHello() {
    console.log(message);
  } return sayHello;
})()

sayHello();   // Hello!

也可以用IIFE来做加法器:

var add = (function () {
    var counter = 0;
    return function () {counter += 1; return counter}
})();

add();
add();
add();

// the counter is now 3

IIFE的好处在于它能创建出一个新的作用域,并且不会写入或者修改全局对象。因为JavaScript中只有全局作用域和函数作用域。为了避免变量污染,应该尽可能地少设置全局变量。

立即执行函数能配合闭包保存状态。
像普通的函数传参一样,立即执行函数也能传参数。如果在函数内部再定义一个函数,而里面的那个函数能引用外部的变量和参数(闭包),利用这一点,我们能使用立即执行函数锁住变量保存状态。

function makeFunctionArray(){
  const arr = [];

  for(var i = 0; i < 5; i++) {
    arr.push((function(a) {
      return function(){console.log(a);}
    })(i));
  }
  console.log(i);       //5
  return arr;
}
const arr = makeFunctionArray();

arr[0]();        //0
arr[1]();        //1

相关文章

  • JS之闭包与IIFE

    本篇文章主要讨论了: JavaScript引擎 全局对象 闭包 循环 + 闭包 IIFE + 闭包 1.JavaS...

  • JS中的IIFE和闭包

    IIFE( 立即调用函数表达式) 是一个在定义时就会立即执行的JavaScript函数。 这是一个被称为自执行匿名...

  • JS闭包问题(二)

    在之前的JS闭包问题(一)文章中大概介绍了一下JS闭包,同时讲了闭包与变量之间的问题,今天我们继续聊闭包,聊聊闭包...

  • 关于立即执行函数IIFE

    [译] JavaScript:立即执行函数表达式(IIFE) IIFE保存闭包的状态 就像当函数通过他们的名字被调...

  • JS中的IIFE(立即执行方法)

    IIFE:immediately-invoked function expression 通常函数的闭包时这样写的...

  • php之闭包函数(Closure)

    php闭包函数(Closure) JS闭包 js和php闭包使用和区别

  • Day4 闭包

    闭包允许函数访问定义时的词法作用域回调函数基本上都是闭包模块利用闭包隔离变量,暴露公共API(也会伴随IIFE立即...

  • JS闭包

    JS闭包 闭包练习

  • js之闭包

    变量的作用域 在JavaScript中,函数内部可以读取全局变量。 但是,函数外部无法读取函数内部的局部变量。 这...

  • js之闭包

    前言:好久没写过东西了,今天突发奇想把自己之前学习中自己对于js理解以及做的笔记整理一下分享出来,同大家一起学习进...

网友评论

      本文标题:JS之闭包与IIFE

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