闭包——我知道你的名字,但我却不了解你

前一段时间学习了一下阮一峰大佬写的python教程,感受本身就是井底之蛙,函数式编程☹、返回函数☹、装饰器☹、偏函数☹,这些都是些什么东西?不过,在看过这些以后,有一种感受就是,部分知识和javascript中的闭包很是类似(也能够说思想如出一辙),经过python与JS,认认真真的去理解一下Javascript中的闭包。javascript

什么是闭包?

我的理解:函数 A 返回函数 B、函数 B 引用了函数 A 的变量。java

百度百科:闭包就是可以读取其余函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,因此闭包能够理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部链接起来的桥梁。python

示例代码:面试

function makeFunc() {
    var name = "Mozilla";
    function displayName() {
        console.log(name);
    }
    return displayName;
}

var myFunc = makeFunc();
myFunc();                   // Mozilla
复制代码

闭包使用场景

1.对象私有数据

闭包最经常使用于实现对象私有数据,将模块的公有属性、方法暴露出来。下面一段代码'return'关键字将对象导出赋值给myModule,从而应用到闭包。编程

var myModule = (function (window, undefined) {
    let name = "echo";
    
    function getName() {
        return name;
    }
    
    return {
        name,
        getName
    }
})(window);

console.log( myModule.name ); // echo
console.log( myModule.getName() ); // echo
复制代码

2.延时器(setTimeout)与定时器(setInterval)

上次面试现时,遇到了定时器这道面试题。题目以下:浏览器

// 问这段代码输出什么?
for( var i = 0; i < 10; i++ ) {
	setTimeout(() => {
		console.log(i);
	}, 0);
}

// 答案是输出 10个 10
复制代码

考察点:var变量提高,以及setTimeout。执行到setTimeout会往浏览器的定时器线程推个任务,而后当达到时间了会经过轮询线程将回调推到宏任务队列中,若是那个时候主线程不忙就会去宏任务队列执行这个回调。因此定时器同步执行,回调是异步!闭包

详细请参考 javascript的宏任务和微任务app

回归正轨,咱们来说闭包: 上面代码如何作到输出0,1,2,3,4,5, 6,7,8,9,10呢?(固然可使用let,但咱们经过闭包来实现一下)异步

for( var i = 0; i < 10; i++ ) {
	((j) => {
        setTimeout(() => {
            console.log(j);
        }, 0);
    })(i)
}
复制代码

"setTimeout"方法里应用了闭包,使其内部可以记住每次循环所在的词法做用域和做用域链。函数式编程

3. 监听器

var oDiv = document.querySeletor("#div");

oDiv.onclick = function() {
	console.log( oDiv.id );
}
复制代码

4.函数式编程中偏函数

在函数式编程中,闭包常常被用于偏函数应用和柯里化。

偏函数应用: 是传给某个函数其中一部分参数,而后返回一个新的函数,该函数等待接收后续参数的过程。英文是partial application,也能够译做“局部应用”、“部分应用”、“偏应用”

话句话讲偏函数应用是一个函数,它接受另外一个函数为参数,这个做为参数的函数自己接收多个参数,它返回一个函数,这个函数与它的参数相比接收更少的参数。偏函数应用提早给出一部分参数,而返回的函数则会等待调用时传入剩余的参数。

// 给一段代码理解理解
// Relatively flexible, more specific function generator.
function bindFirstArg(fn, a) {
  return function(b) {
    return fn(a, b);
  };
}

// More general functions.
function add(a, b) {
  return a + b;
}

add(1, 2);           // 3

function multiply(a, b) {
  return a * b;
}

multiply(10, 2);     // 20

// More specific functions.
var addOne = bindFirstArg(add, 1);
addOne(2);           // 3
addOne(3);           // 4
addOne(10);          // 11
addOne(9000);        // 9001

var multiplyByTen = bindFirstArg(multiply, 10);
multiplyByTen(2);    // 20
multiplyByTen(3);    // 30
multiplyByTen(10);   // 100
multiplyByTen(9000); // 90000
复制代码

上面这段代码,亮点不在于它能够将某个参数绑定到任意的function,而且这个参数做为绑定的function的第一个参数,它还能将方法绑定它本身身上做为第一个参数,所以建立了一个可绑定的function。

python中偏函数案例:

# functools.partial就是帮助咱们建立一个偏函数的,不须要咱们本身定义int2(),
# 能够直接使用下面的代码建立一个新的函数int2

import functools
int2 = functools.partial(int, base=2)
int2('1000000')  # 64
int2('1010101') # 85
复制代码

简单总结functools.partial的做用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

是否是和上面JS代码思想很相似?

总结

闭包是JS中又一必须掌握的知识点,写到最后,想到了之前回答闭包知识点的问题时,本身回答的也不是很好,记住一句函数 A 返回函数 B、函数 B 引用了函数 A 的变量,在想一想应用场景,就能大体掌握闭包知识点了,顺带提一句,若是学过python,装饰器这一章节就是很典型的闭包,这也是学Python要跨过去的一道门。

来看看python装饰器代码:

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print '%s %s():' % (text, func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator
    
@log('execute')
def now():
    print '2013-12-25'
    
now()

# 输出
# execute now():
# 2013-12-25

# 首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。
复制代码
相关文章
相关标签/搜索