详解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循环绑定事件。

示例代码:

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

运行结果:

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

修改后的代码:

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

运行结果:

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

1.2、理解闭包

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

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

二、对象

对象就是“键/值”对的集合并拥有一个连接到原型(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]);
 }

运行结果:

输出顺序是不能保证的。

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中的顶级(标配)对象,类似高级语言中的根类。

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

<script type="text/javascript">
 if(typeof Object.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();
 </script>

运行结果:

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

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

运行结果:

三、函数

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

3.1、参数对象 (arguments)

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

<script type="text/javascript">
 function counter(){
 var sum=0;
 for(var i=0;i<arguments.length;i++){
 sum+=arguments[i];
 }
 return sum;
 }
 console.log(counter(199,991,1,2,3,4,5));
 console.log(counter());
 </script>

运行结果:

1205

0

这里的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中对象构造函数可以创建一个对象。

<script type="text/javascript">
 /*构造函数*/
 //可以简单的认为是一个类型的定义
 function Student(name,age){
 this.name=name;
 this.age=age;
 this.show=function(){
 console.log(this.name+","+this.age);
 }
 }
 //通过new关键字调用构造函数,创建一个对象tom
 var rose=new Student("rose",18);
 var jack=new Student("jack",20);

 rose.show();
 jack.show();
 </script>

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是指调用函数的函数。

<script type="text/javascript">
 function add(n)
 {
 console.log("add被调用");
 if(n<=2){
 return 1;
 }
 return add.caller(n-1)+add.caller(n-2);
 }
 function calc(n){
 console.log("calc被调用");
 return add(n);
 }
 //1 1 2
 console.log(calc(3));
 </script>

结果:

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.5、立即执行函数表达式 (IIFE)

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

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 函数名称(参数){函数主体}

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

代码:

 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关键字就会自动将()里面的代码识别为函数表达式而不是函数声明。

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

<!DOCTYPE html>
<html>
 <head>
 <meta charset="UTF-8">
 <title>IIFE</title>
 </head>
 <body>
 <script type="text/javascript">
 //调用匿名函数
 (function() {
 console.log("这是一个函数表达式");
 })();
 //调用匿名对象
 ({
 name: "foo",
 show: function() {
 console.log(this.name);
 }
 }).show();
 console.log({
 a: 1
 }.a);
 console.log({
 a: function() {}
 }.a());
 </script>
 </body>
</html>

运行结果:

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

<!DOCTYPE html>
<html>
 <head>
 <meta charset="UTF-8">
 <title>A</title>
 </head>
 <body>
 <script src="js/Student.js" type="text/javascript" charset="utf-8"></script>
 <script src="js/common.js" type="text/javascript" charset="utf-8"></script>
 </body>
</html>

B.HTML

<!DOCTYPE html>
<html>
 <head>
 <meta charset="UTF-8">
 <title></title>
 </head>
 <body>
 <script src="js/common.js" type="text/javascript" charset="utf-8"></script>
 <script type="text/javascript">
 other1();
 </script>
 </body>
</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

四、示例下载

http://xiazai.jb51.net/201612/yuanma/javascript003-master_jb51.rar

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!

(0)

相关推荐

  • JavaScript闭包和范围实例详解

    本文实例分析了JavaScript闭包和范围.分享给大家供大家参考,具体如下: 1. if (!("a" in window)) { var a = 1; } alert(a); [分析]代码含义:如果window不包含属性a,就声明一个变量a并赋值为1 ①JS引擎会先扫描所有的变量声明 ②所有的全局变量都是window的属性 ③变量声明和赋值一起用时,Js引擎会自动将它分成两部分:变量声明提前,变量赋值没有(不将赋值提前是因为他有可能影响代码执行出不可预期的结果) 所以代码执行顺序等

  • JavaScript中闭包的写法和作用详解

    1.什么是闭包 闭包是有权访问另一个函数作用域的变量的函数. 简单的说,Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内.而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量.参数和声明的其他内部函数.当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包. 2.变量的作用域 要理解闭包,首先要理解变量的作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变

  • 浅谈JavaScript for循环 闭包

    有个网友问了个问题,如下的html,为什么每次输出都是5,而不是点击每个p,就alert出对应的1,2,3,4,5. <html > <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>闭包演示</title> <script type="text/javascript&quo

  • 深入理解javascript函数参数与闭包

    最近在学习javascript的函数,函数是javascript的一等对象,想要学好javascript,就必须深刻理解函数.本人把学习的过程整理成文章,一是为了加深自己函数的理解,二是给读者提供学习的途径,避免走弯路.内容有些多,但都是笔者对于函数的总结. 1.函数参数 1.1:参数是什么 1.2:参数的省略 1.3:参数默认值 1.4:参数传递方式 1.5:同名参数 1.6:arguments对象 2.闭包 2.1:闭包定义 2.2:立即调用的函数表达式(IIFE, Immediately

  • JS 循环li添加点击事件 (闭包的应用)

    废话不多说了,直接给大家贴代码了,具体代码如下所述: var aLi = document.querySelectorAll('.article-tab li'); for (var i = 0; i <= aLi.length; i++) { (function(){ var p = i aLi[i].onclick = function() { alert(p); } })(); } 以上所述是小编给大家介绍的JS 循环li添加点击事件 (闭包的应用),希望对大家有所帮助,如果大家有任何疑问

  • Javascript闭包与函数柯里化浅析

    闭包和柯里化都是JavaScript经常用到而且比较高级的技巧,所有的函数式编程语言都支持这两个概念,因此,我们想要充分发挥出JavaScript中的函数式编程特征,就需要深入的了解这两个概念,闭包事实上更是柯里化所不可缺少的基础. 一.柯里化的概念 在计算机科学中,柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术.这个技术由Christopher Strachey以逻辑学家 Haskell Curry 命名的,尽管

  • js闭包用法实例详解

    本文实例讲述了js闭包用法.分享给大家供大家参考,具体如下: 引言 在公司中需要写一个js脚本来进行网站的统计,实现类似百度统计或者站长统计的功能,在实现的过程中自己感觉写的代码还是可以的,因为之前的js代码都是这些写,但是在组长代码走查的时候却非常的不满意,因为我们在js中写的方法都是全局的方法,因为我们写的东西需要嵌入到别人的界面中,所以这些全局的东西很可能会和别人的东西重名从而引发错误,所以说组长就给我留下一句话:用js闭包包起来. 变量作用域 我们都非常的熟悉变量的作用域就分为:全局变量

  • JavaScript闭包实例详解

    一.充电 1.一切(引用类型)都是对象,对象是属性的集合. 2.函数是一种对象,但是函数却不像数组一样--你可以说数组是对象的一种,因为数组就像是对象的一个子集一样.但是函数与对象之间,却不仅仅是一种包含和被包含的关系,函数和对象之间的关系比较复杂,甚至有一点鸡生蛋蛋生鸡的逻辑. function Fn() {this.name = '王福朋';this.year = 1988;} var fn1 = new Fn(); var obj = { a: 10, b: 20 };等价于var obj

  • 通俗易懂地解释JS中的闭包

    1. "闭包就是跨作用域访问变量." [示例一] var name = 'wangxi' function user () { // var name = 'wangxi' function getName () { console.log(name) } getName() } user() // wangxi 在 getName 函数中获取 name,首先在 getName 函数的作用域中查找 name,未找到,进而在 user 函数的作用域中查找,同样未找到,继续向上回溯,发现在

  • 详解JavaScript作用域 闭包

    JavaScript闭包,是JS开发工程师必须深入了解的知识.3月份自己曾撰写博客<JavaScript闭包>,博客中只是简单阐述了闭包的工作过程和列举了几个示例,并没有去刨根问底,将其弄明白! 现在随着对JavaScript更深入的了解,也刚读完<你不知道的JavaScript(上卷)>这本书,所以乘机整理下,从底层和原理上去刨一下. JavaScript并不具有动态作用域,它只有词法作用域.词法作用域是在写代码或者说定义时确定的,而动态作用域是在运行时确定的.了解闭包前,首先我

  • 详解JavaScript面向对象实战之封装拖拽对象

    目录 概述 1.如何让一个DOM元素动起来 2.如何获取当前浏览器支持的transform兼容写法 3.如何获取元素的初始位置 5.我们需要用到哪些事件? 6.拖拽的原理 7. 我又来推荐思维导图辅助写代码了 8.代码实现 part1.准备工作 part2.功能函数 part3.声明三个事件的回调函数 9.封装拖拽对象 概述 为了能够帮助大家了解更多的方式与进行对比,我会使用三种不同的方式来实现拖拽. 不封装对象直接实现: 利用原生JavaScript封装拖拽对象: 通过扩展jQuery来实现拖

  • 详解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.函数与函数

  • 详解JavaScript中的数组合并方法和对象合并方法

    1 数组合并 1.1 concat 方法 var a=[1,2,3],b=[4,5,6]; var c=a.concat(b); console.log(c);// 1,2,3,4,5,6 console.log(a);// 1,2,3 不改变本身 1.2 循环遍历 var arr1=['a','b']; var arr2=['c','d','e']; for(var i=0;i<arr2.length;i++){ arr1.push(arr2[i]) } console.log(arr1);/

  • 详解Javascript ES6中的箭头函数(Arrow Functions)

    ES6可以使用"箭头"(=>)定义函数,注意是函数,不要使用这种方式定义类(构造器). 一.语法 1. 具有一个参数的简单函数 var single = a => a single('hello, world') // 'hello, world' 2. 没有参数的需要用在箭头前加上小括号 var log = () => { alert('no param') } 3. 多个参数需要用到小括号,参数间逗号间隔,例如两个数字相加 var add = (a, b) =&g

  • 详解JavaScript异步编程中jQuery的promise对象的作用

    Promise, 中文可以理解为愿望,代表单个操作完成的最终结果.一个Promise拥有三种状态:分别是unfulfilled(未满足的).fulfilled(满足的).failed(失败的),fulfilled状态和failed状态都可以被监听.一个愿望可以从未满足状态变为满足或者失败状态,一旦一个愿望处于满足或者失败状态,其状态将不可再变化.这种"不可改变"的特性对于一个Promise来说非常的重要,它可以避免Promise的状态监听器修改一个Promise的状态导致别的监听器的行

  • 详解JavaScript基础知识(JSON、Function对象、原型、引用类型)

    1.JSON 概念:JavaScript 对象表示法(JavaScript Object Notation),是一种轻量级的数据交换格式 特点:易于程序员编写和查看:易于计算机解析和生成 数据结构:Object对象格式   { key: value , key: value } Array数组格式   [ value , value ] Eg:  var json = ' { "   " : ''  '' } '    (内部双引号,外部单引号) * JSON - 支持独立的"

  • 详解JavaScript匿名函数和闭包

    概述 在JavaScript前端开发中,函数与对其状态即词法环境(lexical environment)的引用共同构成闭包(closure).也就是说,闭包可以让你从内部函数访问外部函数作用域.在JavaScript,函数在每次创建时生成闭包.匿名函数和闭包可以放在一起学习,可以加深理解.本文主要通过一些简单的小例子,简述匿名函数和闭包的常见用法,仅供学习分享使用,如有不足之处,还请指正. 普通函数 普通函数由fucntion关键字,函数名,() 和一对{} 组成,如下所示: function

  • 一文详解JavaScript闭包典型应用

    目录 1.应用 1.1 模拟私有变量 1.2 柯里化 1.3 偏函数 1.4 防抖 1.5 节流 2.性能问题 2.1 内存泄漏 2.2 常见的内存泄漏 3.闭包与循环体 3.1 这段代码输出啥 3.2 改造方法 4.总结 1.应用 以下这几个方面是我们开发中最为常用到的,同时也是面试中回答比较稳的几个方面. 1.1 模拟私有变量 我们都知道JS是基于对象的语言,JS强调的是对象,而非类的概念,在ES6中,可以通过class关键字模拟类,生成对象实例. 通过class模拟出来的类,仍然无法实现传

  • 详解Javascript 中的 class、构造函数、工厂函数

    到了ES6时代,我们创建对象的手段又增加了,在不同的场景下我们可以选择不同的方法来建立.现在就主要有三种方法来构建对象,class关键字,构造函数,工厂函数.他们都是创建对象的手段,但是却又有不同的地方,平时开发时,也需要针对这不同来选择. 首先我们来看一下,这三种方法是怎样的 // class 关键字,ES6新特性 class ClassCar { drive () { console.log('Vroom!'); } } const car1 = new ClassCar(); consol

随机推荐