JavaScript   发布时间:2022-04-16  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了详解JavaScript的闭包、IIFE、apply、函数与对象大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

目录

一、闭包(Closure)

1.1、闭包相关的问题

1.2、理解闭包

二、对象

2.1、对象常量(字面量)

2.2、取值

2.3、枚举(遍历)

2.4、更新与添加

2.5、对象的原型

2.6、删除

2.7、封装

三、函数

3.1、参数对象 (arguments)

3.2、构造函数

3.3、函数调用

3.3.1、call

3.3.2、apply

3.3.3、caller

3.3.4、Callee

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

3.5.1、匿名函数与匿名对象

3.5.2、函数与函数表达式

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

3.5.4、各种IIFE的写法

3.5.5、参数

3.5.6、添加分号

3.5.7、IIFE的作用

3.5.8、IIFE的变形

四、示例下载

一、闭包(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

运行结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

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

修改后代码:

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

运行结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

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

1.2、理解闭包

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

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

二、对象

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

2.1、对象常量(字面量)

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

对象的定义:

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

对象中可包含的内容:

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

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

输出:大家好,我是lucy

2.2、取值

{"nick name":"pig",realname:"Rose"}; console.log(obj6.realName); //console.log(obj6.nick Name); 错误

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

{"nick name":"pig",realname:"Rose"}; console.log(obj6["realname"]); console.log(obj6["nick name"]);

2.3、枚举(遍历)

方法一:

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

运行结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

输出顺序是不能保证的。

2.4、更新与添加

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

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

输出:

Queen,1000

{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();

输出:

详解JavaScript的闭包、IIFE、apply、函数与对象

2.5、对象的原型

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

详解JavaScript的闭包、IIFE、apply、函数与对象

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

ject.beget !== "function") { Object.create = function(o) { //构造函数,用于创建对象 var F = function() {}; //指定由构造函数创建的对象的原型 F.prototype = o; //调用构造方法创建新对象 return new F(); } } var rose={ name:"rose",show:function(){ console.log("姓名:"+this.Name); } }; rose.show(); //输出 var lucy=Object.create(rosE); //简单认为是:创建一个对象且继承rose lucy.name="lucy"; //重写 lucy.show();

运行结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

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

ject.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();

结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

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

2.6、删除

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

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

2.7、封装

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

"+Name); console.log(++i); } //i是全局的 2 show(); //3 show();

详解JavaScript的闭包、IIFE、apply、函数与对象

封装后:

"+this.Name); console.log(++i); } }; }; var bar1=bar(); //2 bar1.show(); //3 bar1.show(); var bar2=bar(); //2,因为被封装,且闭包,i是局部私有的 bar2.show();

运行结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

三、函数

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

详解JavaScript的闭包、IIFE、apply、函数与对象

3.1、参数对象 (arguments)

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

{ var sum=0; for(var i=0;i

运行结果:

1205

0

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

运行结果:

3

0

3.2、构造函数

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

rose.show();
jack.show();

详解JavaScript的闭包、IIFE、apply、函数与对象

3.3、函数调用

3.3.1、call

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

call([thisObj[,args])

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

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

示例:

运行结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

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

示例:

结果:

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,var3])

同时使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入

示例代码:

运行结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

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

结果:

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属性是对调用当前函数的函数的引用

运行结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

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

结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

3.3.4、Callee

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

运行结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

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

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

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

3.5.1、匿名函数与匿名对象

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

匿名对象:

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

3.5.2、函数与函数表达式

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

a)、函数定义(Function Declaration)

function Identifier ( Parameters ){ FunctionBody }

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

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

代码:

结果:

详解JavaScript的闭包、IIFE、apply、函数与对象

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

function Identifier(Parameters){ FunctionBody }

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

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

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

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

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

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

IIFE