我们都知道,函数的声明方式有这两种
function fnA(){alert('msg');}//声明式定义函数
var fnB = function(){alert('msg');}//函数赋值表达式定义函数
函数的调用方式通常是FunctionName()
但是,如果我们尝试为一个“定义函数”末尾加上()
,解析器是无法理解的。
function msg(){
alert('message');
}();//解析器是无法理解的
定义函数的调用方式应该是 msg()
; 那为什么将函数体部分用()
包裹起来就可以了呢?
原来,使用括号包裹定义函数体,解析器将会以函数表达式的方式去调用定义函数。也就是说,任何能将函数变成一个函数表达式的作法,都可以使解析器正确的调用定义函数。而 !
就是其中一个,而 + - || ~
都有这样的功能。
另外,用 ! 可能更多的是一个习惯问题,不同的运算符,性能是不同的。
// 这么写会报错,因为这是一个函数定义:
function() {}()
// 常见的(多了一对括号),调用匿名函数:
(function() {})()
// 但在前面加上一个布尔运算符(只多了一个感叹号),就是表达式了,将执行后面的代码,也就合法实现调用
!function() {}()
js中function
前面加 !
的含义
自执行匿名函数:
- 常见格式:
(function() { /* code */ })();
- 解释:包围函数
(function(){})
的第一对括号向脚本返回未命名的函数,随后一对空括号立即执行返回的未命名函数,括号内为匿名函数的参数。 - 作用:可以用它创建命名空间,只要把自己所有的代码都写在这个特殊的函数包装内,那么外部就不能访问,除非你允许(变量前加上
window
,这样该函数或变量就成为全局)。各JavaScript库的代码也基本是这种组织形式。
总结一下,执行函数的作用主要为 匿名 和 自动执行,代码在被解释时就已经在运行了。
其他写法
(function () { /* code */ } ());
!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();
从function
前面的!
想到的
最近没事喜欢看看,一些js库的源码,结果发现库前不是加一个!
就是加+
或者一个()
,心中猜出个大概知道这个是让函数自动执行,可是这么多符号达到同一个目的,原理是什么呢,下面做一下剖析:
先从IIFE
开始介绍
IIFE(Imdiately Invoked Function Expression 立即执行的函数表达式)
function(){
alert('IIFE');
}
把这个代码放在console
中执行会报错
因为这个是一个匿名函数,要想让它正常运行就必须给个函数名,然后通过函数名调用。
好了这下知道为啥我们看到很多类库写的时候也是匿名函数结果不报错了吧,就是因为这些前面加的符号的原因。
其实在匿名函数前面加上这些符号后,就把一个函数声明语句变成了一个函数表达式,是表达式就会在
script
标签中自动执行。运算符
- 为什么加上了这些运算符后就能让一个匿名函数变成一个不会报错的函数表达式呢?
我们自然会想到javascript的解析器到底是怎么工作识别的呢,js解析器执行js表达式这个肯定是没有问题的。其实无论是括号,还是感叹号,让整个语句合法做的事情只有一件,就是让一个函数声明语句变成了一个表达式。所以我们让一个函数定义变成一个函数表达式来执行就不会报错。 - 原理
这样是一个函数声明
function a(){
alert('IIFE');
}
这样是一个函数调用
a();
理解一下就是在一个声明了的函数后面加上一个()
就可以调用函数了
function a(){
alert('IIFE');
}()
就这样
但是我们按上面在console
中执行发现出错了
因为这样的代码混淆了函数声明和函数调用,以这种方式声明的函数
a
,就应该以 a()
; 的方式调用。但是括号则不同,它将一个函数声明转化成了一个表达式,解析器不再以函数声明的方式处理函数
a
,而是作为一个函数表达式处理,也因此只有在程序执行到函数a
时它才能被访问。所以,任何消除函数声明和函数表达式间歧义的方法,都可以被解析器正确识别。所以,赋值,逻辑,甚至是逗号,各种操作符都可以告诉解析器,这个不是函数声明,它是个函数表达式。并且,对函数一元运算可以算的上是消除歧义最快的方式,感叹号只是其中之一,如果不在乎返回值,这些一元运算都是有效的
!function(){alert('iifksp')}() // true
+function(){alert('iifksp')}() // NaN
-function(){alert('iifksp')}() // NaN
~function(){alert('iifksp')}() // -1
性能
针对这些一元运算符,到底用哪个好呢,测试发现()的性能最优越,但是差别都不是特明显,所以对于一个库来说用几个这样的符号来说看不出什么影响,所以平常用 ! + -
都可以,就看个人的代码习惯,当然最好还是用()
。