前言
在我断断续续地学习闭包的时候,真的是好像越学越糊涂,总感觉什么都是闭包~先说说我之前记住的概念(一段非常生硬的概念。):
闭包允许函数访问并操作函数外部的变量。只要变量或函数存在于声明函数时的作用域内,闭包即可使函数能够访问这些变量或函数。
其实真的理解闭包,看这段话肯定会点头说对,然鹅我竟然只记住了前面一句话,导致我做昨天的面试题时觉得以下地函数test是个闭包,直接判断出错~
var test = 1;
(function test() {
test = 7;
console.log(test); })();
console.log(test);
这当然不是闭包,我也是后来才知道这个IIFE(立即调用函数)也是函数表达式?。我真的没考虑过它属于函数表达式还是函数声明。知道这点后我把代码这样修改了一下,我认为控制台打印的4个结果都是7,毕竟都是函数表达式。但是我却发现同样是函数表达式,打印的结果完全不一样呢!!主要是IIFE部分打印的结果完全和我想的不一样。那IIFE到底是怎样的运行机制,如此神奇?它和平时常见的普通函数表达式有什么不一样的地方?为什么不同样输出7?
var test = 1;
(function test() {
test = 7;
console.log(test); //这里打印的不是7
})();
console.log(test); //这里当然也不是7
test = function() {
test = 7;
console.log(test);
}
test()
console.log(test);
打印结果
函数声明与函数表达式之间的区别
先一层层地慢慢了解,首先要知道函数声明与函数表达式之间地区别在于:函数声明的函数名称会被绑定在所在的作用域里,在函数声明被定义之前,它就可以被调用了。函数表达式的函数名称是绑定在函数表达式自身的函数中而不是所在的作用域中。函数表达式是在代码执行到达时被创建,并且仅从那一刻起可用。
所以,在第一次声明test = function(){…}前调用test()函数一定会报错的。(小声提醒自己:不是undefined,就是报错!)因为在作用域里就找不到test。
同样的,(function test() {…})作为函数表达式就意味着test函数名称只能在{…}里被访问。随着它的立即调用完,这个函数的test变量名就不会也不能在外部作用域出现了,或者说它避免了污染外部作用域。我们可以修改代码看看,先不管为什么第一个test不是7,至少明白函数声明与函数表达式分别将它们的名称标识符绑定哪儿:
//都是函数表达式~
(function test() {
test = 7;
console.log(typeof test); //function
})();
function testB() {
testB = 7;
console.log(typeof testB); //nunber
}
testB()
IIFE到底是怎样的函数表达式
但是现在还是会陷入很尴尬的局面:
(function test() {
test = 7;
console.log( test);
//ƒ test() { test =7;console.log(test);
}
})();
var test = function() {
test = 7;
console.log(test); //7
}
test()
不都是函数表达式,为何IIFE真就那么特立独行,她为什么不等于7❗其实在一开始的代码加入一行 “use strict”; 就知道为什么了。这段代码在严格模式下是会报错的。报错的内容是:Uncaught TypeError: Assignment to constant variable.
有道翻译一下:未捕获类型错误:对常量变量的赋值
"use strict"; //立马报错~~~
var test = 1;
(function test() {
test = 7;
console.log(test);
})();
因为IIFE的函数表达式的形式其实是这样的:
const test = function() {
console.log(test);
}
test()
并不是我认为的把这个函数赋值给普通变量,事实是应该赋值给常量const。正因为IIFE的这一点,导致编译器在执行的时候,会忽略掉test = 7这一句。所以为嘛不输出7?就因为我test是常量!试一试把这个test函数变成自动执行的匿名函数,也就是不会因此存在test常量,这下函数内的打印就可以正常打印出7来。
顺便补充一下:ES6 的模块自动采用严格模式,所以赋值给常量的函数里是不能再把函数名拿出来重新赋值的,不然会报错。所以我上面的代码把test = 7这一行删除了。这点和IIFE就有点不同了。
所以总结一下昨天做的这道题:test函数是一个函数表达式,不可提升,且这个立即调用函数是赋值给常量。