闭包

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

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

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

一个简单的闭包:

/* 分析:在同一个作用域内声明一个变量和函数,`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. windows直接安装

  2. Log4J实用配置指南

    转自:http://www.cnblogs.com/licheng/archive/2008/08/23/1274566.html 1         概述 本文档是针对Log4j日志工具的使用指南. ...

  3. php和AJAX用户注册演示程序

    <! doctype html public "-//w3c//dtd html 4.0//en" "http://www.w3.org/tr/rec-html14 ...

  4. discuz 同步登录问题

    最近一直在搞discuz论坛的二次开发,发现在论坛登录或退出的时候应用却没有同步登录或同步退出,这下子麻烦了,后来查看,原来没有产生js的同步代码,查找原因,发现$_G['setting']['all ...

  5. node.js 对接公众平台

    http://www.tfan.org/wp-content/uploads/使用-Nodejs-和-MongoDB-开发高性能微信公众平台应用.pdf

  6. telnet与ssh有什么不同呀

    含义: 1 使用Telnet这个用来访问远程计算机的TCP/IP协议以控制你的网络设备相当于在离开某个建筑时大喊你的用户名和口令.很快会有人进行监听,并且他们会利用你安全意识的缺乏.传统的网络服务程序 ...

  7. iOS App创建桌面快捷方式

    http://www.cocoachina.com/ios/20150827/13243.html 先mark,暂时用不到

  8. bzoj 2154 莫比乌斯反演求lcm的和

    题目大意: 表格中每一个位置(i,j)填的值是lcm(i,j) , 求n*m的表格值有多大 论文贾志鹏线性筛中过程讲的很好 最后的逆元我利用的是欧拉定理求解的 我这个最后线性扫了一遍,勉强过了,效率不 ...

  9. Android VersionedGestureDetector手势事件

    今天研究了一下PhotoView,发现里面的自定义的手势事件可以支持所有的SDK版本,该事件可以实现拖拽.滑动.缩放功能.下面直接上代码: public abstract class Versione ...

  10. [安卓]我的安卓开发FAQ

    1.什么是ADT: android官方解释是:ADT (Android Developer Tools) is a plugin for Eclipse that provides a suite o ...