闭包

闭包是什么,它们是如何工作的

闭包 是一个函数在创建时允许该自身函数访问并操作该自身函数之外的变量时所创建的作用域

即:闭包可以让函数访问所有的变量和函数,只要这些变量和函数存在于该函数声明时的作用域内就行。

一个简单的闭包:

/* 分析:在同一个作用域内声明一个变量和函数,`outerFunction`可以看到并访问`outerValue`。
* 此处是全局作用域,该作用域实际上就是一个闭包,从未消失过(因为页面已经被加载了)。
*/ var outerValue = "ninja"; function outerFunction() {
alert(outerValue);
} outerFunction(); // ninja

更深入的闭包:

var outerValue = "ninja";
var later; // 声明一个全局变量 function outerFunction() {
// 在函数内部声明一个值。该变量的作用域是限制在该函数内部,并且在函数外部访问不到
var innerValue = "samurai"; // 在外部函数内,声明一个内部函数。注意,声明该函数时,innerValue是在作用域内的。
function innerFunction() {
alert(outerValue);
alert(innerValue);
} later = innerFunction; // 将内部函数引用到later变量上
} /* 调用外部函数,将会声明内部函数,并将内部函数赋值给later变量
* 但并不输出
*/
outerFunction(); /* 通过later调用内部函数
*不能直接调用内部函数,因为它的作用域(和innerValue一起)被限制在outerFunction()内
*/
later(); // ninja,samurai
* 分析:
  • 当调用 later 执行内部函数时,外部函数的作用域早已不复存在,但是 innerValue 变量仍然“活着”,这要归功于闭包。

  • 在外部函数中声明 innerFunction() 的时候,不仅是声明了函数,还创建了一个闭包。该闭包不仅包含函数声明,还包含了函数声明的那一时刻点上该作用域中的所有变量。

  • 针对在函数声明那一时刻点的作用域内的所有函数和变量,闭包创建了一个“安全气泡”,因此函数获得了执行操作所需的所有东西。

闭包的三个有趣的概念:

  1. 内部函数的参数是包含在闭包中的。
  2. 作用域之外的所有变量,即便是函数声明之后的那些声明,也都包含在闭包中。
  3. 相同的作用域内,尚未声明的变量不能进行提前引用。

例子:

var outerValue = "ninja";
var later; function outerFunction() {
var innerValue = "samurai"; function innerFunction(paramValue) {
console.log(outerValue);
console.log(innerValue);
console.log(paramValue);
console.log(tooLate);
} later = innerFunction;
} console.log(tooLate); // undefined var tooLate = "ronin"; outerFunction(); later('wakizashi'); // ninja,samurai,wakizashi,ronin

利用闭包简化开发

闭包的常见用法:

  • 私有变量 :封装一些信息作为“私有变量”,即,限制这些变量的作用域。

      function Ninja() {
    var age = 10; this.getAge = function() {
    return age;
    } this.setAge = function() {
    age++;
    }
    } var ninja = new Ninja();
    ninja.setAge();
    ninja.getAge(); // 11
  • 回调与计时器 : 在这种情况下,函数都是在后期未指定的时间进行异步调用,在这种函数内部,我们经常需要访问外部数据,闭包可以作为一种访问这些数据的很直观的方式,特别是当我们希望避免创建全局变量来存储这些信息时。

      // 示例1:在 Ajax 请求的 callback 里使用闭包
    jQuery('#testButton').click(function() {
    var elem$ = jQuery('#testSubject'); elem$.html("Loading..."); jQuery.ajax({
    url: "test.html",
    success: function(html) { // 通过闭包引用了elem$变量
    console.log(elem$);
    elem$.html(html);
    }
    });
    }); // 示例2:在计时器间隔回调中使用闭包
    function animateIt(elementId) { var elem = document.getElementById(elementId);
    var tick = 0; var timer = setInterval(function() {
    if (tick < 100) {
    elem.style.left = elem.style.top = tick + "px";
    tick++;
    } else {
    clearInterval(timer);
    console.log(tick == 100);
    console.log(elem);
    console.log(timer);
    }
    }, 10);
    } animateIt('box');

没有闭包,同时做多件事情的时候,无论是事件处理,还是动画,甚至是 Ajax 请求,都将是及其困难的。

函数在闭包里执行的时候,不仅可以在闭包创建的时刻点上看到这些变量的值,我们还可以对其进行更新。换句话说,闭包不仅是在创建那一时刻点的状态的快照,而且是一个真实的状态封装,只要闭包存在,就可以对其进行修改。

利用闭包提高性能&&解决常见的作用域问题

1.绑定函数上下文

bind

Prototype 的 bind()(或者是我们自己实现的),并不意味着它是 apply()call() 的一个替代方法,该方法的潜在目的 是通过匿名函数和闭包控制后续执行的上下文。这个重要的区别使 apply()call() 对事件处理程序和定时器的回调进行延迟执行特别有帮助。

2.偏应用函数

柯里化:在一个函数中首先填充几个参数(然后再返回一个新函数)的技术。

3.函数重载

(1)缓存记忆

(2)函数包装

4.即时函数

即时函数(立即执行函数):依赖于对闭包的充分利用。

(function() {
statement..;
})();
  • 创建一个函数实例。
  • 执行该函数。
  • 销毁该函数(因为语句结束以后,,没有任何引用了)。

即时函数的用处:

- 临时作用域和私有变量

(1)创建一个独立作用域:

利用即时函数,我们可以利用其内部作用域来创建一个临时的作用域,用于存储数据状态。

记住,JavaScript 中的作用域依赖于定义变量的函数。在很多编程语言中,作用域是依赖于代码块的,但在 JavaScript 中,变量的作用域依赖于变量所在的闭包。

// method 1
(function() {
var numClicks = 0;
document.addEventListener("click", function(){
alert( ++numClicks );
},false);
})(); // method 2
document.addEventListener("click", (function() {
var numClicks = 0;
return function() {
alert( ++numClicks );
}
})(), false);
  • 这是一种最常见的即时函数使用方式:简单,自包装功能。各功能所需的变量都保存在闭包内,但对其他地方却都不可见。

(2)通过参数限制作用域内的名称

(function(what) {
alert(what);
})('Hi there!');

(3)使用简洁名称让代码保持可读性

(function(v) {
Object.extend(v, {
href: v._getAttr,
src: v._getAttr,
...
});
})(Element.attributeTranslations.read.values);

这种在作用域内创建临时变量的技巧,对没有延迟调用的循环遍历来说尤其有用。

- 循环

闭包记住的是变量的引用,而不是闭包创建时刻该变量的值。

var div = document.getElementsByTagName("div");

for (var i=0; i < div.length; i++) {
(function(n) {
div[n].addEventListener("click", function() {
alert("div #" + n + " was clicked.");
}, false);
})(i);
}
- 类库包装

闭包和即时函数可以帮助我们让类库尽可能的保持私有,并且可以选择性的让一些变量暴露到全局命名空间内。

// method 1
(function() {
var jQuery = window.jQuery = function() {
// Initialize
}; // ...
})(); // method 2
var jQuery = (function() {
function jQuery() {
// Initialize
} // ... return jQuery;
})();

《JavaScript Ninja》之闭包的更多相关文章

  1. JavaScript函数、闭包、原型、面向对象

    JavaScript函数.闭包.原型.面向对象 断言 单元测试框架的核心是断言方法,通常叫assert(). 该方法通常接收一个值--需要断言的值,以及一个表示该断言目的的描述. 如果该值执行的结果为 ...

  2. 深入理解javascript原型和闭包 (转)

    该教程绕开了javascript的一些基本的语法知识,直接讲解javascript中最难理解的两个部分,也是和其他主流面向对象语言区别最大的两个部分--原型和闭包,当然,肯定少不了原型链和作用域链.帮 ...

  3. JavaScript葵花宝典之闭包

    闭包,写过JS脚本的人对这个词一定不陌生,都说闭包是JS中最奇幻的一个知识点,  虽然在工作中,项目里经常都会用到~  但是是不是你已经真正的对它足够的了解~~ 又或者是你代码中出现的闭包,并不是你刻 ...

  4. 深入理解javascript原型和闭包系列

    从下面目录中可以看到,本系列有16篇文章,外加两篇后补的,一共18篇文章.写了半个月,从9月17号开始写的.每篇文章更新时,读者的反馈还是可以的,虽然不至于上头条,但是也算是中规中矩,有看的人,也有评 ...

  5. 让你分分钟学会Javascript中的闭包

    Javascript中的闭包 前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它 ...

  6. 深入理解javascript原型和闭包(1)——一切都是对象

    “一切都是对象”这句话的重点在于如何去理解“对象”这个概念. ——当然,也不是所有的都是对象,值类型就不是对象. 首先咱们还是先看看javascript中一个常用的函数——typeof().typeo ...

  7. 深入理解javascript原型和闭包(2)——函数和对象的关系

    上文(理解javascript原型和作用域系列(1)——一切都是对象)已经提到,函数就是对象的一种,因为通过instanceof函数可以判断. var fn = function () { }; co ...

  8. 深入理解javascript原型和闭包(3)——prototype原型

    既typeof之后的另一位老朋友! prototype也是我们的老朋友,即使不了解的人,也应该都听过它的大名.如果它还是您的新朋友,我估计您也是javascript的新朋友. 在咱们的第一节(深入理解 ...

  9. 深入理解javascript原型和闭包(4)——隐式原型

    注意:本文不是javascript基础教程,如果你没有接触过原型的基本知识,应该先去了解一下,推荐看<javascript高级程序设计(第三版)>第6章:面向对象的程序设计. 上节已经提到 ...

  10. 深入理解javascript原型和闭包(5)——instanceof

    又介绍一个老朋友——instanceof. 对于值类型,你可以通过typeof判断,string/number/boolean都很清楚,但是typeof在判断到引用类型的时候,返回值只有object/ ...

随机推荐

  1. centos 5.8 64位系统安装 mysql5.6

    mysql5.5以上的版本编译需要 cmake         1 .安装cmake wget http://www.cmake.org/files/v2.8/cmake-2.8.10.2.tar.g ...

  2. POJ 3206 最小生成树

    DESCRIPTION:T_T 在下是读不懂题意的.但是捏.现在知道是求把所有的点(是字母的点)连起来的最小的权值.即最小生成树.因为求最小生成树是不计较源点是哪个的.所以可以把A和S看成一样的.首先 ...

  3. spring mvc如何获取问号后的url参数

    @RequestMapping(method=RequestMethod.GET) public ModelAndView allUsers(@RequestParam int page){ Mode ...

  4. 转: HTML的电子邮件链接标签mailto用法详解

    mailto是网页设计制作中的一个非常实用的html标签,许多拥有个人网页的朋友都喜欢在网站的醒目位置处写上自己的电子邮件地址,这样网页浏览者一旦用鼠标单击一下由mailto组成的超级连接后,就能自动 ...

  5. JobTracker启动流程源码级分析

    org.apache.hadoop.mapred.JobTracker类是个独立的进程,有自己的main函数.JobTracker是在网络环境中提交及运行MR任务的核心位置. main方法主要代码有两 ...

  6. HMM TOOL

    HMM隐马尔科夫模型 MATLAB 工具包对各种数据的处理 HMM 工具包下载地址: http://www.cs.ubc.ca/~murphyk/Software/HMM/hmm.html 工具包使用 ...

  7. 隐藏与显示:display/visibility/visible区别

    说到标签的隐藏,你们会用到什么呢?display?visibility?还是服务器控件的visible? 显然,这三者都能起到隐藏与显示的效果,但是用途确完全不一样,请看用法与区别: <div ...

  8. git命令学习用

  9. android中正确导入第三方jar包

    android中正确导入第三方jar包 andriod中如果引入jar包的方式不对就会出现一些奇怪的错误. 工作的时候恰好有一个jar包需要调用,结果用了很长时间才解决出现的bug. 刚开始是这样引用 ...

  10. IIS 6.0 401 错误

    1.错误号401.1 症状:HTTP 错误 401.1 - 未经授权:访问由于凭据无效被拒绝 分析:  由于用户匿名访问使用的账号(默认是IUSR_机器名)被禁用,或者没有权限访问计算机,将造成用户无 ...