博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript学习总结(三)——闭包、IIFE、原型、函数与对象
阅读量:6079 次
发布时间:2019-06-20

本文共 19637 字,大约阅读时间需要 65 分钟。

一、闭包(Closure)

1.1、闭包相关的问题

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

示例代码:

            
闭包
a
b
c
d
e
f
g
h
i
j

运行结果:

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

修改后的代码:

            
闭包
a
b
c
d
e
f
g
h
i
j

运行结果:

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

1.2、理解闭包

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

示例1:

            
闭包

运行结果:

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

定义:闭包是指可以访问另一个函数作用域变量的函数,一般是定义在外层函数中的内层函数。闭包就是能够读取其他函数内部变量的函数定义在一个函数内部的函数。

作用:局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。

特点:占用更多内存;不容易被释放

用法:变量既想反复使用,又想避免全局污染如何使用?

  • 1.定义外层函数,封装被保护的局部变量。
  • 2.定义内层函数,执行对外部函数变量的操作。
  • 3.外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。

示例2:

var getNum;function getCounter() {var n = 1; var inner = function () { return n++; }return inner;}getNum = getCounter();console.log(getNum());console.log(getNum());

结果:1 2

示例3:

function f1(){    var n=999;    nAdd=function(){n+=1}    function f2(){      alert(n);    }    return f2;  }  var result=f1();  result(); // 999  nAdd();  result(); // 1000

结果:999 1000

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

1.3、闭包测试

如果你能理解下面三段代码的运行结果,应该就算理解闭包的运行机制了。

代码片段一:

  var name = "The Window";  var object = {    name : "My Object",    getNameFunc : function(){      return function(){        return this.name;      };    }  };  alert(object.getNameFunc()());

代码片段二:

  var name = "The Window";  var object = {    name : "My Object",    getNameFunc : function(){      var that = this;      return function(){        return that.name;      };    }  };  alert(object.getNameFunc()());

示例三:

            
闭包

h2点击我看看

h2点击我看看

h2点击我看看

h2点击我看看

h2点击我看看

结果:

1.4、小结

闭包就是使用外部函数内部变量的函数

注意事项:

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

二、对象

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

2.1、对象常量(字面量)

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

对象的定义:

//空对象        var obj1={};                //对象中的属性        var obj2={name:"foo",age:19};        var obj3={"nick name":"dog"};                //对象中的方法        var obj4={            price:99,            inc:function(){                this.price+=1;            }        }

对象中可包含的内容:

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

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

输出:大家好,我是lucy

2.2、取值

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

//3取值            var obj6={"nick name":"pig",realname:"Rose"};            console.log(obj6.realname);            //console.log(obj6.nick name);  错误

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

//3取值            var obj6={"nick name":"pig",realname:"Rose"};                        console.log(obj6["realname"]);            console.log(obj6["nick name"]);

2.3、枚举(遍历)

示例一:

var obj7={weight:"55Kg","nick name":"pig",realname:"Rose"};                        for (var key in obj7) {                console.log(key+":"+obj7[key]);            }

运行结果:

输出顺序是不能保证的。

示例二:

            
对象

对象

View Code

结果:

2.4、更新与添加

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

var obj8={realname:"King"};            obj8.realname="Queen";  //修改            obj8.weight=1000;  //添加属性            obj8.show=function()  //添加方法            {                console.log(this.realname+","+this.weight);            }            obj8.show();

输出:

Queen,1000

var obj8={realname:"King"};            obj8.realname="Queen";  //修改            obj8.weight=1000;  //添加属性            obj8.show=function()  //添加方法            {                console.log(this.realname+","+this.weight);            }            obj8.show();                        //引用            var obj9=obj8;   //obj9指向obj8的引用            obj9.realname="Jack";            obj8.show();

输出:

2.5、对象的原型

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

创建对象的三种方法:

            
View Code

理解Object,Function,prototype,__proto__,constractor(构造方法)之间的关系非常重要。

通过修改原型实现扩展方法,对象的prototype是不允许直接访问的,可以使用__proto__访问:

            
对象

原型

结果:

示例2:

            
View Code

结果:

javascript对象藏宝图:

在JavaScript中,原型也是一个对象,通过原型可以实现对象的属性继承,JavaScript的对象中都包含了一个"Prototype"内部属性,这个属性所对应的就是该对象的原型。

"Prototype"作为对象的内部属性,是不能被直接访问的。所以为了方便查看一个对象的原型,Firefox和Chrome中提供了"__proto__"这个非标准(不是所有浏览器都支持)的访问器(ECMA引入了标准对象原型访问器"Object.getPrototype(object)")。

(1)、所有构造器/函数的__proto__都指向Function.prototype,它是一个空函数(Empty function)

(2)、所有对象的__proto__都指向其构造器的prototype

(3)、对于所有的对象,都有__proto__属性,这个属性对应该对象的原型

(4)、对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)

使用原型实现继承:

            
对象

原型

结果:

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

            
对象

原型

运行结果:

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

var lucy=Object.create(rose);  //简单认为是:创建一个对象且继承rose            lucy.name="lucy";  //重写                        var jack=Object.create(rose);            jack.name="jack";                        //修改原型中的方法            rose.show=function(){                console.log("姓名->"+this.name);            }                        lucy.show();            jack.show();

结果:

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

2.6、删除

//删除属性            delete mark.name;               //调用方法,输出:姓名:undefined            mark.show();                         //删除函数            delete mark.show;              //错误,mark.show is not a function            mark.show();

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

2.7、封装

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

封装前:

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

封装后:

//对外只暴露bar,使用闭包封装            var bar=function(){                var i=1;                return{                    name:"bar",                    show:function(){                        console.log("name->"+this.name);                        console.log(++i);                    }                };            };                        var bar1=bar();            //2            bar1.show();            //3            bar1.show();                        var bar2=bar();            //2,因为被封装,且闭包,i是局部私有的            bar2.show();

运行结果:

2.8、对象的浅拷贝与深拷贝

浅拷贝和深拷贝都是对于JS中的引用类型而言的,浅拷贝就只是复制对象的引用(堆和栈的关系,原始(基本)类型Undefined,Null,Boolean,Number和String是存入堆,直接引用,object array 则是存入桟中,只用一个指针来引用值),如果拷贝后的对象发生变化,原对象也会发生变化。只有深拷贝才是真正地对对象的拷贝。

2.8.1、浅拷贝

默认是浅拷贝,只是将地址进行了复制,示例如下:

            

结果:pig

2.8.2、深拷贝

深拷贝就是对目标的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也都复制了。

只要进行了深拷贝,不会相互影响。

实现深拷贝的方法主要是三种:

1、利用 JSON 对象中的 parse 和 stringify

就是说如果对象中含有一个函数时(很常见),就不能用这个方法进行深拷贝

var obj1 = {                name: "cat",                show:function(){                    console.log(this.name);                }            };            var obj2 = JSON.parse(JSON.stringify(obj1));            obj2.name = "pig";            console.log(obj1.name);            console.log(obj2.name);                        obj1.show();            obj2.show();  //函数被丢失

结果:

2、利用递归来实现每一层都重新创建对象并赋值

            

运行结果:

3、ES6 中 引入了Object.assgn 方法展开运算符也能实现对对象的拷贝。

赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值;

JavaScript 中数组和对象自带的拷贝方法都是“首层浅拷贝”;

JSON.stringify 实现的是深拷贝,但是对目标对象有要求(非 undefined,function);

若想真正意义上的深拷贝,请递归。

三、函数

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

属性

arguments[]
一个参数数组,元素是传递给函数的参数。反对使用该属性。
caller
对调用当前函数的Function对象的引用,如果当前函数由顶层代码调用,这个属性的值为null。反对使用该属性。
length
在声明函数时指定的命名参数的个数。
prototype
一个对象,用于构造函数,这个对象定义的属性和方法由构造函数创建的所有对象共享。

方法

apply( )
将函数作为指定对象的方法来调用,传递给它的是指定的参数数组。
call( )
将函数作为指定对象的方法来调用,传递给它的是指定的参数。
toString( )
返回函数的字符串表示。

描述

函数是JavaScript的一种基本数据类型。注意,虽然可以用这里介绍的Function()构造函数创建函数对象, 但这样做效率不高,在大多数情况下,建议使用函数定义语句或函数直接量来定义函数。

在JavaScriptl.1及以后版本中,函数主体会被自动地给予一个局部变量arguments,它引用一个Arguments对象。该对象是一个数组,元素是传递给函数的参数值。

3.0、Function定义

Function实际上是对象,与其他引用类型一样具有属性和方法。Function可以通过三种方法进行定义,分别是函数声明语法定义,函数表达式定义和Function构造函数定义。

1.函数声明语法定义

function functionName(value1...){    //函数体}

 

2.函数表达式定义

var functionName = function(value1...){    //函数体}

3.Function构造函数定义

通过Function构造函数创建函数,可向构造函数中传入任意数量的参数,但值得注意的是传入的最后一个参数会作为函数体,而其他参数则作为参数传入函数中。用该方法去定义函数是不推荐使用的,因为该语法会导致解析两次代码,第一次解析常规ECMAScript代码,第二次解析传入构造函数的字符串,影响性能。

var functionName = new Function("value",...,"函数体");

示例:

var f2=new Function("n1","n2","return n1+n2;");            console.log(f2(1,2));

结果:3

注:函数是引入值类型,所以函数名仅仅是指向函数的指针,当使用函数名去赋值给另一个变量名时,仅仅复制的是一个指针。即在下列a设置为null时,仅将a存的指针消除而已,不会影响b调用函数。

var a = b = function(value1){    return value1;}a = null;b(1);

3.1、参数对象 (arguments)

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

运行结果:

1205

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

function f1()            {                console.log(arguments.length);                f2=function()                {                    console.log(arguments.length);                }                return f2;            }                        var f=f1(1,2,3);            f();

运行结果:

3

0

3.2、构造函数

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

3.3、函数调用

3.3.1、call

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

call([thisObj[,args])

hisObj 可选项。将被用作当前对象的对象。args 将被传递方法参数序列。

call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。

示例:

/*构造函数*/           function Student(name,age){                 this.name=name;                 this.age=age;           }                       show=function(add){                     console.log(add+":"+this.name+","+this.age);               }                      //通过new关键字调用构造函数,创建一个对象tom           var rose=new Student("rose",18);           var jack=new Student("jack",20);                    //调用show方法,指定上下文,指定调用对象,this指向rose,“大家好是参数”          show.call(rose,"大家好");          show.call(jack,"Hello");

运行结果:

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

示例:

var name="无名";          var age=18;          show.call();

结果:

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){                 this.name=name;                 this.age=age;           }                       show=function(greeting,height){                     console.log(greeting+":"+this.name+","+this.age+","+height);               }                      //通过new关键字调用构造函数,创建一个对象tom           var rose=new Student("rose",18);           var jack=new Student("jack",20);                    //调用show方法,指定上下文,指定调用对象,this指向rose,“大家好是参数”          show.apply(rose,["大家好","178cm"]);          show.apply(jack,["Hello","188cm"]);

运行结果:

 

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

function display(){             show.apply(jack,arguments);          }          display("hi","224cm");

结果:

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()        {            console.log("add被调用");            //add方法的调用函数,如果调用add方法的不是函数则为null            console.log(add.caller);        }                function calc(){            add();        }                //直接调用add方法        add();         //间接通过calc方法调用        calc();

运行结果:

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

结果:

3.3.4、Callee

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

function add(n1,n2){                  console.log(n1+n2);                  //arguments.callee(n1,n2);  //指向add方法                  return arguments.callee;           }                      add(1,2)(3,4)(5,6)(7,8)(8,9);

运行结果:

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

3.4、length

在声明函数时指定的命名参数的个数。

示例:

            
Function

Function - length

结果:

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

IIFE即Immediately-Invoked Function Expression,立即执行函数表达式,在 JavaScript 中每个函数被调用时,都会创建一个新的执行上下文。因为在函数里定义的变量和函数是唯一在内部被访问的变量,而不是在外部被访问的变量,当调用函数时,函数提供的上下文提供了一个非常简单的方法创建私有变量。

3.5.0、块级作用域与函数作用域

javascript没有块级作用域但有函数级作用域,可以使用IIFE模拟块级作用域。

任何一对花括号中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。

作用域永远都是任何一门编程语言中的重中之重,因为它控制着变量与参数的可见性与生命周期。

块级作用域:任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。

与函数作用域:函数作用域就好理解了,定义在函数中的参数和变量在函数外部是不可见的。

示例:

结果:

你想的结果:

实际结果:

解决方法,模拟一个块级作用域:

结果:

 

3.5.1、匿名函数与匿名对象

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

function(){            console.log("这是一个匿名函数");        };

匿名对象:

{            name:"foo",            show:function(){                console.log(this.name);            }        }

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

3.5.2、函数与函数表达式

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

a)、函数定义(Function Declaration)

function Identifier ( Parameters ){ FunctionBody }

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

在函数定义中,函数名称是必不可少的,如果遗漏,会报提示错误:

代码:

function(){            console.log("这是一个匿名函数");        };

结果:

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

function Identifier(Parameters){ FunctionBody }

函数表达式中,参数和标识符都是可选的,与函数定义的区别是标识符可省去。

其实,"function Identifier(Parameters){ FunctionBody }"并不是一个完整的函数表达式,完整的函数的表达式,需要一个赋值操作。

比如: var name=function Identifier(Parameters){ FunctionBody }

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

//1 正常定义函数            function f1(){                console.log("正常定义f1函数");            };                        //2 被误解的函数表达式            function(){                console.log("报错Unexpected token (");            }();                        //3 IIFE,括号中的内容被解释成函数表达式            (function(){                console.log("IIFE,正常执行");            })();                        //4 函数表达式            var f2=function(){                console.log("这也被视为函数表达式");            };

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

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

            
IIFE

 

运行结果:

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);            })(100);

输出:100 

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

(function(win,undfd){                win.console.log("Hello"==undfd);            })(window,undefined);

3.5.6、添加分号

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

var k=100            (function (n){                console.log(n);            })(k);

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

var k=100            ;(function (n){                console.log(n);            })(k);

 

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

3.5.7、IIFE的作用

1)、提高性能

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

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

2)、压缩空间

通过参数传递全局对象,压缩时可以将这些全局对象匿名为一个更加精简的变量名

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) {        return new Student(name);    }}/*构造函数*/function Student(name) {    this.name = name;    this.show = function() {        console.log("Hello," + this.name);    }}

Common.js

function other1() {}function other2() {}(function($) {    if($) {        $.getStu("Tom").show();    }})(typeof StuObj=="undefined"?false:StuObj);

A.HTML

            
A

B.HTML

            

3.5.8、IIFE的变形

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

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

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

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

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

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

bootstrap的写法:

+function(yourcode) {                yourcode(window.jQuery, window, document);            }(function($, window, document) {                    $(function() {});  //jQueryDOM加载完成事件              });

结合call或apply的写法:

(function(x){console.log(x)}).call(window,888);              (function(x){console.log(x)}).apply(window,[999]);

输出:888 999

四、示例下载

五、视频

六、作业

6.1、请扩展String类型增加trim方法,实现去掉字符串首尾空格,如:

“   abc   ”.trim();  //abc

6.2、请定义一个动物(Animal)类型,并定义属性(name名称,food食物),定义方法eat吃,在方法中输出“小狗喜欢吃骨头!”

定义猫与狗类型,继承自Animal,增加show方法显示名称与喜欢的食物,完成测试。

 

 

 

转载地址:http://muhgx.baihongyu.com/

你可能感兴趣的文章
[汇编] 比较2个字符串是否相等
查看>>
代码重构~代码注释
查看>>
C#实现二叉查找树
查看>>
Angular 4 http通讯 解决服务器参数无法接收问题
查看>>
rose分析
查看>>
最长递减子序列(nlogn)(个人模版)
查看>>
springMVC拦截配置
查看>>
将不确定变为确定~LINQ查询包含对不同数据上下文上所定义项的引用
查看>>
Cmockery macro demo hacking
查看>>
Android NIO(Noblocking I/O非阻塞I/O)小结
查看>>
为什么机器学习难于应用
查看>>
算法: skiplist 跳跃表代码实现和原理
查看>>
ELK系列~对fluentd参数的理解
查看>>
WF4.0实战(二):超市收银软件
查看>>
CSS3与页面布局学习总结(七)——前端预处理技术(Less、Sass、CoffeeScript、TypeScript)...
查看>>
思考与总结:扇区和磁盘块的区别是什么
查看>>
C++ 数据结构链表的实现代码
查看>>
OVS local network 连通性分析 - 每天5分钟玩转 OpenStack(132)
查看>>
Django 表单校验 表单字段设置 自定义表单校验规则
查看>>
TeamViewer“试用期已到期”解决方法
查看>>