JavaScript学习笔记(二)——闭包、IIFE、apply、函数与对象

一、闭包(Closure)

1.1、闭包相关的问题

请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9;方法:找到所有的div,for循环绑定事件。

示例代码:

<!DOCTYPE html>
<html>

&lt;head&gt;<br/>
    &lt;meta charset=&#34;UTF-8&#34;&gt;<br/>
    &lt;title&gt;闭包&lt;/title&gt;<br/>
    &lt;style type=&#34;text/css&#34;&gt;<br/>
        div {<br/>
            width: 100px;<br/>
            height: 100px;<br/>
            background: lightgreen;<br/>
            float: left;<br/>
            margin: 20px;<br/>
            font: 30px/100px &#34;microsoft yahei&#34;;<br/>
            text-align: center;<br/>
        }<br/>
    &lt;/style&gt;<br/>
&lt;/head&gt;<br/>
&lt;body&gt;<br/>
    &lt;div&gt;a&lt;/div&gt;<br/>
    &lt;div&gt;b&lt;/div&gt;<br/>
    &lt;div&gt;c&lt;/div&gt;<br/>
    &lt;div&gt;d&lt;/div&gt;<br/>
    &lt;div&gt;e&lt;/div&gt;<br/>
    &lt;div&gt;f&lt;/div&gt;<br/>
    &lt;div&gt;g&lt;/div&gt;<br/>
    &lt;div&gt;h&lt;/div&gt;<br/>
    &lt;div&gt;i&lt;/div&gt;<br/>
    &lt;div&gt;j&lt;/div&gt;<br/>
    &lt;script type=&#34;text/javascript&#34;&gt;<br/>
        var divs=document.getElementsByTagName(&#34;div&#34;);<br/>
        for (var i=0;i&lt;divs.length;i++) {<br/>
            divs[i].onclick=function(){<br/>
                alert(i);<br/>
            }<br/>
        }<br/>
    &lt;/script&gt;<br/>
&lt;/body&gt;<br/>

&lt;/html&gt;

运行结果:

因为点击事件的函数内部使用外部的变量i一直在变化,当我们指定click事件时并没有保存i的副本,这样做也是为了提高性能,但达不到我们的目的,我们要让他执行的上下文保存i的副本,这种机制就是闭包。

修改后的代码:

&lt;!DOCTYPE html&gt;
&lt;html&gt;

&lt;head&gt;<br/>
    &lt;meta charset=&#34;UTF-8&#34;&gt;<br/>
    &lt;title&gt;闭包&lt;/title&gt;<br/>
    &lt;style type=&#34;text/css&#34;&gt;<br/>
        div {<br/>
            width: 100px;<br/>
            height: 100px;<br/>
            background: lightgreen;<br/>
            float: left;<br/>
            margin: 20px;<br/>
            font: 30px/100px &#34;microsoft yahei&#34;;<br/>
            text-align: center;<br/>
        }<br/>
    &lt;/style&gt;<br/>
&lt;/head&gt;<br/>
&lt;body&gt;<br/>
    &lt;div&gt;a&lt;/div&gt;<br/>
    &lt;div&gt;b&lt;/div&gt;<br/>
    &lt;div&gt;c&lt;/div&gt;<br/>
    &lt;div&gt;d&lt;/div&gt;<br/>
    &lt;div&gt;e&lt;/div&gt;<br/>
    &lt;div&gt;f&lt;/div&gt;<br/>
    &lt;div&gt;g&lt;/div&gt;<br/>
    &lt;div&gt;h&lt;/div&gt;<br/>
    &lt;div&gt;i&lt;/div&gt;<br/>
    &lt;div&gt;j&lt;/div&gt;<br/>
    &lt;script type=&#34;text/javascript&#34;&gt;<br/>
        var divs=document.getElementsByTagName(&#34;div&#34;);<br/>
        for (var i=0;i&lt;divs.length;i++) {<br/>
            divs[i].onclick=(function(n){<br/>
                return function(){<br/>
                    alert(n);<br/>
                }<br/>
            })(i);<br/>
        }<br/>
    &lt;/script&gt;<br/>
&lt;/body&gt;<br/>

&lt;/html&gt;

运行结果:

n是外部函数的值,但是内部函数(点击事件)需要使用,返回函数前的n被临时驻留在内存中给点击事件使用,简单说就是函数的执行上下文被保存起来,i生成了多个副本。

1.2、理解闭包

闭包概念:当一个内部函数被调用,就会形成闭包,闭包就是能够读取其他函数内部变量的函数,定义在一个函数内部的函,创建一个闭包环境,让返回的这个子程序抓住i,以便在后续执行时可以保持对这个i的引用。内部函数比外部函数有更长的生命周期;函数可以访问它被创建时所处的上下文环境。

Javascript语言特有的“链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量

二、对象

对象就是“键/值”对的集合并拥有一个连接到原型(prototype)对隐藏连接。

2.1、对象常量(字面量)

一个对象字面量就是包含在一对花括号中的零个或多个“键/值”对。对象字面量可以出现在任何允许表达式出现的地方。

对象的定义:

        //空对象

    var obj1={};

//对象中的属性

    var obj2={name:&#34;foo&#34;,age:19};<br/>
    var obj3={&#34;nick name&#34;:&#34;dog&#34;};

//对象中的方法

    var obj4={<br/>
        price:99,<br/>
        inc:function(){<br/>
            this.price+=1;<br/>
        }<br/>
    }</pre>

对象中可包含的内容:

对象常量可以出现在任何允许表达式出现的地方,对象、数组、函数可以相互间嵌套,形式可以多种多样。对象的值可以是:数组,函数,对象,基本数据类型等。

            //对象中可包含的内容

        var obj5 = [{<br/>
            name: &#34;jack&#34;<br/>
        }, {<br/>
            name: &#34;lucy&#34;,  //常量<br/>
            hobby:[&#34;读书&#34;,&#34;上网&#34;,&#34;代码&#34;],  //数组<br/>
            friend:{name:&#34;mark&#34;,height:198,friend:{}},  //对象<br/>
            show:function(){  //函数<br/>
                console.log(&#34;大家好,我是&#34;+this.name);<br/>
            }<br/>
        }];<br/>
        //对象中的this是动态的,指向的是:调用者<br/>
        obj5[1].show();</pre>

输出:大家好,我是lucy

2.2、取值

方法一:直接使用点号运算

            //3取值

        var obj6={&#34;nick name&#34;:&#34;pig&#34;,realname:&#34;Rose&#34;};<br/>
        console.log(obj6.realname);<br/>
        //console.log(obj6.nick name);  错误</pre>

方法二:使用索引器,当对象中的key有空格是

            //3取值

        var obj6={&#34;nick name&#34;:&#34;pig&#34;,realname:&#34;Rose&#34;};

console.log(obj6[“realname”]);

        console.log(obj6[&#34;nick name&#34;]);</pre>

2.3、枚举(遍历)

方法一:

            var obj7={weight:“55Kg”,“nick name”:“pig”,realname:“Rose”};
for (var key in obj7) {

            console.log(key+&#34;:&#34;+obj7[key]);<br/>
        }</pre>

运行结果:

输出顺序是不能保证的。

2.4、更新与添加

如果对象中存在属性就修改对应值,如果不存在就添加。对象通过引用传递,它们永远不会被复制

            var obj8={realname:“King”};

        obj8.realname=&#34;Queen&#34;;  //修改<br/>
        obj8.weight=1000;  //添加属性<br/>
        obj8.show=function()  //添加方法<br/>
        {<br/>
            console.log(this.realname+&#34;,&#34;+this.weight);<br/>
        }<br/>
        obj8.show();</pre>

输出:

Queen,1000

            var obj8={realname:“King”};

        obj8.realname=&#34;Queen&#34;;  //修改<br/>
        obj8.weight=1000;  //添加属性<br/>
        obj8.show=function()  //添加方法<br/>
        {<br/>
            console.log(this.realname+&#34;,&#34;+this.weight);<br/>
        }<br/>
        obj8.show();

//引用

        var obj9=obj8;   //obj9指向obj8的引用<br/>
        obj9.realname=&#34;Jack&#34;;<br/>
        obj8.show();</pre>

输出:

2.5、对象的原型

javascript是一种动态语言,与C#和Java这样的静态语言是不一样的;javascript并没有严格的类型,可以简单认为javascript是由对象组成的,对象间连接到原型(prototype)实现功能的扩展与继承。每个对象都链接到一个原型对象,并且可以从中继承属性,所有通过常量(字面量)创建的对象都连接到Object.prototype,它是JavaScript中的顶级(标配)对象,类似高级语言中的根类。

现在我们修改系统中的Object对象,添加一个创建方法,指定要创建对象的原型,实现类似继承功能:

        &lt;script type=“text/javascript”&gt;

        if(typeof Object.beget !== &#34;function&#34;)<br/>
        {<br/>
            Object.create = function(o) {<br/>
                //构造函数,用于创建对象<br/>
                var F = function() {};<br/>
                //指定由构造函数创建的对象的原型<br/>
                F.prototype = o;<br/>
                //调用构造方法创建新对象<br/>
                return new F();<br/>
            }<br/>
        }

var rose={

            name:&#34;rose&#34;,<br/>
            show:function(){<br/>
                console.log(&#34;姓名:&#34;+this.name);<br/>
            }<br/>
        };

rose.show(); //输出 var lucy=Object.create(rose); //简单认为是:创建一个对象且继承rose

        lucy.name=&#34;lucy&#34;;  //重写<br/>
        lucy.show();<br/>
    &lt;/script&gt;</pre>

运行结果:

原型关系是一种动态关系,如果修改原型,该原型创建的对象会受到影响。

            var lucy=Object.create(rose);  //简单认为是:创建一个对象且继承rose

        lucy.name=&#34;lucy&#34;;  //重写

var jack=Object.create(rose);

        jack.name=&#34;jack&#34;;

//修改原型中的方法

        rose.show=function(){<br/>
            console.log(&#34;姓名-&gt;&#34;+this.name);<br/>
        }

lucy.show();

        jack.show();</pre>

结果:

关于原型在函数中会再讲到。

2.6、删除

            //删除属性

        delete mark.name;<br/>
        //调用方法,输出:姓名:undefined<br/>
        mark.show(); 

//删除函数

        delete mark.show;<br/>
        //错误,mark.show is not a function<br/>
        mark.show();</pre>

删除不用的属性是一个好习惯,在某些情况下可能引发内存泄漏。

2.7、封装

使用对象封装的好处是可以减少全局变量的污染机会,将属性,函数都隶属一个对象。

封装前:

var name=“foo”;   //name是全局的,被暴露

        i=1;  //全局的,没有var关键字声明的变量是全局的,与位置关系不大<br/>
        function show(){  //show 是全局的,被暴露<br/>
            console.log(&#34;name-&gt;&#34;+name);<br/>
            console.log(++i);<br/>
        }

//i是全局的 2

        show();<br/>
        //<br/>
        show();</pre>

封装后:

//对外只暴露bar,使用闭包封装

        var bar=function(){<br/>
            var i=1;<br/>
            return{<br/>
                name:&#34;bar&#34;,<br/>
                show:function(){<br/>
                    console.log(&#34;name-&gt;&#34;+this.name);<br/>
                    console.log(++i);<br/>
                }<br/>
            };<br/>
        };

var bar1=bar();

        //<br/>
        bar1.show();<br/>
        //<br/>
        bar1.show();

var bar2=bar();

        //2,因为被封装,且闭包,i是局部私有的<br/>
        bar2.show();</pre>

运行结果:

三、函数

javascript中的函数就是对象,对象就是“键/值”对的集合并拥有一个连接到原型对隐藏连接。

3.1、参数对象 (arguments)

第一个函数中有一个默认对象叫arguments,类似数组,但不是数组,该对象是传递给函数的参数。

        &lt;script type=“text/javascript”&gt;

        function counter(){<br/>
            var sum=0;<br/>
            for(var i=0;i&lt;arguments.length;i++){<br/>
                sum+=arguments[i];<br/>
            }<br/>
            return sum;<br/>
        }

console.log(counter(199,991,1,2,3,4,5));

        console.log(counter());<br/>
    &lt;/script&gt;</pre>

运行结果:

1205

0

这里的arguments是一个隐式对象,不声明也在函数中,内部函数可以访问外部函数的任意内容,但是不能直接访问外部函数的arguments与this对象。

            function f1()

        {<br/>
            console.log(arguments.length);<br/>
            f2=function()<br/>
            {<br/>
                console.log(arguments.length);<br/>
            }<br/>
            return f2;<br/>
        }

var f=f1(1,2,3);

        f();</pre>

运行结果:

3

0

3.2、构造函数

在javascript中对象构造函数可以创建一个对象。

           &lt;script type=“text/javascript”&gt;

       /*构造函数*/<br/>
      //可以简单的认为是一个类型的定义<br/>
       function Student(name,age){<br/>
             this.name=name;<br/>
             this.age=age;<br/>
             this.show=function(){<br/>
                 console.log(this.name+&#34;,&#34;+this.age);<br/>
             }<br/>
       }

//通过new关键字调用构造函数,创建一个对象tom

       var rose=new Student(&#34;rose&#34;,18);<br/>
       var jack=new Student(&#34;jack&#34;,20);

rose.show();

       jack.show();<br/>
    &lt;/script&gt;</pre>

3.3、函数调用

3.3.1、call

调用一个对象的一个方法,以另一个对象替换当前对象

call([thisObj[,args])

hisObj 可选项。将被用作当前对象的对象。args 将被传递方法参数序列。
call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。

示例:

           /构造函数/

       function Student(name,age){<br/>
             this.name=name;<br/>
             this.age=age;<br/>
       }

show=function(add){

                 console.log(add+&#34;:&#34;+this.name+&#34;,&#34;+this.age);<br/>
           }

//通过new关键字调用构造函数,创建一个对象tom

       var rose=new Student(&#34;rose&#34;,18);<br/>
       var jack=new Student(&#34;jack&#34;,20);

//调用show方法,指定上下文,指定调用对象,this指向rose,“大家好是参数”

      show.call(rose,&#34;大家好&#34;);<br/>
      show.call(jack,&#34;Hello&#34;);</pre>

运行结果:

call方法中的参数都可以省去,第1个参数表示在哪个对象上调用该方法,或this指向谁,如果不指定则会指向window对象。

示例:

          var name=“无名”;

      var age=18;<br/>
      show.call();</pre>

结果:

undefined:无名,18

3.3.2、apply

apply([thisObj[,argArray]])
应用某一对象的一个方法,用另一个对象替换当前对象,与call类似。
如果 argArray 不是一个有效的数组或者不是arguments对象,那么将导致一个 TypeError。
如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数。
对于第一个参数意义都一样,但对第二个参数:
apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。
如 func.call(func1,var1,var2,var3)对应的apply写法为:func.apply(func1,[var1,var2,var3])
同时使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入

示例代码:

           /构造函数/

       function Student(name,age){<br/>
             this.name=name;<br/>
             this.age=age;<br/>
       }

show=function(greeting,height){

                 console.log(greeting+&#34;:&#34;+this.name+&#34;,&#34;+this.age+&#34;,&#34;+height);<br/>
           }

//通过new关键字调用构造函数,创建一个对象tom

       var rose=new Student(&#34;rose&#34;,18);<br/>
       var jack=new Student(&#34;jack&#34;,20);

//调用show方法,指定上下文,指定调用对象,this指向rose,“大家好是参数”

      show.apply(rose,[&#34;大家好&#34;,&#34;178cm&#34;]);<br/>
      show.apply(jack,[&#34;Hello&#34;,&#34;188cm&#34;]);</pre>

运行结果:

从上面的示例中可以发现apply的第2个参数是一个数组,数组中的内容将映射到被调用方法的参数中,如果单这样看发现不如call方便,其实如果直接取方法的参数arguments则apply要方便一些。通过简单的变化就可以替代call。

          function display(){

         show.apply(jack,arguments);<br/>
      }<br/>
      display(&#34;hi&#34;,&#34;224cm&#34;);</pre>

结果:

hi:jack,20,224cm

javascript里call和apply操作符可以随意改变this指向
如果在javascript语言里没有通过new(包括对象字面量定义)、call和apply改变函数的this指针,函数的this指针都是指向window的。
关于this指针,我的总结是:是谁调用的函数,那么这个函数中的this指针就是它;如果没有明确看出是谁调用的,那么应该就是window调用的,那么this指针就是window。

3.3.3、caller

在一个函数调用另一个函数时,被调用函数会自动生成一个caller属性,指向调用它的函数对象。如果该函数当前未被调用,或并非被其他函数调用,则caller为null。
在JavaScript的早期版本中,Function对象的caller属性是对调用当前函数的函数的引用

        function add()

    {<br/>
        console.log(&#34;add被调用&#34;);<br/>
        //add方法的调用函数,如果调用add方法的不是函数则为null<br/>
        console.log(add.caller);<br/>
    }

function calc(){

        add();<br/>
    }

//直接调用add方法

    add();<br/>
    //间接通过calc方法调用<br/>
    calc();</pre>

运行结果:

caller与this还是有区别的,this是指调用方法的对象,而caller是指调用函数的函数。

        &lt;script type=“text/javascript”&gt;

    function add(n)<br/>
    {<br/>
        console.log(&#34;add被调用&#34;);<br/>
        if(n&lt;=2){<br/>
            return 1;<br/>
        }<br/>
        return add.caller(n-1)+add.caller(n-2);<br/>
    }

function calc(n){

        console.log(&#34;calc被调用&#34;);<br/>
        return add(n);<br/>
    }

//1 1 2

    console.log(calc(3));<br/>
    &lt;/script&gt;</pre>

结果:

3.3.4、Callee

当函数被调用时,它的arguments.callee对象就会指向自身,也就是一个对自己的引用

           function add(n1,n2){

              console.log(n1+n2);<br/>
              //arguments.callee(n1,n2);  //指向add方法<br/>
              return arguments.callee;<br/>
       }

add(1,2)(3,4)(5,6)(7,8)(8,9);

运行结果:

当第1次调用add方法时输入3,立即将函数返回再次调用,每次调用后又返回自己,这样可以实现链式编程。

3.5、立即执行函数表达式 (IIFE)

IIFE即Immediately-Invoked Function Expression,立即执行函数表达式

3.5.1、匿名函数与匿名对象

匿名函数就是没有名称的函数,javascript中经常会使用匿名函数实现事件绑定,回调,实现函数级的私有作用域,如下所示:

        function(){

        console.log(&#34;这是一个匿名函数&#34;);<br/>
    };</pre>

匿名对象:

        {

        name:&#34;foo&#34;,<br/>
        show:function(){<br/>
            console.log(this.name);<br/>
        }<br/>
    }</pre>

没有名称的匿名函数也叫函数表达式,它们间是有区别的。

3.5.2、函数与函数表达式

下面是关于函数与函数表达式定义时的区别

a)、函数定义(Function Declaration)

function Identifier ( Parameters ){ FunctionBody }

function 函数名称(参数){函数主体}

在函数定义中,参数(Parameters)标识符(Identifier )是必不可少的。如果遗漏,会报提示错误:

代码:

        function(){

        console.log(&#34;这是一个匿名函数&#34;);<br/>
    };</pre>

结果:

b)、函数表达式(Function Expression)

function Identifier(Parameters){ FunctionBody }
函数表达式中,参数和标识符都是可选的,与函数定义的区别是标识符可省去。

其实,“function Identifier(Parameters){ FunctionBody }”并不是一个完整的函数表达式,完整的函数的表达式,需要一个赋值操作。
比如: var name=function Identifier(Parameters){ FunctionBody }

3.5.3、立即执行函数表达式与匿名对象

            //1 正常定义函数

        function f1(){<br/>
            console.log(&#34;正常定义f1函数&#34;);<br/>
        };

//2 被误解的函数表达式

        function(){<br/>
            console.log(&#34;报错Unexpected token (&#34;);<br/>
        }();

//3 IIFE,括号中的内容被解释成函数表达式

        (function(){<br/>
            console.log(&#34;IIFE,正常执行&#34;);<br/>
        })();

//4 函数表达式

        var f2=function(){<br/>
            console.log(&#34;这也被视为函数表达式&#34;);<br/>
        };</pre>

第3种写法为什么这样就能立即执行并且不报错呢?因为在javascript里,括号内部不能包含语句,当解析器对代码进行解释的时候,先碰到了(),然后碰到function关键字就会自动将()里面的代码识别为函数表达式而不是函数声明。

如果需要将函数表达式或匿名对象立即执行,可以使用如下方法:

&lt;!DOCTYPE html&gt;
&lt;html&gt; &lt;head&gt;

    &lt;meta charset=&#34;UTF-8&#34;&gt;<br/>
    &lt;title&gt;IIFE&lt;/title&gt;<br/>
&lt;/head&gt;

&lt;body&gt;

    &lt;script type=&#34;text/javascript&#34;&gt;<br/>
        //调用匿名函数<br/>
        (function() {<br/>
            console.log(&#34;这是一个函数表达式&#34;);<br/>
        })();

//调用匿名对象

        ({<br/>
            name: &#34;foo&#34;,<br/>
            show: function() {<br/>
                console.log(this.name);<br/>
            }<br/>
        }).show();

console.log({

            a: 1<br/>
        }.a);

console.log({

            a: function() {}<br/>
        }.a());<br/>
    &lt;/script&gt;<br/>
&lt;/body&gt;

&lt;/html&gt;

运行结果:

3.5.4、各种IIFE的写法

//最常用的两种写法
(function(){ /* code / }()); // 老师推荐写法
(function(){ /
code / })(); // 当然这种也可以 // 括号和JS的一些操作符(如 = && || ,等)可以在函数表达式和函数声明上消除歧义
// 如下代码中,解析器已经知道一个是表达式了,于是也会把另一个默认为表达式
// 但是两者交换则会报错
var i = function(){ return 10; }();
true && function(){ /
code / }();
0, function(){ /
code / }(); // 如果你不怕代码晦涩难读,也可以选择一元运算符
!function(){ /
code / }();
~function(){ /
code / }();
-function(){ /
code / }();
+function(){ /
code / }(); // 你也可以这样
new function(){ /
code / }
new function(){ /
code */ }() // 带参

如果是函数表达式,可直接在其后加“()”立即执行。

如果是函数声明,可以通过“()”、“+”、“-”、“void”、“new”等运算符将其转换为函数表达式,然后再加“()”立即执行。

3.5.5、参数

函数表达式也是函数的一种表达形式,同样可以像函数一样使用参数,如下所示:

            (function (n){

            console.log(n);<br/>
        })(100);</pre>

输出:100

其实通过IIFE还能形成一个类似的块级作用域,当块内的程序在使用外部对象时将优先查找块内的对象,再查找块外的对象,依次向上。

            (function(win,undfd){

            win.console.log(&#34;Hello&#34;==undfd);<br/>
        })(window,undefined);</pre>

3.5.6、添加分号

为了避免与其它的javascript代码产生影响后报错,常常会在IIFE前增加一个分号,表示前面所有的语句都结束了,开始新的一语句。

            var k=100

        (function (n){<br/>
            console.log(n);<br/>
        })(k);</pre>

上面的脚本会报错,因为javascript解释器会认为100是函数名。

            var k=100

        ;(function (n){<br/>
            console.log(n);<br/>
        })(k);</pre>

这样就正确了,在javascript中一行语句的结束可以使用分号,也可以不使用分号,因为一般的自定义插件会使用IIFE,这是一段独立的代码,在应用过程中不能保证用户会加上分号,所以建议在IIFE前加上分号。

3.5.7、IIFE的作用

1)、提高性能

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

function(window, document, \() {
}(window, document, window.jQuery); </pre>
<p><strong>2)、压缩空间</strong></p>
<p>通过参数传递全局对象,压缩时可以将这些全局对象匿名为一个更加精简的变量名</p>
<pre>function(w, d, \)) {
}(window, document, window.jQuery);

3)、避免冲突

匿名函数内部可以形成一个块级的私有作用域。

4)、依赖加载

可以灵活的加载第三方插件,当然使用模块化加载更好(AMD,CMD),示例如下。

A.html与B.html文件同时引用公用的common.js文件,但是只有A.html需要使用到StuObj对象,B.html不需要,但使用其它方法。

Student.js

var StuObj = {

getStu: function(name) {<br/>
    return new Student(name);<br/>
}<br/>

} /构造函数/
function Student(name) {

this.name = name;<br/>
this.show = function() {<br/>
    console.log(&#34;Hello,&#34; + this.name);<br/>
}<br/>

}

Common.js

function other1() {}
function other2() {}
(function($) {

if($) {<br/>
    $.getStu(&#34;Tom&#34;).show();<br/>
}<br/>

})(typeof StuObj==“undefined”?false:StuObj);

A.HTML

&lt;!DOCTYPE html&gt;
&lt;html&gt;

&lt;head&gt;<br/>
    &lt;meta charset=&#34;UTF-8&#34;&gt;<br/>
    &lt;title&gt;A&lt;/title&gt;<br/>
&lt;/head&gt;<br/>
&lt;body&gt;<br/>
    &lt;script src=&#34;js/Student.js&#34; type=&#34;text/javascript&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;<br/>
    &lt;script src=&#34;js/common.js&#34; type=&#34;text/javascript&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;<br/>
&lt;/body&gt;<br/>

&lt;/html&gt;

B.HTML

&lt;!DOCTYPE html&gt;
&lt;html&gt;

&lt;head&gt;<br/>
    &lt;meta charset=&#34;UTF-8&#34;&gt;<br/>
    &lt;title&gt;&lt;/title&gt;<br/>
&lt;/head&gt;<br/>
&lt;body&gt;<br/>
    &lt;script src=&#34;js/common.js&#34; type=&#34;text/javascript&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;<br/>
    &lt;script type=&#34;text/javascript&#34;&gt;<br/>
        other1();<br/>
    &lt;/script&gt;<br/>
&lt;/body&gt;<br/>

&lt;/html&gt;

3.5.8、IIFE的变形

也许有人会说IIFE将参数放在最后,需要移动到文档的末尾才能看到参数,比较麻烦,那么可以将IIFE变形为如下形式:

        (function(n){

        console.log(n);

//认为这里有30000代码 }(100));

如果中间有很长的代码,参数100只有到文档的末尾才可以看得到,变形后的结果:

        (function(exp){

        exp(100);<br/>
    }(function(n){<br/>
        console.log(n);<br/>
        //认为这里有30000代码<br/>
    }));</pre>

修改后的代码中有两个函数表达式,一个作为参数,就是我们主要要完成的功能向控制台输出数字,另一个作来IIFE立即执行的函数,主要的功能函数变成的IIFE的参数了。

            (function(win, doc, $) {
}(window, document, jQuery));
(

            function(library) {<br/>
                library(window, document, window.jQuery);<br/>
            }<br/>
            (function(window, document, $) {

})

        );</pre>

bootstrap的写法:

            +function(yourcode) {
yourcode(window.jQuery, window, document);
}(function($, window, document) {

                $(function() {});  //jQueryDOM加载完成事件<br/>
          });</pre>

结合call或apply的写法:

              (function(x){console.log(x)}).call(window,888);

          (function(x){console.log(x)}).apply(window,[999]);</pre>

输出:888 999

四、示例下载