GVKun编程网logo

Javascript闭包(Closure)详解(javascript 闭包)

15

针对Javascript闭包和Closure详解这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展JavaScript匿名函数(anonymousfunction)与闭包(closure)、J

针对Javascript闭包Closure详解这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展JavaScript 匿名函数(anonymous function)与闭包(closure)、JavaScript 匿名函数(anonymous function)与闭包(closure)_javascript技巧、javascript 闭包 (Closure) 初探、JavaScript 闭包(Closure)学习等相关知识,希望可以帮助到你。

本文目录一览:

Javascript闭包(Closure)详解(javascript 闭包)

Javascript闭包(Closure)详解(javascript 闭包)

下面就是我的学习笔记,对于Javascript初学者应该是很有用的。

一、变量的作用域

要理解闭包,首先必须理解Javascript特殊的变量作用域。

变量的作用域无非就是两种:全局变量和局部变量。

Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。

rush:js;"> var n=999;

function f1(){
  alert(n);
}

f1(); // 999

另一方面,在函数外部自然无法读取函数内的局部变量。

rush:js;"> function f1(){   var n=999; }

alert(n); // error

这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

rush:js;"> function f1(){   n=999; }

f1();

alert(n); // 999

二、如何从外部读取局部变量?

出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。

那就是在函数的内部,再定义一个函数。

rush:js;"> function f1(){

  var n=999;

  function f2(){
    alert(n); // 999
  }

}

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

rush:js;"> function f1(){

  var n=999;

  function f2(){
    alert(n);
  }

  return f2;

}

var result=f1();

result(); // 999

三、闭包的概念

上一节代码中的f2函数,就是闭包。

各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。

由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

四、闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

怎么来理解这句话呢?请看下面的代码。

rush:js;"> function f1(){

    var n=999;

    nAdd=function(){n+=1}

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999

  nAdd();

  result(); // 1000

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

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

这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

五、使用闭包的注意点

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

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

六、思考题

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

代码片段一

rush:js;"> var name = "The Window";

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

    }

  };

  alert(object.getNameFunc()());

代码片段二

rush:js;"> var name = "The Window";

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

    }

  };

  alert(object.getNameFunc()()); 

以上所述就是本文的全部内容了,希望大家能够喜欢。

JavaScript 匿名函数(anonymous function)与闭包(closure)

JavaScript 匿名函数(anonymous function)与闭包(closure)

引入
匿名函数
闭包
变量作用域
函数外部访问函数内部的局部变量
用闭包实现私有成员
引入
闭包是用匿名函数来实现。闭包就是一个受到保护的变量空间,由内嵌函数生成。“保护变量”的思想在几乎所有的编程语言中都能看到。

先看下 JavaScript 作用域:
JavaScript 具有函数级的作用域。这意味着,不能在函数外部访问定义在函数内部的变量。
JavaScript 的作用域又是词法性质的(lexically scoped)。这意味着,函数运行在定义它的作用域中,而不是在调用它的作用域中。这是 JavaScript 的一大特色,将在后面说明。
把这两个因素结合在一起,就能通过把变量包裹在匿名函数中而对其加以保护。你可以这样创建类的私有变量:

var baz; 
(function() { 
var foo = 10; 
var bar = 2; 
baz = function() { 
return foo * bar; 
}; 
})(); 
baz(); 

尽管在匿名函数外执行,但 baz 仍然可以访问 foo 和 bar。

说明:

1,第 1 行,baz 是全局变量;

2,第 3 ~第 9 行,定义一个匿名函数;

3,第 4 和 5 行,foo 和 bar 是匿名函数内的局部变量;第 6 ~ 8 行,在匿名函数内定义一个匿名函数,并将其赋值给全局变量 baz;

4,第 10 行,调用 baz。若改成 "alert(baz());",将显示 20;

5,按理说,在匿名函数外不能访问 foo 和 bar,但是现在可以。

在说明闭包前,先了解一下匿名函数。

匿名函数
匿名函数是指那些无需定义函数名的函数。匿名函数与 Lambda 表达式(拉姆达表达式)是一回事。唯一的不同――语法形式不同。Lambda 表达式更进一步。本质上,它们的作用都是:产生方法――内联方法,也就是说,省去函数定义,直接写函数体。

Lambda 表达式一般形式:

(input parameters) => {statement;}
其中:

参数列表,可以有多个、一个或者无参数。参数可以隐式或者显式定义。
表达式或者语句块,也就是函数体。
上面代码,第 6 ~ 8 行,没有函数名,是个匿名函数,采用 Lambda 表达式,严格意义上,虽然语法有差异,但目的一样。


示例1:

var baz1 = function() { 
var foo = 10; 
var bar = 2; 
return foo * bar; 
}; 
function mutil() { 
var foo = 10; 
var bar = 2; 
return foo * bar; 
}; 
alert(baz1()); 
var baz2 = mutil(); 
alert(baz2); 

说明:

1,baz1 与 baz2 完全一样,但 baz1 与 baz2 相比,省去了函数定义,直接函数体――看上去多简约。

闭包
变量作用域
示例2:函数内部可以访问全局变量。

var baz = 10; 
function foo() { 
alert(baz); 
} 
foo(); 

这是可以。

示例3:函数外部不能访问函数内部的局部变量。

function foo() { 
var bar = 20; 
} 
alert(bar); 

这会报错。

另外,函数内部声明变量时,一定要使用 var 关键字,否则,声明的是一个全局变量。

示例4:

function foo() { 
bar = 20; 
} 
alert(bar); 

 函数外部访问函数内部的局部变量
实际情况,需要我们从函数外部获得函数内部的局部变量。先看示例5。

示例5:

function foo() { 
var a = 10; 
function bar() { 
a *= 2; 
} 
bar(); 
return a; 
} 
var baz = foo(); 
alert(baz); 

a 定义在 foo 内,bar 可以访问,因为 bar 也定义在 foo 内。现在,如何让 bar 在 foo 外部被调用?

示例6:

function foo() { 
var a = 10; 
function bar() { 
a *= 2; 
return a; 
} 
return bar; 
} 
var baz = foo(); 
alert(baz()); 
alert(baz()); 
alert(baz()); 

var blat = foo(); 
alert(blat()); 

说明:

1,现在可以从外部访问 a;
2,JavaScript 的作用域是词法性的。a 是运行在定义它的 foo 中,而不是运行在调用 foo 的作用域中。 只要 bar 被定义在 foo 中,它就能访问 foo 中定义的变量 a,即使 foo 的执行已经结束。也就是说,按理,"var baz = foo()" 执行后,foo 已经执行结束,a 应该不存在了,但之后再调用 baz 发现,a 依然存在。这就是 JavaScript 特色之一――运行在定义,而不是运行的调用。

其中, "var baz = foo()" 是一个 bar 函数的引用;"var blat= foo()" 是另一个 bar 函数引用。

用闭包实现私有成员
现在,需要创建一个只能在对象内部访问的变量。用闭包再适合不过,因为通过闭包你可以创建只允许特定函数访问的变量,而且这些变量在这些函数的各次调用间依然存在。

为了创建私有属性,你需要在构造函数的作用域中定义相关变量。这些变量可以被定义于该作用域中的所有函数访问,包括那些特权方法。

示例7:

var Book = function(newIsbn,newTitle,newAuthor) { 
// 私有属性 
var isbn,title,author; 
// 私有方法 
function checkIsbn(isbn) { 
// Todo 
} 
// 特权方法 
this.getIsbn = function() { 
return isbn; 
}; 
this.setIsbn = function(newIsbn) { 
if (!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.'); 
isbn = newIsbn; 
}; 
this.getTitle = function() { 
return title; 
}; 
this.setTitle = function(newTitle) { 
title = newTitle || 'No title specified.'; 
}; 
this.getAuthor = function() { 
return author; 
}; 
this.setAuthor = function(newAuthor) { 
author = newAuthor || 'No author specified.'; 
}; 
// 构造器代码 
this.setIsbn(newIsbn); 
this.setTitle(newTitle); 
this.setAuthor(newAuthor); 
}; 

// 共有、非特权方法 
Book.prototype = { 
display: function() { 
// Todo 
} 
}; 

说明:

1,用 var 声明变量 isbn、title 和 author,而不是 this,意味着它们只存在 Book 构造器中。checkIsbn 函数也是,因为它们是私有的;

2,访问私有变量和方法的方法只需声明在 Book 中即可。这些方法称为特权方法。因为,它们是公共方法,但却能访问私有变量和私有方法,像 getIsbn、setIsbn、getTitle、setTitle、getAuthor、setAuthor(取值器和构造器)。

3,为了能在对象外部访问这些特权方法,这些方法前边加了 this 关键字。因为这些方法定义在 Book 构造器的作用域里,所以它们能够访问私有变量 isbn、title 和 author。但在这些特权方法里引用 isbn、title 和 author 变量时,没有使用 this 关键字,而是直接引用。因为它们不是公开的。

4,任何不需要直接访问私有变量的方法,像 Book.prototype 中声明的,如 display。它不需要直接访问私有变量,而是通过 get*、set* 简介访问。

5,这种方式创建的对象可以具有真正私有的变量。其他人不能直接访问 Book 对象的任何内部数据,只能通过赋值器和。这样一切尽在掌握。

但这种方式的缺点是:

“门户大开型”对象创建模式中,所有方法都创建在原型 prototype 对象中,因此不管生成多少对象实例,这些方法在内存中只有一份。
而采用本节的做法,没生成一个新的对象实例,都将为每个私有方法(如,checkIsbn)和特权方法(如,getIsbn、setIsbn、getTitle、setTitle、getAuthor、setAuthor)生成一个新的副本。
因此,本节方法,只适于用在真正需要私有成员的场合。另外,这种方式也不利于继承。

JavaScript 匿名函数(anonymous function)与闭包(closure)_javascript技巧

JavaScript 匿名函数(anonymous function)与闭包(closure)_javascript技巧

本文内容
引入
匿名函数
闭包
变量作用域
函数外部访问函数内部的局部变量
用闭包实现私有成员
引入
闭包是用匿名函数来实现。闭包就是一个受到保护的变量空间,由内嵌函数生成。“保护变量”的思想在几乎所有的编程语言中都能看到。
先看下 JavaScript 作用域:
JavaScript 具有函数级的作用域。这意味着,不能在函数外部访问定义在函数内部的变量。
JavaScript 的作用域又是词法性质的(lexically scoped)。这意味着,函数运行在定义它的作用域中,而不是在调用它的作用域中。这是 JavaScript 的一大特色,将在后面说明。
把这两个因素结合在一起,就能通过把变量包裹在匿名函数中而对其加以保护。你可以这样创建类的私有变量:

复制代码 代码如下:

var baz;
(function() {
var foo = 10;
var bar = 2;
baz = function() {
return foo * bar;
};
})();
baz();

尽管在匿名函数外执行,但 baz 仍然可以访问 foo 和 bar。

说明:

1,第 1 行,baz 是全局变量;

2,第 3 ~第 9 行,定义一个匿名函数;

3,第 4 和 5 行,foo 和 bar 是匿名函数内的局部变量;第 6 ~ 8 行,在匿名函数内定义一个匿名函数,并将其赋值给全局变量 baz;

4,第 10 行,调用 baz。若改成 "alert(baz());",将显示 20;

5,按理说,在匿名函数外不能访问 foo 和 bar,但是现在可以。

在说明闭包前,先了解一下匿名函数。



匿名函数
匿名函数是指那些无需定义函数名的函数。匿名函数与 Lambda 表达式(拉姆达表达式)是一回事。唯一的不同——语法形式不同。Lambda 表达式更进一步。本质上,它们的作用都是:产生方法——内联方法,也就是说,省去函数定义,直接写函数体。

Lambda 表达式一般形式:

(input parameters) => {statement;}
其中:

参数列表,可以有多个、一个或者无参数。参数可以隐式或者显式定义。
表达式或者语句块,也就是函数体。
上面代码,第 6 ~ 8 行,没有函数名,是个匿名函数,采用 Lambda 表达式,严格意义上,虽然语法有差异,但目的一样。

示例1:
复制代码 代码如下:

var baz1 = function() {
var foo = 10;
var bar = 2;
return foo * bar;
};
function mutil() {
var foo = 10;
var bar = 2;
return foo * bar;
};
alert(baz1());
var baz2 = mutil();
alert(baz2);

说明:

1,baz1 与 baz2 完全一样,但 baz1 与 baz2 相比,省去了函数定义,直接函数体——看上去多简约。



闭包
变量作用域
示例2:函数内部可以访问全局变量。

复制代码 代码如下:

var baz = 10;
function foo() {
alert(baz);
}
foo();


这是可以。

示例3:函数外部不能访问函数内部的局部变量。

复制代码 代码如下:

function foo() {
var bar = 20;
}
alert(bar);


这会报错。

另外,函数内部声明变量时,一定要使用 var 关键字,否则,声明的是一个全局变量。

示例4:

复制代码 代码如下:

function foo() {
bar = 20;
}
alert(bar);


函数外部访问函数内部的局部变量
实际情况,需要我们从函数外部获得函数内部的局部变量。先看示例5。

示例5:

复制代码 代码如下:

function foo() {
var a = 10;
function bar() {
a *= 2;
}
bar();
return a;
}
var baz = foo();
alert(baz);


a 定义在 foo 内,bar 可以访问,因为 bar 也定义在 foo 内。现在,如何让 bar 在 foo 外部被调用?

示例6:

复制代码 代码如下:

function foo() {
var a = 10;
function bar() {
a *= 2;
return a;
}
return bar;
}
var baz = foo();
alert(baz());
alert(baz());
alert(baz());

var blat = foo();
alert(blat());


说明:

1,现在可以从外部访问 a;

2,JavaScript 的作用域是词法性的。a 是运行在定义它的 foo 中,而不是运行在调用 foo 的作用域中。 只要 bar 被定义在 foo 中,它就能访问 foo 中定义的变量 a,即使 foo 的执行已经结束。也就是说,按理,"var baz = foo()" 执行后,foo 已经执行结束,a 应该不存在了,但之后再调用 baz 发现,a 依然存在。这就是 JavaScript 特色之一——运行在定义,而不是运行的调用。

其中, "var baz = foo()" 是一个 bar 函数的引用;"var blat= foo()" 是另一个 bar 函数引用。

用闭包实现私有成员
现在,需要创建一个只能在对象内部访问的变量。用闭包再适合不过,因为通过闭包你可以创建只允许特定函数访问的变量,而且这些变量在这些函数的各次调用间依然存在。

为了创建私有属性,你需要在构造函数的作用域中定义相关变量。这些变量可以被定义于该作用域中的所有函数访问,包括那些特权方法。

示例7:

复制代码 代码如下:

var Book = function(newIsbn, newTitle, newAuthor) {
// 私有属性
var isbn, title, author;
// 私有方法
function checkIsbn(isbn) {
// TODO
}
// 特权方法
this.getIsbn = function() {
return isbn;
};
this.setIsbn = function(newIsbn) {
if (!checkIsbn(newIsbn)) throw new Error(''Book: Invalid ISBN.'');
isbn = newIsbn;
};
this.getTitle = function() {
return title;
};
this.setTitle = function(newTitle) {
title = newTitle || ''No title specified.'';
};
this.getAuthor = function() {
return author;
};
this.setAuthor = function(newAuthor) {
author = newAuthor || ''No author specified.'';
};
// 构造器代码
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
};

// 共有、非特权方法
Book.prototype = {
display: function() {
// TODO
}
};


说明:

1,用 var 声明变量 isbn、title 和 author,而不是 this,意味着它们只存在 Book 构造器中。checkIsbn 函数也是,因为它们是私有的;

2,访问私有变量和方法的方法只需声明在 Book 中即可。这些方法称为特权方法。因为,它们是公共方法,但却能访问私有变量和私有方法,像 getIsbn、setIsbn、getTitle、setTitle、getAuthor、setAuthor(取值器和构造器)。

3,为了能在对象外部访问这些特权方法,这些方法前边加了 this 关键字。因为这些方法定义在 Book 构造器的作用域里,所以它们能够访问私有变量 isbn、title 和 author。但在这些特权方法里引用 isbn、title 和 author 变量时,没有使用 this 关键字,而是直接引用。因为它们不是公开的。

4,任何不需要直接访问私有变量的方法,像 Book.prototype 中声明的,如 display。它不需要直接访问私有变量,而是通过 get*、set* 简介访问。

5,这种方式创建的对象可以具有真正私有的变量。其他人不能直接访问 Book 对象的任何内部数据,只能通过赋值器和。这样一切尽在掌握。

但这种方式的缺点是:

“门户大开型”对象创建模式中,所有方法都创建在原型 prototype 对象中,因此不管生成多少对象实例,这些方法在内存中只有一份。
而采用本节的做法,没生成一个新的对象实例,都将为每个私有方法(如,checkIsbn)和特权方法(如,getIsbn、setIsbn、getTitle、setTitle、getAuthor、setAuthor)生成一个新的副本。
因此,本节方法,只适于用在真正需要私有成员的场合。另外,这种方式也不利于继承。

javascript 闭包 (Closure) 初探

javascript 闭包 (Closure) 初探

closure 被翻译成 “闭包”,感觉这东西被包装的太学术化。下面参考书本和网上资源简单探讨一下(理解不当之处务请留意)。
1、什么是闭包
官方的回答:所谓 “闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
看了上面的定义,如果你不是高手,我坚信你会和我一样愤怒的质问:这 tmd 是人话吗?
要理解闭包,还是代码最有说服力啊,上代码:


function funcTest()
{
  
var tmpNum=100// 私有变量

  
// 在函数 funcTest 内定义另外的函数作为 funcTest 的方法函数
  function innerFuncTest(
  {
       alert(tmpNum); 
// 引用外层函数 funcTest 的临时变量 tmpNum
  }

  
return innerFuncTest; // 返回内部函数
}

// 调用函数
var myFuncTest=funcTest(); 
myFuncTest();
// 弹出 100

上面代码中,注释已经写的清清楚楚。现在我们可以这么理解 “闭包”:在函数体内定义另外的函数作为目标对象的方法函数(示例中就是在函数 funcTest 内定义另外的函数 innerFuncTest 作为 funcTest 的方法函数),而这个对象的方法函数反过来引用外层函数体中的临时变量(闭包是一种间接保持变量值的机制。示例中就是内部函数 innerFuncTest 引用外层函数 funcTest 的临时变量 tmpNum, 这里必须注意,临时变量可以包括外部函数中声明的所有局部变量参数和声明的其他内部函数)。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包(示例中,调用函数的时候,myFuncTest 实际调用的是 innerFuncTest 函数,也就是说 funcTest 的一个内部函数 innerFuncTest 在 funcTest 之外被调用,这时就创建了一个闭包)。
2、两个利用闭包的例子
下面举两个例子,一个是因为闭包导致了问题,而另一个则利用闭包巧妙地通过函数的作用域绑定参数。
这两个例子相关的 HTML 标记片断如下:
<a href="#" id="closureTest0"> 利用闭包的例子(1 秒后会看到提示)</a><br />
<a href="#" id="closureTest1"> 由于闭包导致问题的例子 1</a><br />
<a href="#" id="closureTest2"> 由于闭包导致问题的例子 2</a><br />
<a href="#" id="closureTest3"> 由于闭包导致问题的例子 3</a><br />
(1)、因闭包而导致问题
上面的 HTML 标记片断中有 4 个 <a> 元素,现在要给后三个指定事件处理程序,使它们在用户单击时报告自己在页面中的顺序,比如:当用户单击第 2 个链接时,报告 “您单击的是第 1 个链接”。为此,如果编写下列为后三个链接添加事件处理程序的函数:


function badClosureExample(){
    
for (var i = 1; i <4; i++) {
        
var element = document.getElementById(''closureTest'' + i);
        element .onclick 
= function(){
            alert(
'' 您单击的是第'' + i + '' 个链接'');
        }
    }
}

然后,在页面载入完成后(不然可能会报错)调用该函数:
window.onload = function(){
    badClosureExample();
}
看一下运行结果,此时单击后 3 个链接,会看到警告框中显示什么信息呢?—— 全都是 “您单击的是第 4 个链接”。是不是令你感到十分意外?为什么?
分析:因为在 badClosureExample () 函数中指定给 element.onclick 的事件处理程序,也就是 onclick 那个匿名函数是在 badClosureExample () 函数运行完成后(用户单击链接时)才被调用的。而调用时,需要对变量 i 求值,解析程序首先会在事件处理程序内部查找,但 i 没有定义。然后,又到 badClosureExample () 函数中查找,此时有定义,但 i 的值是 4(只有 i 大于 4 才会停止执行 for 循环)。因此,就会取得该值 —— 这正是闭包(匿名函数)要使用其外部函(badClosureExample)作用域中变量的结果。而且,这也是由于匿名函数本身无法传递参数(故而无法维护自己的作用域)造成的。
那么这个例子的问题怎么解决呢?其实方法有很多 (自己不妨写一下看看),我认为比较简单直接的代码:


function popNum(oNum){
    
return function(){
                    alert(
'' 您单击的是第''+oNum+'' 个链接'');
   }
}
function badClosureExample(){
    
for (var i = 1; i <4; i++) {
        
var element = document.getElementById(''closureTest'' + i);
        element .onclick 
=new popNum(i);
        }
}

(2)、巧妙利用闭包绑定参数
 还是上面的 HTML 片段,我们要在用户单击第一个链接时延时弹出一个警告框,怎么实现?答案是使用 setTimeout () 函数,这个函数会在指定的毫秒数之后调用一个函数,如:
setTimeout(someFunc,1000);
但问题是,无法给其中的 someFunc 函数传递参数。而使用闭包则可以轻松解决这个问题:

function  goodClosureExample(oMsg){
    
return   function (){
        alert(oMsg);
    };
}

函数 goodClosureExample 用来返回一个匿名函数(闭包)。而我们可以通过为它传递参数来使返回的匿名函数绑定该参数,如:
var good = goodClosureExample ('' 这个参数是通过闭包绑定的 '');
而此时,就可以将绑定了参数的 good 函数传递给 setTimeout () 实现延时警告了:
setTimeout (good,1000) // 此时 good 中已经绑定了参数
最后,测试通过的完整代码:


window.onload = function(){
    
var element = document.getElementById(''closureTest0'');
    
if (element) {
        
var good = goodClosureExample('' 这个参数是由闭包绑定的'');
        element.onclick 
= function(){
            setTimeout(good, 
1000); // 延迟 1 秒弹出提示
        }
    }
}

3、javascript 的垃圾回收原理
(1)、在 javascript 中,如果一个对象不再被引用,那么这个对象就会被 GC 回收;
(2)、如果两个对象互相引用,而不再被第 3 者所引用,那么这两个互相引用的对象也会被回收。
在 js 中使用闭包,往往会给 javascript 的垃圾回收器制造难题。尤其是遇到对象间复杂的循环引用时,垃圾回收的判断逻辑非常复杂,搞不好就有内存泄漏的危险,所以,慎用闭包。ms 貌似已经不建议使用闭包了。

JavaScript 闭包(Closure)学习

JavaScript 闭包(Closure)学习

一、变量作用域

    JavaScript中,变量的作用域分为两种:全局变量和局部变量

        函数内部可以直接访问全局变量;

        反过来,函数外部不能访问局部变量;

    注意:

         函数内部声明变量时,一定要使用var关键字,不然实际上声明了一个全局变量

二、如何从外部读取局部变量

    JavaScript特有的“链式作用域”结构(Chain Scope):

        子对象会一级一级地向上寻找所有父对象的变量

    父对象的所有变量,对子对象都是可见的,反之则不成立

    JavaScript中函数名本身就是变量,所以函数也可以当做普通变量来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的返回值

    范例:

        function f1() {

            var n = 999;

            function f2() {

                alert(n);

            }

            return f2;

        }

        var res = f1();

        res(); // 999

三、闭包(Closure)概念

    上述范例中f2()函数,就是闭包。

    定义:

        在另一个作用域中保存了一份上一级函数或者作用域得到的变量,而这些变量不会随上一级函数的执行完成而销毁。

    简单理解:闭包就是能够读取其他函数内部变量的函数

        JavaScript语言中,只有函数内部的子函数才能读取局部变量,因此闭包可以理解为“定义在一个函数内部的函数

        本质上,闭包是将函数内部和函数外部连接起来的一座桥梁。

四、闭包的用途

    最大的用处有两个:

        一个是可以读取函数内部的变量

        两一个是让这些变量的值始终保存在内存中

    范例:

        function f1() {

            var n = 999;

            //nAdd是一个没有使用var的全局变量,并且指向了f1函数内部声明的匿名函数

            nAdd = function() {n+=1;}

            function f2() {console.log("n=" + n);};

            return f2;

        }   

        var result = f1();

        result(); // n = 999;

        nAdd();

        result(); // n = 1000

    解释:

        result引用了f2()闭包函数,共执行了两次,第一次输出999,第二次输出1000,证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动消除。

        原因:f1是f2的父函数,而f2被复制给一个全局变量(result),这导致f2始终在内存中,并且f2的存在依赖于f1,因此,f1也始终在内存中,不会在调用结束后被垃圾回收机制(garbage collection)回收。

五、使用闭包注意点

    1). 闭包会使得函数中的变量被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,IE中可能导致内存泄露。

        解决办法:退出函数前,将不使用的局部变量全部删除

    2). 闭包会在函数外部,改变父函数内部变量的值。所以,如果将父函数作为对象来使用,闭包就会作为对象的公用方法(public method),把内部变量当做对象的私有属性(private value),此时一定要注意,不要随便改变父函数内部变量的值。

    

我们今天的关于Javascript闭包Closure详解的分享已经告一段落,感谢您的关注,如果您想了解更多关于JavaScript 匿名函数(anonymous function)与闭包(closure)、JavaScript 匿名函数(anonymous function)与闭包(closure)_javascript技巧、javascript 闭包 (Closure) 初探、JavaScript 闭包(Closure)学习的相关信息,请在本站查询。

本文标签: