闭包的概念,作用,和优缺点

闭包的概念

闭包就是能读取其他函数内部变量的函数。

也可以简单的理解为:“定义在一个函数内部的函数”

闭包的形式

    //在执行fn1时,其返回值是一个函数,即fn2,所以再一次执行的时候就是返回的a+b的值。
    function fn1(){
        var a = 5;
        return function fn2(b){
            return a + b
        }
    }
    var result = fn1();
    alert(result(2)); //7

ES6中的闭包(经典闭包面试题)

    //闭包常见的面试题
for (var i = 1; i <= 5; i++) {
    setTimeout( function timer() {
        console.log(i);
    }, 1000 );
}   //连续5个6

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

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

闭包的优缺点

优点:

  1. 保护函数内的变量安全,加强了封装性
  2. 在内存中维持一个变量(用的太多就变成了缺点,占内存)

缺点:

  1. 闭包有一个非常严重的问题,那就是内存浪费问题,这个内存浪费不仅仅因为它常驻内存,更重要的是,对闭包的使用不当会造成无效内存的产生

闭包的作用

看了闭包与ES6块级作用域的比较之后,我们会不会觉得既然有了块级作用域就没有闭包存在的必要性了呢?答案是否定的,闭包的作用远不止于此。

单例模式(封装变量,收敛权限)
  1. 什么是单例模式?

用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象
2. 写一个单例模式?

    //单例模式的实现
    var CreateDiv = function (html) {
        this.html = html;
        this.init();
    };
    
    CreateDiv.prototype.init = function () {
        var div = document.createElement('div');
        div.innerHTML = this.html;
        document.body.appendChild(div);
    };
    
    var ProxySingletonCreateDiv = (function () {
        var instance;
        return function (html) {
            if (!instance) {
                instance = new CreateDiv(html);
            }
            return instance;
        }
    })();
    
    var a = new ProxySingletonCreateDiv('sven1');
    var b = new ProxySingletonCreateDiv('sven2');

    alert(a === b); //true
  1. 核心?

     //单例模式抽象,分离创建对象的函数和判断对象是否已经创建
     var getSingle = function (fn) {
         var result;
         return function () {
             return result || ( result = fn.apply(this, arguments) );
         }
     };
    
函数柯里化
  1. 什么是函数柯里化?
    柯里化是指这样一个函数(假设叫做createCurry),他接收函数A作为参数,运行后能够返回一个新的函数。并且这个新的函数能够处理函数A的剩余参数。
  2. 与闭包的关系?

在前端面试中,你可能会遇到这样一个涉及到柯里化的题目。

    // 实现一个add方法,使计算结果能够满足如下预期:
    add(1)(2)(3) = 6;
    add(1, 2, 3)(4) = 10;
    add(1)(2)(3)(4)(5) = 15;

要补充的知识点是函数的隐式转换。当我们直接将函数参与其他的计算时,函数会默认调用toString方法,直接将函数体转换为字符串参与计算。

    function fn() { return 20 }
    console.log(fn + 10);     // 输出结果 function fn() { return 20 }10

但是我们可以重写函数的toString方法,让函数参与计算时,输出我们想要的结果。

    function fn() { return 20; }
    fn.toString = function() { return 30 }
    
    console.log(fn + 10); // 40

除此之外,当我们重写函数的valueOf方法也能够改变函数的隐式转换结果。

    function fn() { return 20; }
    fn.valueOf = function() { return 60 }
    
    console.log(fn + 10); // 70

当我们同时重写函数的toString方法与valueOf方法时,最终的结果会取valueOf方法的返回结果。

    function fn() { return 20; }
    fn.valueOf = function() { return 50 }
    fn.toString = function() { return 30 }
    
    console.log(fn + 10); // 60

下面是具体的实现方法:

    //实现参数不确定下的相加
    function add() {
        // 第一次执行时,定义一个数组专门用来存储所有的参数
        var _args = [].slice.call(arguments);
    
        // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
        var adder = function () {
            var _adder = function() {
                // [].push.apply(_args, [].slice.call(arguments));
                _args.push(...arguments);
                return _adder;
            };
    
            // 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
            _adder.toString = function () {
                return _args.reduce(function (a, b) {
                    return a + b;
                });
            }
    
            return _adder;
        }
        // return adder.apply(null, _args);
        return adder(..._args);
    }
    
    var a = add(1)(2)(3)(4);   // f 10
    var b = add(1, 2, 3, 4);   // f 10
    var c = add(1, 2)(3, 4);   // f 10
    var d = add(1, 2, 3)(4);   // f 10
    
    // 可以利用隐式转换的特性参与计算
    console.log(a + 10); // 20
    console.log(b + 20); // 30
    console.log(c + 30); // 40
    console.log(d + 40); // 50
    
    // 也可以继续传入参数,得到的结果再次利用隐式转换参与计算
    console.log(a(10) + 100);  // 120
    console.log(b(10) + 100);  // 120
    console.log(c(10) + 100);  // 120
    console.log(d(10) + 100);  // 120

    // 其实上栗中的add方法,就是下面这个函数的柯里化函数,只不过我们并没有使用通用式来转化,而是自己封装
    function add(...args) {
        return args.reduce((a, b) => a + b);
    }
经典的作用域问题

只要牵涉到函数作用域的问题我们一般都要想到闭包。他的牵涉范围甚广,一道题往往牵涉作用域,闭包,变量提升,自由变量,this指向等等。

在全局函数中this等于window

当函数被当做某个对象的方法调用时,this等于那个对象

匿名函数的执行环境具有全局性,this通常指向window

闭包在实际开发中的运用?

在实际开发中,闭包主要是用来封装变量,收敛权限。

    //封装变量,收敛权限
function isFirstLoad(){
    var list=[];
    return function(option){
        if(list.indexOf(option)>=0){ //检测是否存在于现有数组中,有则说明已存在
            console.log('已存在')
        }else{
            list.push(option);
            console.log('首次传入'); //没有则返回true,并把这次的数据录入进去
        }
    }
}

var ifl=isFirstLoad();
ifl("zhangsan"); 
ifl("lisi");
ifl("zhangsan");

猜你喜欢

转载自blog.csdn.net/qq_39771710/article/details/88850996