javascript之闭包七(闭包的应用场景)

7,894 阅读4分钟

1. 闭包应用场景之setTimeout

原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果

    setTimeout(function(param){
        alert(param)
    },1000)

    //通过闭包可以实现传参效果
    function func(param){
        return function(){
            alert(param)
        }
    }
    var f1 = func(1);
    setTimeout(f1,1000);

2. 闭包应用场景之回调

我们定义行为,然后把它关联到某个用户事件上(点击或者按键)。我们的代码通常会作为一个回调(事件触发时调用的函数)绑定到事件上

<body>
    <p>哈哈哈哈哈哈</p>
    <h1>hhhhhhhhh</h1>
    <h2>qqqqqqqqq</h2>
    <a href="#" id="size-12">12</a>
    <a href="#" id="size-14">14</a>
    <a href="#" id="size-16">16</a>

<script>
    function changeSize(size){
        return function(){
            document.body.style.fontSize = size + 'px';
        };
    }

    var size12 = changeSize(12);
    var size14 = changeSize(14);
    var size16 = changeSize(16);

    document.getElementById('size-12').onclick = size12;
    document.getElementById('size-14').onclick = size14;
    document.getElementById('size-16').onclick = size16;
</script>
</body>

3. 闭包应用场景之封装变量

用闭包定义能访问私有函数和私有变量的公有函数

    var counter = (function(){
        var privateCounter = 0; //私有变量
        function change(val){
            privateCounter += val;
        }
        return {
            increment:function(){   //三个闭包共享一个词法环境
                change(1);
            },
            decrement:function(){
                change(-1);
            },
            value:function(){
                return privateCounter;
            }
        };
    })();

    console.log(counter.value());//0
    counter.increment();
    counter.increment();//2
  1. 共享的环境创建在一个匿名函数体内,立即执行。
  2. 环境中有一个局部变量一个局部函数,通过匿名函数返回的对象的三个公共函数访问。
封装一个私有变量另一个例子:用JavaScript创建一个计数器:
'use strict';
function create_counter(initial) {
    var x = initial || 0;
    return {
        inc: function () {
            x += 1;
            return x;
        }
    }
}

运行结果:

var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3

var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13

在返回的对象中,实现了一个闭包,该闭包携带了局部变量x,并且,从外部代码根本无法访问到变量x。换句话说, 闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。

4. 闭包应用场景之为节点循环绑定click事件

function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push(function () {
            return i * i;
        });
    }
    return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都添加到一个Array中返回了。

你可能认为调用f1(),f2()和f3()结果应该是 1 4 9, 但实际结果是:

f1(); // 16
f2(); // 16
f3(); // 16

全部都是16!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了4,因此最终结果为16。

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push((function (n) {
            return function () {
                return n * n;
            }
        })(i));
    }
    return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

f1(); // 1
f2(); // 4
f3(); // 9

注意这里用了一个“创建一个匿名函数并立刻执行”的语法:

(function (x) {
    return x * x;
})(3); // 9

5. ES6不经意中闭包的应用

有时候闭包会在你不经意的时候出现,你可能已经看到了我们称之为局部应用程序的示例,如下面的代码所示。

let c = 4
const addX = x => n => n + x
const addThree = addX(3)
let d = addThree(c)
console.log('example partial application', d)

如果不使用箭头函数,等效的代码如下:

let c = 4
function addX(x) {
  return function(n) {
     return n + x
  }
}
const addThree = addX(3)
let d = addThree(c)
console.log('example partial application', d)

我们声明了一个通用加法函数 addX,它接受一个参数(x)并返回另一个函数。

返回的函数也接受一个参数并将其与变量 x 相加。

变量 x 是闭包的一部分,当在本地上下文中声明变量 addThree 时,它会分配到一个函数定义和一个闭包,闭包含变量 x。

所以,当调用并执行 addThree 时,它可以从闭包中访问变量 x 和变量 n(作为参数传递进去),并返回相加的和。

在这个示例中,控制台将打印数字 7。

参考文章链接:

  1. blog.csdn.net/qq_21132509…
  2. www.liaoxuefeng.com/wiki/102291…