带你理解最最最真实易懂的闭包,妈妈再也不怕面试官问问题

掀翻闭包的遮羞布

首先,什么是闭包?你可以理解为在两个不相连接的作用域下共享变量。
举个栗子:

function foo() {
	var a = 10;
	function bar () {
		console.log(a);
	}
	return bar;
}
foo()();// 10 请注意这里的10怎么来的。

注意到了么,这里在全局的作用域下面,使用了foo函数内部的变量,这就是闭包!!!

函数bar作用域覆盖了函数foo,我们把bar函数的引用作为返回值传递了回来,然后就可以在引用里使用foo的函数引用。

而闭包本身的神奇之处并不在此,一般来讲在foo函数执行完成之后foo的作用域也就被垃圾回收器回收了,但是这里并不是,因为还存在一个对foo作用域的引用被传递出来了,也就是bar,bar有一个指向a变量的指针,所以foo的作用域在foo函数使用完成之后没有被回收。bar依然持有对foo作用域的引用,这就叫做闭包。

还有多种各式各样的函数闭包。

function foo() {
	var a = 2;
	function baz() {
	console.log( a ); // 2
}
	bar( baz );
}
function bar(fn) {
	fn(); // 妈妈快看呀,这就是闭包!
}

也许在你了解了之后觉得闭包只是一个稍微奇怪一点的玩具而已,但是我想说的是闭包它并不仅仅只是一种写法而已,日常生活中都会用到各式各样的情况:

function wait(message) {
	setTimeout( function timer() {
		console.log( message );
	}, 1000 );
}
wait( "Hello, closure!" );

timer在1000毫秒之后其内部作用域并不会消失,然后含有对message的引用。

2、循环与闭包

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

在我的另一篇对前端的小知识点里提到过这里存在变量提升,每次都会打印出6,稍微解释一下,由于在函数延迟之后再去对i的引用进行RHS查找的话只会找到i=6,我们本来是想在每次迭代的时候都捕获一个i的副本,但是每次迭代都是共享的作用域,所以找到的都是6;
加入通过作用域的原理来修改代码的话,来试试:

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

这样可以吗?哈哈哈哈,还是不行,因为我们的词法作用域是空的,我们需要给他建立一个自己的私有属性。

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

这样就可以了,因为i指向的是每个内部的匿名函数的内部作用域j!
给这个代码美化一下:

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

当然你如果想更简洁的完成逻辑,可以用let

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

模块

在理解闭包之后再了解一下一般怎么去使用闭包。

function Car() {
	var name = "五菱宏光";
	var age = "2 年";	

	function sayName () {
		console.log(name);
	}

	function sayAge () {
		console.log(age);
	}
	function drive () {
		console.log('drived');
    }
    
    return {
        sayName: sayName,
        sayAge: sayAge,
        drive: drive
    }
}

var car = Car();
car.sayName();

这就是模块的使用。
本博客思路来源:(NEW)[美]-Kyle-Simpson-你不知道的JavaScript(上卷)

发布了20 篇原创文章 · 获赞 5 · 访问量 2075

猜你喜欢

转载自blog.csdn.net/qq_42859887/article/details/105348870