阅读 503

IIFE为什么不会污染全局变量

在开发过程中见到的IIFE,函数表达式都是匿名的,如果我们把它写成命名函数表达式,那全局作用域中岂不是多了一个变量,这样又怎么能保证不会污染全局变量呢?

通常我们在谈论IIFE时,都是在谈论它的用法,模块化,闭包等等,却少有提及它为什么可以拿来做模块化,为什么不会污染全局变量。

本文虽然是在说IIFE,但是并不会过多的介绍它的使用,或者其实践意义(毕竟在标准中已经ES6的今天讨论这个意义不是很大),更多的是讨论这种写法的本身在语法上会成立的原因,为什么可以在块级作用域出现之前替代它,及其中涉及到的点。

阅读判定

考虑下面代码

(function a(){
    console.log('run a');
})();
console.log(a);
复制代码

这段代码运行在严格模式下,这里说一下运行结果,a无法打印,运行到这里会报错。

对运行结果存在疑惑?或许本文能够令你稍解疑惑。

IIFE的成立

IIFE可以看做是两部分构成,前半部分定义了一个函数表达式,后半部分的括号这是表示运行这个函数。拿一个具体的例子描述一下。

(function a(){
    console.log('run a');
})();
复制代码

在上面的代码中前半部分的圆括号定义了一个函数表达式,函数后面加上圆括号表示的语法是运行这个函数。当然IIFE的写法不止这一种,还有其他诸如,使用一个圆括号将函数表达式和后面的圆括号一起括起来等各种写法。

由于标准中规定了function关键字开头是一个函数声明,所以要它变成函数表达式,我们需要加点东西,比如在function 开头加个 +,void,-等各种运算符,总之我们的目的是不让这一行以function开头,这样在语法解析式会认为function(){}()是一个表达式去运行它,而不是当做函数声明去解析它。看下面的例子:

console.log(+function s(){return 1}())      // 1
console.log(+function a(){})    // NaN
复制代码

从上面的运行过可以看出,IIFE执行时是将function(){}()整体作为了一个表达式在运行的最终得出一个结果,当然在使用IIFE时我们并不关心返回结果。

不会污染全局作用域的原因

IIFE不会污染全局变量的原因,是函数表达式的特性。

命名函数表达式

当我们使用函数表达式创建函数时,想在函数体内部使用当前函数,可以使用命名函数表达式。这个函数名称只会作为函数体内部变量。换言之,使用表达式创建的命名函数,并不能和声明函数一样在声明函数的作用域产生变量,而只会在这个命名函数内部产生这个变量,且该变量是只读的,不可被赋值。

var a = function c() {
    c = 'test';
    console.log(c); // ƒ c() {
                    // c = 'test';
                    // console.log(c);
                    // } 
}
a();
console.log(c); // c is not defined
复制代码

这里我们可以理解,IIFE不会污染全局变量是利用了函数表式的特性,由此而衍生的种种写法,只是为了在语法上是的解释器执行时能够识别这个表达式,然后执行它。当看到了这个本质的时候,对于它的种种写法我们就不需要去机械记忆了,我们自己也可以写出很多。

结论

究其根本,IIFE是充分理解了语言特性并结合时代需求的产物。这里的特性更多的就是函数表达式的特性了,当然也有一部分语法特性的结合。需求当然就是所谓前端 “刀耕火种” 时代人们对组件化的探索。

放在篇文章下不合适,但是就是突然想写的话: 记得有一种论调说,前端的发展不过是把其他语言多年前就已经实践过的思想搬过来而已,因此觉得前端没什么技术含量,甚至产生优越感。但是我想说的是,思想并不是某一语言,或者某一领域的所有物。不能因为年轻的事物,正在践行别人实践过的思想就认为它是在抄袭照搬的,毕竟同一个思想的实践在不同的需求下产生的火花是可以完全不同的。也借此表达下前端还是有很多独特而有意思的东西的,这一细分领域的出现不过是时代需求的产物,大家都是为需求服务的,所以没有必要产生所谓优越感或者自卑感,毕竟更好的实现需求才是根本。

关注下面的标签,发现更多相似文章
评论