欢迎!从2017年开始,将慢慢的不在xmind上写总结了,全部转到博客中!这里将不再随便写写,将继承在xmind的精神,继续前行!!!

函数 进阶学习之四: 函数和函数表达式的区别 与 IIFE 的原理

一 :函数和函数表达体

1.前文 提到声明一个变量 和 定义一个变量,那么对于函数,也存在这个问题,这里 要区分 函数函数表达体

function keqing(){   //函数
    alert('Hi~');  
}

var keqing = function(){  //函数表达式
  alert('Hi~')
}

 

function foo() {...}     // 这是定义,Declaration;定义只是让解释器知道其存在,但是不会运行。
foo();                   // 这是语句,Statement;解释器遇到语句是会运行它的。

 

 

 

2.函数的函数声明有一个重要特征 —— 函数声明提升(即前文提到的Hoisting)

keqing();   // 正常执行 弹出Hi~
function keqing(){   
  alert('Hi~');
}
函数:在读取执行代码之前会先读取函数声明

 


keqing();  //报错  Uncaught TypeError: keqing is not a function
var keqing = function(){
  alert('Hi~');
}
函数表达式:没有函数声明提升,在执行前必须先赋值

二:IIFE 的真相

在Bootstrap源码(具体请看《Bootstrap源码解析》)和其他jQuery插件经常看到如下的写法: 

+function ($) {   
  
}(window.jQuery); 

这种写法就是 IIFE (Imdiately Invoked Function Expression 立即执行的函数表达式)

 

结论1:
函数表达式中的函数可以为匿名函数,也可以有函数名,但是该函数实际上不能直接使用,只能通过表达式左边的变量 a 来调用。

var a = function(){  
  alert(0);  
}  
var b = new a(); //可以弹出 0
直接 a(); 也可以执行 弹出0.个人觉着还是 直接执行a();比较直观,

 

结论2:
函数声明时必须有函数名。 

三:使用

那么为什么要 IIFE?
1.传统的方法啰嗦,定义和执行分开写;
2.传统的方法直接污染全局命名空间(浏览器里的 global 对象,如 window)
于是,开发者们想找一个可以解决以上问题的写法:function foo(...){}();

但是:这样写是不行的

因为 function foo(...){} 这个部分只是一个声明,函数并没有执行,对于解释器来说,就好像你写了一个字符串 "function foo(...){}",它需要使用解析函数,比如 eval() 来执行它才可以。

所以把 () 直接放在声明后面是不会执行,这是错误的语法。

如何把它变得正确?说起来也简单,只要把 声明 变成 表达式(Expression) 就可以了,

最常见的办法是把函数声明用一对 () 包裹起来

例如

(function foo() {...})    // 这里是故意换行,实际上可以和下面的括号连起来
();
//这就等价于:
var foo = function () {...};    // 这就不是定义,而是表达式了。
foo();

注意:function foo(...){}();

也可以直接用括号包起来,这也是一种等价的表达式:(function foo(){...}());

所以:以后见到(function foo(){...}());和  (function foo(){...})()是一样的。没啥区别!!

一般用(function (){}) 还有个作用,就是 避免全局变量

 

总计一下实现方法:

方法:通过一元操作符+变成了函数表达式。也可以使用 - ~ !等其他一元运算符或者括号,目的是为了引导解析器,指明运算符附近是一个表达式。以下是三种经典方式 :

+function () {     
};  
  
(function () {  
});  
  
void function() {  
}; 

然后,在末尾加个();函数表达式通过 末尾的() 来调用并运行,就不会报错了。就是一个IIFE。

+function () {   
}();  
  
(funtion () {  
})();  

四:使用IIFE的好处

4.1、减少作用域查找。使用IIFE的一个微小的性能优势是通过匿名函数的参数传递常用全局对象window、document、jQuery,在作用域内引用这些全局对象。JavaScript解释器首先在作用域内查找属性,然后一直沿着链向上查找,直到全局范围。将全局对象放在IIFE作用域内提升js解释器的查找速度和性能。

//传递全局对象到IIFE的一段代码示例
// Anonymous function that has three arguments  
function(window, document, $) {  
  
  // You can now reference the window, document, and jQuery objects in a local scope  
  
}(window, document, window.jQuery); // The global window, document, and jQuery objects are passed into the anonymous function  
//

4.2、有利于压缩。另一个微小的优势是有利于代码压缩。既然通过参数传递了这些全局对象,压缩的时候可以将这些全局对象匿名为一个字符的变量名(只要这个字符没有被其他变量使用过)。如果上面的代码压缩后会变成这样:

// Anonymous function that has three arguments  
function(w, d, $) {  
  
  // You can now reference the window, document, and jQuery objects in a local scope  
  
}(window, document, window.jQuery); // The global window, document, and jQuery objects are passed into the anonymous function 

4.3、避免全局命名冲突。当使用jQuery的时候,全局的window.jQuery对象 作为一个参数传递给$,在匿名函数内部你再也不需要担心$和其他库或者模板申明冲突

4.4、通过传参的方式,可以灵活的加载第三方插件。

+function( KindEditor){  
  
    var editor  
    if(KindEditor){  
        KindEditor.ready(function(K) {  
  
            editor = K.create('textarea[data-name="kindeditor"]', {  
            resizeType : 1  
            })  
        })  
    }  
  
}(KindEditor || undefined)  

4.5结合IIFE的最佳实践,更好的写法是,立即执行document ready

+function ($) {  
  
  $(function(){  
  
  })  
  
}(window.jQuery)  

4.6最佳实践

// IIFE - Immediately Invoked Function Expression  
  +function(yourcode) {  
  
    // The global jQuery object is passed as a parameter  
    yourcode(window.jQuery, window, document);  
  
  }(function($, window, document) {  
  
    // The $ is now locally scoped   
  
   // Listen for the jQuery ready event on the document  
   $(function() {  
  
     // The DOM is ready!  
  
   })); 

 

五:自执行匿名函数剖析

 

(function( window, undefined ) {
    // code
})(window);
1.自执行匿名函数写法的好处:防止变量名冲突
2.为什么传入window:这样传入window可将其从全局变量变为局部变量,在函数作用域内可以直接访问到window,就不用将作用域链退回到顶层作用域了。
3.为什么增加参数undefined:由于undefined在一些情况下有可能会被重写,为确保在自执行匿名函数里的undefined是"真的undefined",就需要增加参数undefined。


总结:

1.虽然叫自执行匿名函数,不要让自执行迷惑了。认为是自己执行!比如我开始进行代码实验的时候写过下面的代码

 

 

//实验一
<script type="text/javascript">
            (function(){
                    alert(1)
            })()
</script>
//实验二
<script type="text/javascript">
            (function(){
                              function foo(){
                    alert(1)
                           }
            })()
</script>

 

实验二是不会弹出1的。。。。。

2.结合目前项目中的js,很少用到这种样式,只有在写插件的时候才这样使用。

所以,到目前为止,先了解了他的原理,如果需要可以回来反复仔细的理解上述的 方法!!

 

posted @ 2017-01-26 12:01  拐进web的奋斗者  阅读(420)  评论(0编辑  收藏  举报