闭包(Closures)闭包属于比较难的一部分,在学习闭包之前,先来学习以下Javascript的作用域(Scope)
作用域链(Scope Chain)
函数内部定义的变量,在函数外不能访问,或者变量定义在代码段中(如if,for中),在代码段之外也不可访问。
var a =1;
function f(){
var b=1;
return a;
}
f();//a=1
b;//b 没有定义
a 是全局变量,而b定义在函数f之中。所以:
在f函数中,a和b都可以访问。
在f函数之外,a可以访问,而b不可以
再次看个例子
var a = 1;
function f(){
var b = 1;
function n(){
var c =3;
}
}
如果定义一个函数n在函数f中,函数n不但可以访问自己作用域的c,而且还能访问所谓的父作用域的b和a
这就是作用域链(Scope Chain)
词法作用域(Lexical Scope)
在Javascript中,也有词法作用域(Lexical Scope),这个意思是,函数在定义的时候就生成了它的作用域,而不是在调用的时候,看个例子就明白了。
function f1(){var a=1;f2();}
function f2(){return a;}
f1();//a没有定义
先看看函数f1,调用了函数f2,因为函数局部变量a也在f1中,可能期望函数f2也访问a,但事实却不是这样。
因为这个时候f2函数已经定义完毕了,而它的范围没有a被找到。不管是函数f1还是函数f2,仅仅能访问的是本身的局部变 量或者全局变量。
用闭包来破坏这个作用域链(Breaking the Chain with Closure)
让我们举例来说明闭包吧。
实例1
function f(){
var b="b";
return function(){
return b;
}
}
函数f包含了变量b,在全局作用域中不可能访问b,结果为没有定义(undefined)。
看看这个函数的返回值为一个函数。这个新的函数可以访问f范围中的变量b。看如下代码
var n = f();
n();//访问b
由于函数f本身就是全局的,而它返回的函数又是个新的全局函数,而它又可以访问函数f的作用域。
实例2
这个例子和上个例子结果是一样的,但是有一点不同,函数f并不返回一个函数,而是新建一个全局变量n,代码如下
var n;
function f(){
var b = "b";
n=function(){
return b;
}
}
所以可以直接n()来访问函数f里的变量b
通过上面两个例子我们就可以说当一个函数指向了它的父作用域,就可以称之为闭包。
当我们创建了个传递参数的函数f,这个参数就变成了函数f的局部变量了。我们可以创建一个f的内部函数来返回这个参数
function f(arg){
var n =function(){
return args;
}
arg++;
return n;
}
var m= f(123);
m();//124
闭包在循环中的应用
再循环中很容易引起一些bug,虽然表面是正常的。
看如下的代码
function f(){
var a = [];
var i;
for(i=0;i<3;i++){
a[i] = function(){
alert(i);
return i;
}
}
return a;
}
var a= f();
a[0]();//3
a[1]();//3
a[2]();//3
新建个循环,我们的目的是每次循环都新建一个函数,函数返回的是循环的序列值也就是i。我们看看以上代码的运行结果
都是为3.而我们期望的结果是1,2,3。
到底是为什么呢?我们新建了三个闭包,都指向了变量i。闭包并没有记住变量i的值,它仅是变量i的引用。在循环结束后,i的值是3,所以结果都是3了。
来看看正确的写法
function f() {
var a = [];
var i;
for(i = 0; i < 3; i++) {
a[i] = (function(x){
return function(){
alert(x);
return x;
}
})(i);
}
return a;
}
var a = f();
a[0]();//0
a[1]();//1
a[2]();//2
我们又用了一个闭包,把变量i变成了局部变量x了,x变量每次都是不同的值了。如果没有彻底理解自调用函数那就如下写法就可以明白了
function f() {
function makeClosure(x) {
return function(){
return x;
}
}
var a = [];
var i;
for(i = 0; i < 3; i++) {
a[i] = makeClosure(i);
}
return a;
}
分享到:
相关推荐
在讨论JavaScript编程语言时,匿名函数和闭包是两个重要的概念,它们在函数式编程和模块化代码设计中扮演着核心角色。匿名函数是没有具体名称的函数,它们可以是独立的,也可以是表达式的一部分,通常用于定义临时...
闭包是JavaScript中的一个核心概念,它是指有权访问另一个函数作用域中变量的函数。闭包的产生是因为内部函数持有了外部函数的变量,即使外部函数已经执行完毕,内部函数仍然可以访问这些变量。 #### 三、闭包的...
**闭包**是JavaScript中一种非常强大的机制,它允许函数访问并操作其定义时所在的范围内的变量,即使在函数被调用时这个范围已经不存在了。 ##### 原理: - **作用域链**:当一个函数被创建时,它会捕获一个**作用...
闭包是ECMAScript (JavaScript)最强大的特性之一,但用好闭包的前提是必须理解闭包。闭包的创建相对容易,人们甚至会在不经意间创建闭包,但这些无意创建的闭包却存在潜在的危害,尤其是在比较常见的浏览器环境下...
在JavaScript函数式编程中,闭包是一个非常重要的概念。闭包是指有权访问另一个函数作用域中变量的函数。由于JavaScript的作用域链,闭包能够访问到函数定义时的外部变量,即使外部函数已经执行结束。闭包通常用于...
理解函数的原型链和闭包对于深入掌握JavaScript至关重要。 首先,让我们看看函数的定义方式。在JavaScript中,我们可以使用`function`关键字直接定义函数,如`function fn(a, b) {}`。此外,函数也可以通过赋值语句...
JavaScript函数式编程是一种编程范式,它强调将计算视为数据处理的过程,并且重视函数作为第一类公民,即函数可以作为变量赋值、作为参数传递、作为返回值返回。这种编程风格在JavaScript中尤其常见,因为它提供了...
深入理解javascript原型和闭包(01)——一切都是对象 深入理解javascript原型和闭包(02)——函数和对象的关系
**闭包**是JavaScript中的一个重要概念,它允许函数访问并操作外部作用域中的变量,即使在其外部函数已经执行完毕的情况下。闭包最常见的创建方式就是在函数内部定义另一个函数。例如: ```javascript function box...
js高级中的函数之闭包函数全解与应用场景(循环闭包,定时器,面试题)
在JavaScript中,闭包(Closure)是一个极其关键的概念,它使得内部函数能够访问到其外部函数的作用域内的变量,即使外部函数已经执行完毕。这一特性是基于JavaScript的函数作用域规则以及函数本身可以作为值进行...
这表明了JavaScript函数的灵活性和对象的特性。在JavaScript中,函数、数组、对象等都是对象,它们可以包含各种属性,甚至可以动态地修改自己的结构。 原型(Prototype)是JavaScript实现继承的基础,每一个对象都...
本文结合 ECMA 262 规范详解了闭包的内部工作机制,让 JavaScript 编程人员对闭包的理解从“嵌套的函数”深入到“标识符解析、执行环境和作用域链”等等 JavaScript 对象背后的运行机制当中,真正领会到闭包的实质。
JavaScript中的闭包是一种重要的编程概念,它涉及到函数、作用域和变量持久化等多个核心知识点。闭包的本质是函数能够访问并操作其外部作用域内的变量,即使在其外部作用域已经结束之后仍然能保持对这些变量的访问。...
在JavaScript中,闭包是一个函数和其周围环境的结合体。在for循环中,我们使用一个函数来处理每个元素,但是这个函数捕捉的是循环变量i的引用,而不是其值。因此,当我们点击每个段落时,alert出来的总是循环结束时...
JavaScript函数是编程语言的核心组成部分,它是一段可重复使用的代码块,可以接受参数并返回值。在JavaScript中,函数不仅可以作为表达式,还能作为变量赋值、作为参数传递以及作为返回值。本速查指南将深入探讨...
在探讨JavaScript嵌套函数及闭包之前,我们必须了解一些基础知识。首先是JavaScript中的函数,它们是JavaScript编程的核心。函数在JavaScript中是“一等公民”,这意味着函数可以作为参数传递给其他函数,也可以作为...
JavaScript函数式编程是一种编程范式,它将计算视为数据处理,并强调使用无副作用的纯函数。在JavaScript中,函数式编程允许我们写出更简洁、可读性更强的代码,同时提高了代码的复用性和测试性。《JavaScript函数式...