最简单易懂闭包讲解

简介

闭包在web前端开发中特别重要,好多前端开发者工作了几年都不知道闭包原理。现在我就讲一下【闭包】希望各位开发者都能够理解其原理。网上的教程也很多,希望这篇您真的能够学会使用。

概念

Closures (闭包)是使用被作用域封闭的变量,函数,闭包等执行的一个函数的作用域。通常我们用和其相应的函数来指代这些作用域。(可以访问独立数据的函数)

变量作用域

变量声明,无论发生在何处,都在执行任何代码之前进行处理。用var声明的变量的作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。如果你重新声明一个 JavaScript 变量,它将不会丢失其值。 将赋值给未声明变量的值在执行赋值时将其隐式地创建为全局变量(它将成为全局对象的属性)。声明和未声明变量之间的差异是:

  1. 声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的。
  2. 声明变量在任何代码执行前创建,而非声明变量只有在执行赋值操作的时候才会被创建。
  3. 声明变量是它所在上下文环境的不可配置属性,非声明变量是可配置的(如非声明变量可以被删除)。

/**例子 1 **/
var a=100;//全局变量a
function demo(){
	console.log(a);//由于当前上下文中存在a值,因此这里是undefined;
	               //也许有些开发者会问为什么呢?因为【变量提升】特性。
	var a=50;//局部变量a
	d=110;//全局变量 d
}
demo();
复制代码
函数or方法返回值

只要是函数或者方法都有返回值,默认返回值为undefined;

/**例子 2 **/
/**
 * 函数返回值
 * 方法也同理
 * 默认返回:undefined
 * */
function demo(){
	//如果没有使用return 改变返回值,默认值为undefined
}
var b=demo();//b的值为undefined;
复制代码

闭包

下边这些例子几乎涵盖了JavaScript中所有闭包,只要学会了下边的例子。你就能彻底掌握闭包。

/**例子 3 **/
/**
 * 简单的闭包
 * return 返回一个匿名函数
 * */
function demo(){
	var a=100;//局部变量a
	return function(){
		a=a+1;//在这个作用域可以访问 a值
		console.log(a);
	}
}

var b=demo();//b接收匿名函数;
b();//执行b,相当于执行匿名函数  结果:101
b();//执行b,相当于执行匿名函数  结果:102
复制代码
/**例子 4 **/
//同例子3
/**
 * 简单的闭包
 * return 返回一个sum函数
 * */
function demo(){
	var a=100;//局部变量a
	function sum(){
		a=a+1;//在这个作用域可以访问 a值
		console.log(a);
	}
	return sum;
}

var b=demo();//b接收匿名函数;
b();//执行b,相当于执行sum函数  结果:101
b();//执行b,相当于执行sum函数  结果:102
复制代码
/**例子 5 **/
/**
 * 简单的闭包
 * return undefined
 * */
var b=null;
function demo(){
	var a=100;//局部变量a
	b=function (){//把匿名函数的引用赋值给全局变量 b
		a=a+1;//在这个作用域可以访问 a值
		console.log(a);
	}	
}
demo();//执行demo函数--改变b的值
b();//执行b,相当于执行匿名函数  结果:101
b();//执行b,相当于执行匿名函数  结果:102
复制代码
/**例子 6 **/
/**
 * 闭包
 * return {}//返回一个对象,对象包含sum方法
 * */

function demo(){
	var a=100;//局部变量a
    function sum(){
		a=a+1;//在这个作用域可以访问 a值
		console.log(a);
	}	
	return {//返回一个对象,对象内包含 sum方法
		sum:sum
	}
}
var b=demo();//执行demo函数返回一个对象赋值给b
b.sum();//执行b.sum,相当于执行demo中的sum函数   结果:101
b.sum();//执行b.sum,相当于执行demo中的sum函数   结果:102
复制代码
/**例子 7 **/
/**
 * 闭包
 * return sum函数
 * */
function demo(){
	var a=100;//局部变量a
	/**
	 * 统计值
	 * @n {number} 数值
	 * */
	function count(n){
		console.log('a=',a+n);
	}
    function sum(){
		a=a+1;//在这个作用域可以访问 a值
		console.log(a);
		count(9);
	}	
	return sum;
}
var b=demo();//执行demo函数返回赋值给b
b();//执行b函数,相当于执行demo中的sum函数   结果:101,
       在执行demo函数中的 count函数进行统计  结果:110
复制代码
/**例子 8 **/
/**
 * 回调函数,产生的-->闭包
 * 
 * */
function demo(){
	var a=100;//局部变量a
    function sum(){
		a=a+1;//在这个作用域可以访问 a值
		console.log(a);
	}	
	demo1(sum);//执行demo1函数 把sum的引用传递进去
}
/**
 * 延迟执行函数
 * @fn {function} //回调执行的函数
 * */
function demo1(fn){
	//延迟1秒执行
	setTimeout(function(){
		var a=1000;//局部变量a为1000
		fn();//执行 demo 内的 sum函数,fn 只是 demo 内的 sum函数引用。
	},1000);
}
demo();//执行demo函数   结果:101
复制代码

闭包的实际应用

1.使用闭包手写bind函数

/**
 * bind 函数
 * */
if(!Function.prototype.bind){
	Function.prototype.bind = function(){
	    var _arg = [].slice.call(arguments),//绑定时候的参数
	    _that= _arg.shift(),//绑定到谁身上
		_older=this;//当前函数
		return function(){
			_older.apply(_that,_arg.concat([].slice.call(arguments)));
		}
	}
}

var s={
	names:'s',
	fn:function(){
		console.log(this);
	}
},
names='window-s';

function demo(){
	console.log(this.names);
	console.log([].slice.call(arguments));
}

var dome1=demo.bind(s,1,2,3);
dome1(4,5);//结果:s 
复制代码

2.我专门写了一个这样的插件用于函数整合队列并顺序执行。参考链接:https://juejin.im/post/5a1b863bf265da432d27cfee

/**
 * 作者:小小坤
 * 联系:java-script@qq.com
 * 日期:2017-11-11
 * 版本:1.0.0.4
 *     -----------使用说明----------
 * 1、把所有函数【包含异步执行的函数】按照顺序依次 使用lk.push存入
 * 2、带有参数的函数,一定要注意{最一个参数如果是callback}会被认为是 异步执行函数
 * 3、异步执行的函数,需要把最一个参数设置为callback。并在函数执行完毕执行callback();函数保证按照顺序执行
 * 
 * */
;! function() {
    var list = [], //存储函数的列表
    isFun = Object.prototype.toString; //用于验证是否是函数
    /**
     * 添加到列表中
     * @fn {Function} 函数体
     * */
    function push(fn) {
        isFun.call(fn) === "[object Function]" && list.push(fn);
    };
    /**
     * 开始执行列表中的所有函数,
     * 按照先进先出原则
     * 
     * */
    function star() {
        if(list.length) {
            var fn = list.shift(),//截取第一个函数
            arry=getfunarg(fn),//获取这个函数的参数列表
            _length=arry.length;//参数列表的长度
            if(_length && arry[_length-1] === 'callback') {
                if(_length===1){
                    fn(star);
                }else{
                    arry.pop();//删除最后一个参数
                    arry.push(star);//把回调函数存入数组
                    fn.apply(this,arry);
                }
            } else {
                fn.apply(this,arry);
                star(); 
            }
        }
    }
    /**
     * 查找函数参数名
     * @fn {Function } 要查找的函数
     * @return []返回参数数组
     * */
    function getfunarg(fn) {
        var f = /^[\s\(]*function[^(]*\(\s*([^)]*?)\s*\)/.exec(fn.toString());
        return f && f[1] ? f[1].split(/,\s*/) : [];
    }
    //挂在到Windows上。
    window.lk = {
        push: push,
        start: star
    }
}();
//使用测试
/**--------一条华丽的分割线--------**/
var a=100,
b=200,
d=300,
f=400;

//定义函数 a2  ,此函数带有一个参数,被认为是异步函数
function a2(a,b,callback) {
    console.time('2');
    setTimeout(function() {
        console.timeEnd('2');
        callback();
        console.log(a,'a');
    }, 1000);
}
//把函数函数 a2 放入数组
lk.push(a2);

//定义函数 a3
function a3(d, f) {
    console.log(f,'f');
    console.log(3);
}
//把函数函数 a3 放入数组
lk.push(a3);

//定义函数 a4 此函数带有一个参数,被认为是异步函数
function a4(callback) {
    console.time('4');
    setTimeout(function() {
        console.timeEnd('4');
        callback();
    }, 2000);
}
//把函数函数 a4 放入数组
lk.push(a4);

//最后开始执行
lk.start();
复制代码

3.vue、jQuery、react等框架使用闭包

总结

学习是一个漫长而痛苦的过程,当你真真正正的沉寂到知识的海洋,你会发现好多乐趣。不要让我们单纯为了工作而工作,要为了自己的兴趣而工作。代码是一种神奇的东西,我们可以驱动它干好多事情。为什么我们不好好做一个控制者呢!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值