一、函数命名的几种方式:
1、函数的声明式写法:function foo(){/*...*/},这种写法会导致函数提升,所有function关键字都会被解释器优先编译,不管是声明在什么位置,都可以调用它,但是它本身不会被执行,定义只是让解释器知道其存在,只有在被调用的时候才会执行。
例:
这里的foo(a,b)函数会被 Javascript引擎优先编译,因此在进行函数调用时候 不论放在哪个位置都可以成功打印出 结果。
2、函数的表达式写法为:var foo=function(){/*...*/},这种写法不会导致函数提升,于是就必须先声明,再调用,否则会出错,如图2。
例:
这里的函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式。因此在未声明之前调用,则报错。
引:匿名函数:function(){/*...*/};,使用function关键字声明一个函数,但未给函数命名,因此叫做匿名函数。匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。
如果将匿名函数赋予一个变量,就叫做上述的函数表达式了。
总结:函数声明式写法和函数表达式写法有两个区别:一个是函数提升的区别,另一个是:函数表达式可以在其后面加上() 立即调用该函数,函数声明只能通过foo()方式调用,后面加括号会报错,不符合语法规范。
例:
如果想通过语法检查,可以在函数声明式写法中加些符号,如:()、+、!等。
例:
function foo(){console.log("Hello World!")}() //声明函数后加()会报错
(function foo(){console.log("Hello World!")}()) //用括号把整个表达式包起来,正常执行 (function foo(){console.log("Hello World!")})() //用括号把函数包起来,正常执行 !function foo(){console.log("Hello World!")}() //使用!,求反,这里只想通过语法检查。 +function foo(){console.log("Hello World!")}() //使用+,正常执行 -function foo(){console.log("Hello World!")}() //使用-,正常执行 ~function foo(){console.log("Hello World!")}() //使用~,正常执行 void function foo(){console.log("Hello World!")}() //使用void,正常执行 new function foo(){console.log("Hello World!")}() //使用new,正常执行
从上述代码可以看出,在function前面加!、+、 -甚至是逗号等到都可以起到函数定义后立即执行的效果,而()、!、+、-、~等运算符,都可以将函数声明转换成函数表达式,消除了javascript引擎识别函数表达式和函数声明的歧义,告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。PS:加括号是最安全的做法,因为!、+、-等运算符还会和函数的返回值进行运算,有时会造成不必要的麻烦。
二、立即执行函数。
1、写法:( function() {/*...*/})()和( function() {/*...*/}() )
一般也写成匿名函数:匿名函数写法为function(){/*...*/},就是使用function关键字声明一个函数,但未给函数命名,倘若需要传值,直接将参数写到括号内即可,具体示例如下所示。
将它赋予一个变量则创建函数表达式,赋予一个事件则成为事件处理程序等。但是需要注意的是匿名函数不能单独使用,否则会js语法报错,至少要用()包裹起来。
上面的例子可以写成如下形式:
(function(){console.log("我是匿名函数。")}()) (function(){console.log("我是匿名函数。")})() !function(){console.log("我是匿名函数。")}() +function(){console.log("我是匿名函数。")}() -function(){console.log("我是匿名函数。")}() ~function(){console.log("我是匿名函数。")}() void function(){console.log("我是匿名函数。")}() new function(){console.log("我是匿名函数。")}()
2、作用
立即执行函数的作用是:1.创建一个独立的作用域,这个作用域里面的变量,外面访问不到,这样就可以避免变量污染。2.闭包和私有数据。
javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function() {/*...*/})()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。
引申:
闭包问题:
<ul id=”test”> <li>这是第一条</li> <li>这是第二条</li> <li>这是第三条</li> </ul> <script> var liList=document.getElementsByTagName(‘li‘); for(var i=0;i<liList.length;i++) { liList[i].onclick=function(){ console.log(i); } }; </script>
很多人觉得这样的执行效果是点击第一个li,则会输出1,点击第二个li,则会输出2,以此类推。但是真正的执行效果是,不管点击第几个li,都会输出3。因为 i 是贯穿整个作用域的,而不是给每个 li 分配了一个 i,用户触发的onclick事件之前,for循环已经执行结束了,而for循环执行完的时候i=3。
1》但是如果我们用了立即执行函数给每个 li 创造一个独立作用域,就可以改写为下面的这样,这样就能实现点击第几条就能输出几的功能。
<script> var liList=document.getElementsByTagName(‘li‘); for(var i=0;i<liList.length;i++) { (function(ii) { liList[ii].onclick=function(){ console.log(ii); } })(i) }; </script>
在立即执行函数执行的时候,i 的值被赋值给 ii,此后 ii 的值一直不变。i 的值从 0 变化到 3,对应3 个立即执行函数,这 3个立即执行函数里面的 ii 「分别」是 0、1、2。即:点击第几个li,就输出几
2》当然使用ES6语法中的let也可以实现上述的功能,将for循环中的var换成let去声明,如下所示
<script> var liList=document.getElementsByTagName(‘li‘); for(let i=0;i<liList.length;i++) { liList[i].onclick=function(){ console.log(i); } } </script>