1-5 立即调用表达式
本节编程练习不计算学习进度,请电脑登录imooc.com操作

立即调用表达式

任何库与框架设计的第一个要点就是解决命名空间与变量污染的问题。jQuery就是利用了JavaScript函数作用域的特性,采用立即调用表达式包裹了自身的方法来解决这个问题。

jQuery的立即调用函数表达式的写法有三种:

写法1:

(function(window, factory) {
    factory(window)
}(this, function() {
    return function() {
       //jQuery的调用
    }
}))

可以看出上面的代码中嵌套了2个函数,而且把一个函数作为参数传递到另一个函数中并且执行,这种方法有点复杂,我们简化一下写法:

写法2:

var factory = function(){
    return function(){
        //执行方法
    }
}
var jQuery = factory();

上面的代码效果和方法1是等同的,但是这个factory有点变成了简单的工厂方法模式,需要自己调用,不像是一个单例的jQuery类,所以我们需要改成“自执行”,而不是另外调用。

写法3:

(function(window, undefined) {
    var jQuery = function() {}
    // ...
    window.jQuery = window.$ = jQuery;
})(window);

从上面的代码可看出,自动初始化这个函数,让其只构建一次。详细说一下这种写法的优势:

  1、window和undefined都是为了减少变量查找所经过的scope作用域。当window通过传递给闭包内部之后,在闭包内部使用它的时候,可以把它当成一个局部变量,显然比原先在window scope下查找的时候要快一些。
  2、undefined也是同样的道理,其实这个undefined并不是JavaScript数据类型的undefined,而是一个普普通通的变量名。只是因为没给它传递值,它的值就是undefined,undefined并不是JavaScript的保留字。

 

有童鞋留言到,为什么要传递undefined?

Javascript 中的 undefined 并不是作为关键字,因此可以允许用户对其赋值。

我们看一个

var undefined = '慕课网'
;(function(window) {
  alert(undefined);//IE8 '慕课网'
})(window)

IE8存在这个问题,当然,大部分浏览器都是不能被修改的

如果函数调用不传递,参数默认就是undefined

;(function(window,undefined) {
    //undefined
})(window)

 


jQuery为什么要创建这样的一个外层包裹,其原理又是如何?

这里要区分2个概念一个是匿名函数,一个是自执行。顾名思义,匿名函数,就是没有函数名的函数,也就是不存在外部引用。但是是否像下面代码实现呢:

function(){
//代码逻辑
}

上面这种写法是错了,声明了它但是又不给名字又没有使用,所以在语法上错误的,那么怎么去执行一个匿名的函数呢?
要调用一个函数,我们必须要有方法定位它、引用它。所以,我们要取一个名字:

var jQuery = function(){
//代码逻辑
}

jQuery使用()将匿名函数括起来,然后后面再加一对小括号(包含参数列表),那么这小括号能把我们的表达式组合分块,并且每一块(也就是每一对小括号),都有一个返回值。这个返回值实际上也就是小括号中表达式的返回值。所以,当我们用一对小括号把匿名函数括起来的时候,实际上小括号返回的,就是一个匿名函数的Function对象。因此,小括号对加上匿名函数就如同有名字的函数般被我们取得它的引用位置了。所以如果在这个引用变量后面再加上参数列表,就会实现普通函数的调用形式。

最后,我们回到写法1看看jQuery利用写法3的写法,然后把整个函数作为参数传递给另外一个函数,主要是为了判断jQuery在不同平台的下的加载逻辑,主流的库一般都有对 AMD 和 CommonJS 的支持代码,看看jQuery的代码:

if (typeof module === "object" && typeof module.exports === "object") {
    module.exports = global.document ?
        factory(global, true) :
        function(w) {
            if (!w.document) {
                throw new Error("jQuery requires a window with a document");
            }
            return factory(w);
    };
} else {
    factory(global);
}

总结:全局变量是魔鬼, 匿名函数可以有效的保证在页面上写入JavaScript,而不会造成全局变量的污染,通过小括号,让其加载的时候立即初始化,这样就形成了一个单例模式的效果从而只会执行一次

任务

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. <style>
  6.  
  7. </style>
  8. <script src="http://img1.sycdn.imooc.com//down/540812440001e40e00000000.js" type="text/javascript"></script>
  9. <title>匿名函数</title>
  10. </head>
  11.  
  12. <div id="target1">
  13. 点击测试1
  14. </div>
  15.  
  16. <div id="target2">
  17. 点击测试2
  18. </div>
  19.  
  20. <div id="target3">
  21. 点击测试3
  22. </div>
  23.  
  24. <div id="target4">
  25. 点击测试4
  26. </div>
  27.  
  28. <script>
  29.  
  30. function oClick1(){
  31. ajQuery1();
  32. function ajQuery1(){
  33. show('ajQuery1')
  34. }
  35. ajQuery1();
  36. }
  37.  
  38. function oClick2(){
  39. try{
  40. ajQuery2();
  41. }catch(e){
  42. show('oClick2,ajQuery2会报错,未定义,命名函数表达式不会预先提升')
  43. }
  44. var ajQuery2 = function(){
  45. show('ajQuery2')
  46. }
  47. ajQuery2();
  48. }
  49.  
  50. function oClick3(){
  51. ;(function(){
  52. show('ajQuery3')
  53. })()
  54. }
  55.  
  56. function oClick4(){
  57. var ajQuery4 = function(){
  58. show('ajQuery4')
  59. }();
  60. }
  61.  
  62.  
  63. $("#target1").click(oClick1);
  64. $("#target2").click(oClick2);
  65. $("#target3").click(oClick3);
  66. $("#target4").click(oClick4);
  67.  
  68.  
  69.  
  70. function show(data) {
  71. $("body").append('<li>' + data + '</li>')
  72. }
  73.  
  74. </script>
  75. </body>
  76. </html>
下一节