su37josephxia/frontend-interview

Day11 - 谈谈闭包与即时函数的应用

su37josephxia opened this issue · 31 comments

闭包:内部函数访问到外部函数的作用域

过程

  1. 在编译阶段,会创建一个闭包对象保存在内存中
  2. 执行完后,函数上下文销毁,当内部函数作用域链仍然在引用,所以不会销毁,直到内部用完后才销毁

优点

  1. 变量驻扎在内存中,供之后使用
  2. 避免变量污染全局
  3. 吧变量存到独立的作用域,作为私有成员的存在

缺点

  1. 内存消耗,操作不当导致无法回收
  2. 作用域的层级,影响处理速度

应用

  1. 函数式编程的应用
  2. 让变量临时存储

eg:给dom元素绑定点击事件

var liDoms = document.querySelectorAll('.article-tab li');
for (var i = 0; i <= liDoms.length; i++) {
    (function () {
        //让变量临时存储
        var p = i
        liDoms[i].onclick = function () {
            alert(p);
        }
    })();
}

即时函数:又称“立即执行函数”IIFF
eg:

;((win)=>{
win.jQuery = win.$ = jQuery;
})(window)

优点

  1. 避免污染全局变量
  2. 引用透明
  3. 代码自动运行

缺点

  1. 代码的可读性变差
  2. 开头前面需要加上";",避免代码压缩导致的语法解析错误。

应用

  1. 在无模块化早期也就是靠立即执行函数的方式来隔离数据,避免造成全局变量污染
  2. 惰性函数的应用

eg:惰性函数的应用

const ajax = (()=>{
    var xhr = null;
    if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
    } else {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
    }
    return xhr;//关键代码,后期都不需要环境判断支持了,惰性函数,只执行一次
})()

即时执行函数,是一种特殊的JS语法,可以使函数在创建时候立即运行

如下写法

;(function () {
    alert('watch out!');
}());

这个函数会立即弹出一个alert

很明显,即时函数也是一个明显的闭包构成。

常见业务场景有:项目初始化、程序模块化(避免全局变量污染等)、事件绑定...

  • 即时函数(Immediate Functions)是一种特殊的JavaScript语法,可以使函数在定义后立即执行。
  • ( function () {
    alert('!!!');
    } ) () ;
  • 即时函数可以用于项目初始化数据;
  • 可提供独立局部作用域,不会污染全局环境;
  • 可以做模块化程序;

即时函数

也叫立即执行函数,是定义之后立刻执行的函数,执行完之后,函数内部的变量也随着函数一起销毁

如果要定义变量,又不希望变量污染全局,可以在立即执行函数中定义变量,然后通过闭包的形式将变量保存下来

立即执行函数 + 闭包的模式 通常用于 程序的模块化设计,解决全局命名冲突的问题

即时函数

即时函数是一种语法模式,它会使函数在定义后立即执行。

这种模式由以下几个部分组成:

  • 使用函数表达式定义一个函数。(不能使用函数声明。)
  • 在最后加入一对括号,这会使函数立即被执行。
  • 把整个函数包裹到一对括号中(只在没有将函数赋值给变量时需要)。

即时函数可以帮助我们做一些不想留下全局变量的工作。因为即时函数只能执行一次,执行完了立马消失,所以不会重复创建闭包,也不会对当前作用域造成任何污染。

即时函数

即时函数是定义了立即调用的匿名函数,也称立即执行函数,可以用它来开辟一个独立的作用域。当即时函数中定义的变量被内部函数使用时,就会形成闭包,如下所示:

(function() {
   var num = 0;
   button.click = function() {
       console.log(num++)
   }
})()

这个就形成了闭包。我们通常用立即执行函数来创建闭包,因为立即执行函数只能执行一次,执行完就立马消失了,不会重复创建闭包,同时也不会对当前作用域造成任何的污染。

即时函数会在定义后立即执行。

即使函数与闭包的配合

  • 可以保存状态
  • 模块化使用,避免污染全局变量,解决命名冲突等问题

IIFE

创建后立即执行的函数,好处是函数内是独立的一个模块,不会和全局作用作用域冲突

  • 立即执行函数构成的闭包将函数内部的变量隐藏
  • 隐藏的变量可以被外部使用,又不受外界影响,不可以修改内部变量

什么时候需要用到立即执行函数呢?

1.当我们需要写一个js文件,并且复用率很高的时候,建议使用。

2.如果声明的函数只需要调用一次,建议使用。

3.独立模块,这个和第一点差不多。单独提出来,是想强调一下立即执行函数的好处,开发时,它能做到各模块的低耦合,减少对全局作用域的污染。

相同点:他们都是函数的一种特殊形态,并且可以共存。而且闭包配合即时函数“口味更佳”。
不同点:即时函数是定义一个函数,并立即执行。它只能被使用一次,相当于“阅后即焚”。闭包是指一个函数与它捕获的外部变量的合体,按照MDN的说法,闭包就像一个对象---一个具有一个方法(行为)和一个或多个私有字段(状态)的对象。从这个角度看,闭包是符合面向对象的封装**的。

经典应用

for(var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i)
    }, 1000)
}
  • 期望在1秒后输出0 1 2 3 4,实际会输出5 5 5 5 5,这是因为for循环同步执行,setTimeout是异步的,seTimeout接收到参数的时候 i 已经变成了5
  • 在外面套一层立即执行函数会将 i 值复制一份并当参数传入,改变了setTimeout的作用域,内部会产生闭包拿到这个复制的 i 值,因此就能按照预期输出
for(var i = 0; i < 5; i++) {
    (function(j){
        setTimeout(function() {
            console.log(j)
        }, 1000)
    })(i)
}

什么是即时函数
即时函数是匿名函数的另一种应用。什么是即时函数?即时函数就是函数在定义后可以被立即调用
使用场景
1、隔离作用域
IIFE最常见的功能,就是隔离作用域,在ES6之前JS原生也没有块级作用域的概念,所以需要函数作用域来模拟。
2 惰性函数
DOM事件添加中,为了兼容现代浏览器和IE浏览器,我们需要对浏览器环境进行一次判断:
3 用闭包保存状态
作用
1、避免变量污染
2、隔离作用域
3、提高性能(减少对作用域的查找)
4、模拟块级作用域
5、改变代码执行顺序

即时函数是一种特殊的JavaScript语法,可以使函数在定义后立即执行,也就是常说的iife

使用场景:当你需要在程序启动时执行一些初始代码,这些初始化的工作只需要执行一次,而且当中用到的临时变量以后将不再有用,这时就可以把这些代码放到一个即时函数中执行

即时函数也是闭包的一种形式

即时函数是指定义后就立即调用的匿名函数,可以开辟一个独立的作用域,如果在即时函数中定义一个的变量被内部函数捕获使用,这个函数就形成了闭包。
对于即时函数,只会执行一次,执行完就会被回收,因此不会多次引用闭包,可以对作用域起到保护作用。

即时函数IIFE会形成闭包,但不是经典的闭包

立即执行函数定义:

此类函数没有声明,在一次执行过后即释放。适合做初始化工作。

立即执行函数使用场景

  1. 函数只执行一次,不需要定义浪费空间
var num = (function (a,b,c){
	var d = a+b+c
	return d;
}(1,2,3))
  1. 可以避免污染全局变量
    某些代码只需要执行一次,比如只需要显示一个时间,但是这些代码也需要一些临时的变量,但是初始化过程结束之后,就再也不会被用到,如果将这些变量作为全局变量,不是一个好的主意,我们可以用立即执行函数——去将我们所有的代码包裹在它的局部作用域中,不会让任何变量泄露成全局变量

在jq中经常看到这样的代码

;(function ( $, window, document, undefined ){

//函数体内具体代码

})(jQuery, window,document);

1.为什么要传入 jQuery
通过定义一个匿名函数,创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命名空间。这点非常有用也是一个 JS 框架必须支持的功能,jQuery 被应用在成千上万的 JavaScript 程序中,必须确保 jQuery 创建的变量不能和导入他的程序所使用的变量发生冲突。

2.window,document参数
window, document实参分别接受window, document对象,window, document对象都是全局环境下的,而在函数体内的window, document其实是局部变量,不是全局的window, document对象。这样做有个好处就是可以提高性能,减少作用域链的查询时间,如果你在函数体内需要多次调用window 或 document对象,这样把window 或 document对象当作参数传进去,这样做是非常有必要的。当然你如果你的插件用不到这两个对象,那么就不用传递这两个参数了。

3.undefined
原因是有的浏览器中(IE8)undefined 是可以重新赋值的,这样写可以避免函数体内的undefined被外部重写。

4.为什么我们有时候会看到自执行函数前面有分号";"
因为自执行函数前面是括号,可能会把括号前面的当成一个函数来执行。

1.看过jQuery源码的人应该知道,jQuery开篇用的就是立即执行函数。立即执行函数常用于第三方库,好处在于隔离作用域,任何一个第三方库都会存在大量的变量和函数,为了避免变量污染(命名冲突),开发者们想到的解决办法就是使用立即执行函数。

  • 即时函数是匿名函数的另一种应用。即时函数就是函数在定义后可以被立即调用。
  • 基本形式调用
  • `
  • (function (){alert (“hello word !”);)();
  • `
  • 通过定义一个匿名函数,创建了一个新的函数作用域,相当于创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏污染全局的命名空间。此时若是想访问全局对象,将全局对象以参数形式传进去即可,如jQuery代码结构
  • `
    (function($){
    $.fn.extend({
    test:function(){
    alert($(this).attr('id'));
    }
    })
    })(jQuery)

$('#myDiv').test();
打印出 : myDiv

  • `

即时函数 即 立即执行函数,是一种js语法,当一个函数不需要在其他地方调用,只需要执行这一次时可以使用
他的作用是开辟出一块独立的作用域,避免污染全局
如果外界需要访问即时函数内部的某些变量时,可以通过闭包的方式来访问

即时函数的应用

  • 执行一次初始代码

    有些初始化的工作只需要执行一次,这时就可以把这些代码放到一个即时函数中执行

  • 模块化

    即时函数提供局部的变量作用域,不会污染全局环境,同时其中的代码又能立即执行。

    所以经常用在模块化的程序中。如果我们把每一个功能独立的代码段都用即时函数来组织,并放到一个单独的js文件中,那我们什么时候想使用,就把这个js文件引入进来就可以了

    // module1 defined in module1.js
    (function () {
      // all the module 1 code ...
    }());
  • 保存闭包作用域的变量值

    //每秒执行1次,分别输出1-10
    for(var i=1;i<=10;i++){
        (function(j){
            //j来接收
            setTimeout(function(){
                console.log(j);
            },j*1000);
        })(i)//i作为实参传入
    }

即时函数是一种利用JavaScript函数生成新作用域的编程方法, 常应用于

  • jquery等第三方库,用于隔离作用域, 防止污染全局作用域
  • 由于传入到当前作用域,所以避免了向上查找作用域的查找(window/document)
  • 即时函数不一定是闭包,取决于是否利用跨作用域获取变量

自执行函数自身创建了一个函数作用域,在这个即时函数内如果有函数引用了外部但是在即时函数内的变量就形成了闭包。之所以能使用闭包是因为即时函数创造了一个函数作用域的环境。即时函数的经典使用就是生成单例,umd模块的封装。

// 单例
  ;(function () {
    let instance
    return () => {
      if (instance) return instance
      return (instance = { a: 1 })
    }
  })()
// 浏览器模块化
  (function (window) {
    let uid = 0
    window.createUid = () => uid++
  })(window)
// umd
  (function(_this, factory) {
    if (typeof window != undefined) ...
    if (typeof global != undefined) ...
    if (typeof module != undefined) ...
  })(globalThis, function () {})

即时函数:

即时函数(IIEF)是一种特殊的JavaScript语法,可以使函数在定义后立即执行,它也是闭包使用的一种形式。

(function ( ) {
    alert('watch out!');
}( ));
//或者
(function ( ) {
    alert('watch out!');
})( );

优点:

  • 弥补了js在全局作用域控制上的缺陷,因为js只有全局作用域和函数作用域,从ES6开始才有块级作用域。使用即时函数就能很容易开辟出一块独立的作用域。

应用

  • 模块化开发
    一些第三方插件,或者模块化开发时候,对一些功能模块封装到IIFE中,可以避免其中变量造成全局作用域污染的问题。

  • 利用独立的作用域保存参数
    轮流输出1-5

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

即时函数顾名思义,就是在创建函数后立即调用的函数;
(把一个函数包裹在一对括号中将函数声明转换为表达式,然后在结尾加上一个(), 让函数立即执行)
它可以提供局部作用域,不会污染全局环境,而且其中代码立即执行。
即时函数的函数体通常是匿名函数的形式,当然也可以给他命名,只不过他立即执行一次,也没必要命名。
即时函数本身跟闭包没有关系,他只是函数调用的一种方式。
可以利用即时函数构建闭包,返回想要的结果,然后执行完成后,销毁,不会占用多余内存。

即时函数是指定义完了就立刻被调用执行的函数

由于即时函数只能执行一次,执行完了立马消失

所以通过在即时函数内部来调用闭包,既不会重复创建闭包,也不会对当前作用域造成任何污染

eg:
(function() {
var str = ‘abcdefg’;
button.onclick = function() {
alert(str);
};
})();

即时函数是匿名函数的另一种应用。什么是即时函数?即时函数就是函数在定义后可以被立即调用

函数前加运算符的作用就是将匿名函数或函数声明转换为函数表达式。

  1. 通过定义一个匿名函数,创建了一个新的函数作用域,相当于创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏污染全局的命名空间。
    如果立即执行函数return出的内容引用了立即执行函数的参数或内部变量,很明显就是使用闭包的特性完成的。
  2. 由于立即执行函数立即执行的特性,所以只能执行一次,也可以用这种办法创建单例应用。

即时函数

即时函数又叫立即执行函数,顾名思义,是一种在定义后立即执行的函数.

应用场景

普通场景

因为函数执行后销毁,内部变量也会被回收,所以在我们不想留下全局变量以及避免命名冲突的时候可以用到即时函数。

和闭包配合使用

即时函数和闭包配合使用可以开辟一个独立的作用域,内部的变量可以用来保存状态,并且是外部访问不到的。

(function(){
  const obj = {
    name:'snail',
    age:18
  }
  document.body.onclick = function(){
    obj.age++
    console.log(obj)
  }
})()

闭包的特性是内部函数持续维护外部函数执行期间的作用域,形成自己的独立作用域;
即时函数又叫立即执行函数,与闭包不同的是,即时函数只会被调用一次,调用完,占用的内存就会被释放,不会造成内存泄露,那么与闭包的相同点是,即时函数可以通过从外部传递参数的方式,对外部作用域的变量持续引用,形成一次性的闭包环境。

即时函数,就是函数定义后立即执行。类似下面这样的写法

(function (){
    console.log('111')
})()

即时函数有以下特点:

  1. 一般都匿名函数,就算你给它起名字,也不生效,你后面再调这个函数会报错。
  2. 代码只执行一次,执行后内部执行上下文就会被销毁。

即时函数有以下优点:

  1. 封装私有变量。函数会创建一个独立的作用域,在里面申明的变量不会和外界冲突,外界也访问不到。
  2. 模块化封装代码,将一些模块化的代码封装在即时函数中,便于在各处复用。

闭包和即时函数的应用,其实惰性函数算一种,如下,即时函数执行后会给Fn赋值一个新的函数,这段代码的用在做环境判断的时候最合适。

const Fn = (function(){
    if(a){
        return function(){
            console.log('环境a这么处理')
        }
    }else if(b){
        return function(){
            console.log('环境b这么处理')
        }
    }
})()

立即执行函数,我们就把它简称为即时函数好了。它的使用场景就是我们不需要一个函数一次定义,多次调用,我们只需要一个函数作用域来做一些防止命名冲突,防止全局污染这些事情,这就是即时函数的作用。

那么在即时函数中,我们也可以使用闭包。在即时函数中,我们返回一个函数,然后这个函数用到了即时函数的本地变量,那么,这就是闭包和即时函数的联合使用。

即时函数:Immediately-Invoked Function Expression。简写为IIFE。意思就是函数定义之后立马被执行。
例如:
(function fn(){
return 3
})();
一般可以用在for循环中,在es6之前还没有块作用域的时候,就会使用for循环里面的立即执行函数来保存每次循环得到的数据。
闭包可以让函数作用域中的变量,可以用作柯里化等等,但是有时并不需要一直掉用闭包函数,使用里面的返回值,而是只需要它执行一次即可。
立即执行函数的核心是闭包,若是外部需要访问立即执行函数里面的变量就构成了闭包。
立即执行函数有主要的优点是:
1、不用所有的变量和方法都定义在全局,执行一次过后就不再需要了
2、内部可以形成一个单独的作用域,可以用来封装一些私有变量
3、内部变量执行完立即被销毁,不会占用多余内存