`
毒药001
  • 浏览: 5348 次
  • 性别: Icon_minigender_1
  • 来自: 昆明
最近访客 更多访客>>
社区版块
存档分类
最新评论

学习面向对象javascript(三)闭包

 
阅读更多

在这篇文章中,我们会了解到一些闭包的知识。在了解闭包之前,我们先了解一下 javascript 作用域的概念。

作用域链

我们知道,在javascript中,是以function作为域的界限,而不是像其他很多语言一样,是以大括号作为边界的。一个定义在函数内部的变量,在函数外部是访问不了的,但是在代码块里面定义的变量,比如一个for循环中定义的变量,在代码块外面是可以被访问到的。

var a = 1; 
function f(){
    var b = 1;
    return a;
}
f();  // 1
b;  // b is not defined

变量a是一个全局域的,然而b则是在函数f()内部作用域的,所以:
在f()中,a和b都可以被访问到。
在f()外,a可以被访问到,但是b不能。
如果你在f()内部定义一个function n(),那么n()将有访问他自己域和父函数f()域的权限。这就是作用域链,并且链可以任意深。

词法作用域

在javascript中,函数具有词法作用域。意思是,在函数被定义的时候创建他的作用域,而不是在被调用时才创建。例如:

function f1(){
    var a = 1; 
    f2();
}
function f2(){
    return a;
}
f1();  // a is not defined

在函数f1()内部调用了函数f2()。因为变量a也在f1()内部,所以你的第一反应可能会觉得在f2()中可以访问a,但是并不是这样的。在f2被定义的时候,并没有a这个变量。f2()就像f1()一样,只能访问它自己的作用域和全局作用域。他们并没有共享出他们的本地作用域。

当一个函数被定义时,它会记住它自己的环境、作用域链。这并不意味着他能够知道他作用域里的每一个变量。相反的,我们可以添加、删除、更新函数作用域里面的变量,并且函数可以得到最新的变量的状态。如何在上面的例子中加一个全局变量a,那么f2()就可以访问得到。

var a = 5;
f1();  // 5
a = 55;
f1();  // 55
delete a;  // true
f1();  // a is not defined

这种特性给了javascript很大的灵活性。你可以添加和删除变量,并且重新添加他们之后,一样可以继续运行。下面我们试一下把f2()删除以后重新定义他。

delete f2;  // true
f1();  // f2 is not defined
var f2 = function(){
    return a * 2;
}
var a = 5; 
f1();  // 10

用闭包切断链

首先,我们用一些图来描述一下闭包。
这是一个全局作用域,就像一个宇宙,包含了所有东西。

它包含了变量a和函数F。

函数有它自己的私有空间来存储它内部的变量和函数,在某些情况下,会像下面的图一样。

如果你在a点,那么你在全局空间中。如果你在函数F中的b点,那么你即在全局空间中,又在函数F的空间中。如果你在函数N中的c点,那么你即在全局空间中,又在函数F的空间中,还在函数N的空间中。但是反过来,b不在函数N的空间里,a不在函数F和函数N的空间里。因此,在a点,不能访问b和c的东西,在b点,不能访问c点的东西。但是在c点可以访问b和a的东西,b点可以访问a点的东西。但是,当函数N从F里面出来到全局空间的时候,这就形成了闭包。

这时,N已经像a一样在全局空间中,并且函数N记住了他自己的状态,N仍然可以访问F的空间中的b。
如何让N像上面这样切断链呢?下面来看一下具体的代码是怎么实现的。
第一种可以把N声明成为一个全局的函数。

function f(){
  var b = "b"; 
  n = function(){
    return b;
  }
}

在函数中定义了一个变量n,由于没有加var,所以它成为了一个全局函数,但是为了养成好的习惯,建议还是在全局加上一个变量声明。

var n;
function f(){
  var b = "b"; 
  n = function(){
    return b;
  }
}
f();

当我们执行了函数f以后,函数n有了他的方法体,由于n在函数f里面,所以可以访问到f的变量b。就是出了函数f的作用域成为一个全局函数,它一样可以访问到f的变量b。

n();  // "b"

第二种可以在F中,把N返回到全局空间中。
仍然使用上面的函数,但是这次的函数和前面的有点区别。

function f(){
  var b = "b"; 
  return function(){
    return b;
  }
}

 函数包含一个局部变量b,因此,从全局是无法访问到的。

b; b is not defined

但是函数返回了另一个函数,我们可以把他想象成上面的N。这个新的函数可以访问到他的私有空间,f的空间和全局的空间。所以,他可以访问到b。由于在全局区域可以访问函数f,因此,可以把他的返回值重新赋值给一个新的全局变量。这就成为一个可以访问函数f私有空间的一个全局函数。

var n = f();
n();  // "b"

因此,你可以认为闭包就是创建一个能够在其父函数return以后仍然在父作用域中保持关联的一个函数。
当你传入参数到一个函数,你也可以创建一个函数来return他的父函数的参数。

function f(arg) {
  var n = function(){
    return arg;
  }; 
  arg++; 
  return n;
}
var m = f(123);
m();  // 124

循环中的闭包

首先来看一下这个程序。

function f() {
  var a = [];
  var i; 
  for(i = 0; i < 3; i++) {
    a[i] = function(){
      return i;
    }
  } 
  return a;
}

 运行此函数以后赋值给数组a

var a = f();

 现在,我们运行数组a中的各个元素。

for(var j=0;j<a.length;j++){
    alert(a[j]());
}

可以发现,浏览器弹出3次3。其实,我们在创建了3个指向局部变量i的闭包。闭包并没有记住i的值,而是指向i并返回它当前的值。所以在循环之后,i的值是3。所以就会有我们看到的结果。
要是我们想得到结果是弹出0,1,2的话,应该怎么改呢?其实我们可以利用自执行函数来实现。

function f() {
  var a = [];
  var i;
  for(i = 0; i < 3; i++) {
    a[i] = (function(x){
      return function(){
        return x;
      }
    })(i);
  }
  return a;
}
var a = f();
for(var j=0;j<a.length;j++){
    alert(a[j]());
}

 另外,我们也可以用一个内部函数来解决这样的问题。用这个函数在每次循环中都得到i的值。

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;
}

Getter/Setter

在Getter/Setter中,我们同样可能会用到闭包。比如:

var getValue, setValue; 
(function() {
  var secret = 0; 
  getValue = function(){
    return secret;
  };
  setValue = function(v){
    secret = v;
  }; 
})()

这样,当我们想得到一个比较保密的变量,但是又不想让外部能够直接得到这个变量的时候,我们可以用这样的方式来隐藏这些比较保密的变量。

setValue(123);
getValue();  // 123

 Iterator

在有的时候,由于一个数组里的元素的结构太过于复杂。要循环这个数组就显得比较麻烦。那么,我们可以把next这个逻辑包含在一个函数内。这样我们就可以通过连续调用这个函数来得到数组里面各个值。

function setup(x) {
  var i = 0; 
  return function(){
    return x[i++];
  };
}
var next = setup(['a', 'b', 'c']);
next();  // "a"
next();  // "b"
next();  // "c"

 

 

利用闭包这样可以把函数内部的变量的生命周期延长,可以把局部变量暴露的特性,我们可以用javascript实现很多复杂的功能。

分享到:
评论

相关推荐

    面向对象JavaScript开发

    面向对象JavaScript开发是现代Web开发中不可或缺的一部分,它使得JavaScript能够构建复杂、可维护的应用程序。 面向对象编程是一种基于对象和类的编程范式,它强调数据和操作数据的方法的封装。在JavaScript中,...

    基于闭包的JavaScript面向对象编程框架.pdf

    “基于闭包的JavaScript面向对象编程框架” 本文总结了基于闭包的JavaScript面向对象编程框架的设计和实现。通过使用闭包,实现了基于类的面向对象编程的封装、继承和多态特征。 闭包(Closure)是JavaScript中的...

    Javascript面向对象编程.

    在JavaScript中,面向对象主要通过以下三种方式实现: 1. **构造函数(Constructor)**:构造函数是一种特殊的函数,用于创建和初始化对象。我们可以通过`new`关键字来调用构造函数,创建一个新的对象实例。例如: ...

    JavaScript面向对象编程指南.pdf

    JavaScript作为一门浏览器语言的核心思想;面向对象编程的基础知识及其在... 《JavaScript面向对象编程指南》着重介绍JavaScript在面向对象方面的特性,展示如何构建强健的、可维护的、功能强大的应用程序及程序库

    JavaScript面向对象编程指南

    《JavaScript面向对象编程指南》内容包括:JavaScript作为一门浏览器语言的核心思想;面向对象编程的基础知识及其在JavaScript中的运用;数据类型、操作符以及流程控制语句;函数、闭包、对象和原型等概念,以代码...

    JAVASCRIPT 面向对象编程精要

    ### JavaScript面向对象编程精要 #### 一、引言 JavaScript是一种灵活且强大的脚本语言,它虽然起源于一种简单的浏览器脚本语言,但随着时间的发展,JavaScript已经成为了一种功能全面的编程语言,尤其是在Web开发...

    javascript面向对象编程

    面向对象编程的基础知识及其在JavaScript中的运用;数据类型、操作符以及流程控制语句;函数、闭包、对象和原型等概念,以代码重用为目的的继承模式;BOM、DOM、浏览器事件、AJAX和JSON;如何实现JavaScript中缺失的...

    javascript 经典面向对象设计

    ### JavaScript经典面向对象设计 #### 标题与描述解析 标题“JavaScript经典面向对象设计”指出...通过学习本书中的概念和技术,开发者可以更好地理解如何利用面向对象编程的优势,构建出更加健壮和灵活的应用程序。

    JavaScript面向对象编程指南(第2版).rar

    JavaScript是一种广泛...通过深入学习这本《JavaScript面向对象编程指南(第2版)》,开发者不仅能掌握JavaScript的面向对象编程基础,还能了解到实际项目中如何有效地运用这些知识,提升编程技巧和解决问题的能力。

    JavaScript面向对象精要(英文版)

    ### JavaScript面向对象精要 #### 一、概述 ...通过学习本书,开发者能够更好地理解JavaScript的底层工作原理,掌握高效的编码技巧,并能灵活运用面向对象的设计原则来构建可维护性强的应用程序。

    JavaScript面向对象编程案例

    JavaScript的面向对象主要基于以下三个核心概念: 1. **对象**:对象是JavaScript中的基本单位,它们由属性(key-value对)和方法(可执行的函数)组成。你可以通过创建一个`{}`字面量或`new Object()`来创建一个空...

    面向对象javascript笔记

    面向对象的JavaScript编程是JavaScript开发中的重要概念,它允许我们以类和对象的...以上就是对"面向对象javascript笔记"所涵盖知识点的详细解析。理解并掌握这些概念对于深入理解和高效地编写JavaScript代码至关重要。

    JavaScript面向对象的支持

    JavaScript是一种广泛应用于Web开发的脚本语言,它虽然支持面向对象编程,但其面向对象的实现方式与其他传统面向对象语言(如Java或C++)有所不同。JavaScript中的面向对象特性主要体现在以下几个方面: 1. **基于...

    JavaScript碎片—函数闭包(模拟面向对象)

    JavaScript函数闭包是模拟面向对象编程的一种技术,它允许函数记住并访问其词法作用域,即使在函数执行完毕后也能保持对其的访问。这种特性使得JavaScript能够在没有内置类和继承等传统面向对象特性的情况下实现类似...

    JS面向对象经典案例

    在本文中,我们将介绍JavaScript面向对象编程中的经典案例,包括对象、类、继承、原型链和闭包等概念。 一、对象和类 在JavaScript中,对象是指一个实体,可以拥有自己的属性和方法。对象可以使用工厂函数或构造...

Global site tag (gtag.js) - Google Analytics