闭包:  //JS函数式风格中,在内部保存数据和对外无副作用这两个特性主要就是通过闭包实现的;

函数与闭包: 一个函数是一段静态代码,它是一个代码书写时已经编译期,静态概念;闭包是函数在代码运行过程中产生的一个动态环境,是一个运行期,动态的概念;

函数引用和函数实例://在被调用时,每个函数实例至少拥有一个闭包;

//函数引用
function myFunc2() { };
var f1 = myFunc2;
var f2 = myFunc2;
console.log(f1 === f2); //true
//函数实例
function MyObject1() {
function func() { }
this.doFunc = func;
}
var obj1 = new MyObject1;
var obj2 = new MyObject1; console.log(obj2 === obj1); //false
console.log(obj2.doFunc === obj1.doFunc); //false
console.log(obj2.toString() == obj1.toString()); //true

//常见的两种构造对象的方法,产生对象实例的效果也并不一样;demo

 闭包和调用对象:

  • 闭包相关元素(调用对象,上下文环境等)的内部数据结构;  //这个结构同样使用与全局对象,只是TheContext内容为空;

    • TheContext结构描述函数作为对象时外在表现;
    • ScriptObject(调用对象)包含函数全部代码的语法分析;包括内部变量表:varDecls,内嵌函数表:funcDecls,以及此外全部代码:source;
      • varDecls总是在语法分析阶段就创建好,所以函数内会有变量提升效果,且初始化为undefined;
      • 函数执行退出时,varDecls不被重置,所以具有在函数内保存数据的效果;
      • 函数内数据持续的生成周期,取决于该函数实例是否存在活动引用;如果没有,ScriptObject会被内存回收;
  • 函数闭包与调用对象的生存周期

    • 函数执行创建函数实例时:

      • 创建一个函数实例;
      • 为该函数实例创建一个闭包;
      • 为该函数实例(及闭包)的运行环境从ScriptObject复制一个调用对象;

闭包的特性:

  • 引用与泄漏:

    • 除了变量引用,JS中最常用的是对象属性引用:

      • 对象在构造时,使用this引用进入构造函数;
      • 对象在调用方法时,使用this引用进入函数;
      • 某函数使用apply/call调用,并传入某个对象作为this引用;
      • 调用一个函数时,对象从参数入口传入;
    • 函数实例被创建和引用的过程: //有些对象不被销毁或销毁时不能通知JS引擎,所以有些JS闭包总不能被销毁,会形成内存泄漏;
        function MyObject(obj) {
      var foo = function() {};
      if(!obj) return;
      obj.method = foo;
      }
      • MyObject();    //函数执行后一个匿名函数实例被创建并赋值给foo变量,到函数执行后,闭包内的数据未被外部引用,闭包马上销毁,foo指向的匿名函数也被销毁;
      • MyObject(new Object); //函数执行后,匿名函数与MyObject都不能被销毁;但之后传入对象未被引用,依次随机销毁了;之后obj.method引用被释放;匿名函数也没有其他引用,开始闭包销毁过程;
      • var obj = new Object; MyObject(obj);  //函数执行后,由于有函数外部obj引用,所以JS引擎会维护MyObject()闭包中foo变量的关系,直到变量被销毁或指定方法被重置,删除:如

        • obj.method = new Function();
          delete obj.method;
  • 函数闭包特性总结:

    • JS中函数被调用,总会初始化一个闭包;

    • JS中函数实例可能有多个闭包;  //重复调用

    • JS中函数实例和闭包的生存周期是分别管理的;

    • 在函数执行中闭包没有被其他对象引用,则在函数执行结束之时也被销毁;  

 语句中闭包问题:

  • 问题:语句级别中创建函数实例也会创建一个对应的闭包;当同时创建多个实例时,它们仍共享外层函数闭包(语句中是全局闭包)中的upvalue值

     var obj1 = new Object;
    var events = {m1: 'clicked', m2: 'changed'};
    for(e in events) {
    obj1[e] = function() {
    console.log(events[e]);
    }
    }
    console.log(obj1.m1 === obj1.m2); //false; obj1.m1(); //changed
    obj1.m2(); //changed

    上例中创建的两个实例,在执行的时候其闭包环境是全局闭包,共享相同值e;

  • 解决:
     for(e in events) {
    obj2[e] = function(aValue) { //闭包1
    return function() { //闭包2
    console.log(events[aValue]);
    }
    }(e)
    }

    上例中创建的两个实例所在的是闭包1,每个实例对应的闭包会暂存不同的传入参数;   其实这里只是需要暂存值e的地方,并不一定要多加一层闭包,并且多加闭包增加了消耗,可以这样优化

     for(e in events) {
    (obj3[e] = function() {
    console.log(events[arguments.callee.aValue]);
    }).aValue = e;
    }

闭包中标识符特例:

  • 函数内标识符绑定顺序:

    • 内部函数声明优于参数名;
    • 内部函数或参数中有arguments名称的标识符时,当前函数的arguments不被创建;
    • 函数内的局部变量声明时(不是赋值),如果标识符已经被绑定,则忽略变量标识符声明;
  • 对应函数名,引擎会为函数准备好一个闭包,开始绑定其他标识符(然后开始执行)之前,引擎会在闭包中初始化这个函数名,并将其绑定到函数自身;所以函数名是闭包中最先被初始化的标识符;

函数对象的闭包:

  • Function构造器在任意位置创建实例,都处于全局闭包中;即Function实例的upvalue总是指向全局闭包;原因在于Function构造器传入的参数都是字符串,不必与函数局部变量建立引用;基于此对前面的问题可以这样写

    var events = {m1: 'clicked', m2: 'changed'};
    for(e in events) {
    obj[e] = new Function('console.log(events["'+e+'"])');
    }

闭包与可见性:

  • 闭包带来的可见性效果:具体实现上,JS只要求在语法分析期将一个函数体中所有用var声明的变量记入自己的ScriptObject的varDecls域,然后设定访问规则,如果当前函数的ScriptObject.varDecls域中不存在该变量声明,则通过闭包.parent来取得上一层函数的scriptObject.varDecls作为upvalue.

    function foo() {
    var A = 100;
    function foo_2() {
    var A = 1000;
    function foo_3() { }
    }
    }
    • 可见性传递的实质是foo_2()与foo_3()都能访问来自与foo()的同一份ScriptObject.varDecls;
    • 可见性覆盖的实质是foo_3()在foo_2()的ScriptObject.varDecls中找到名为A的变量,因而不必再向上层回溯;
    • 变量在代码任何位置隐式声明,都全局可见的实质是该变量标识符不在函数内所有parent的ScriptObject.varDecls中存在时,必然回溯到顶层全局函数闭包中的varDecls,并在该位置隐式地声明了一个变量;
    • 正是由于每一个函数实例都有一份ScriptObject的副本,所有不同闭包访问到的私有变量不一致;

js:语言精髓笔记10--闭包的更多相关文章

  1. js:语言精髓笔记11--动态语言特性(1)

    语言:程序最终被表达为数据(结构)和逻辑(算法),命令式和说明式/函数式语言分别从这两方面分类: 动态:在语言陈述时无法确定,必须在计算机执行时才能确定语言关系:JS是完全动态语言,导致其不确定性一般 ...

  2. js:语言精髓笔记9--函数式语言特征

    形式化运算系统的研究: 图灵:提出图灵机形式系统,通过0,1运算系统来解决复杂问题: 冯诺依曼:提出了冯诺依曼体系:即通过修改内存反映运算结果: 阿隆左.丘奇:提出新的运算范型Lambda演算,计算机 ...

  3. js:语言精髓笔记12--动态语言特性(2)

    对于括号内: 通过赋值时发生的重写: (Object1 = function() {}).prototype.value = 100; var obj1 = new Object1; console. ...

  4. js:语言精髓笔记8--对象系统

    封装: 一把对象系统,封装是由语法解析来实现的,即语法作用域:但js是动态语言,因此只能依赖变量作用域: js的变量作用域只有表达式,函数,全局三种:所以js只能实现public和private两种封 ...

  5. js:语言精髓笔记7----原型继承

    面向对象有三个基本特性:封装,继承,多态:如果都满足的话称为面向对象语言:而部分满足则称为基于对象语言: 数据类型实现模型描述: JavaScript对象模型: 构造过程:函数->构造器 构造器 ...

  6. js:语言精髓笔记5----语言分类

    计算模型:源于对计算过程的不同认识: 1.基于不同计算模型一般分为://教科书的一般分类 命令式语言: 函数式语言: 逻辑式语言: 面向对象程序设计语言: 2.基于程序本质分类:  //编程的经典法则 ...

  7. js:语言精髓笔记2--表达式

    表达式:由运算符和运算元构成:JS中没有运算符的表达式称为单值表达式:没有运算元,孤立与代码上下文的运算符是不符合语法的:(表达式是有返回值的) 单值表达式: this引用: 变量引用: 直接量: n ...

  8. js:语言精髓笔记1--标识符与基本类型

    标识符: 命名: 语法以及类型----语法关键字                                           //逻辑 值(的存储位置)----变量和常量           ...

  9. js:语言精髓笔记13--语言技巧

    消除代码全局变量名占用: //本质是使用匿名函数: void function(x, y, z) { console.log(x + y + z); }(1,2,3); //要使函数内的变量不被释放, ...

随机推荐

  1. 高效使用git的一些命令

    1,添加文件到版本库        添加单个文件: git add filename        添加所有txt文件: git add *.txt        添加所有修改文件: git add ...

  2. RootKit学习之 IDT Hook

    0x00 前言  IDT(Interrupt Descriptor Table)中断描述符表,中断就是停下现在的活动,去完成新的任务.一个中断可以起源于软件或硬件.比如,出现页错误,调用IDT中的0x ...

  3. mac安装最新版本的git

    文章转载自我的个人博客 http://www.iwangzheng.com/ 1.下载一个安装包哦 在mac下安装git,首先下载一个git安装包,这里我选的是git-1.9.3.tar , 因为这是 ...

  4. 重构edit 和 new页面

    www.iwangzheng.com 由于edit和new页面的相似部分很多,需要提取出来,现在就是提取的方法 从form 的开始部分选中,shift+v选中对应的行 :Rextract form 然 ...

  5. #define 和typedef的区别

    typedef和define的详细区别 2011-04-19 15:11 firnow firnow 字号:T | T 对于都可以用来给对象取一个别名的Typedef和define来说,是有区别的.本 ...

  6. navicat连接oracle报错ORA-12737: Instant Client Light: unsupported server character set CHS16GBK”

    原文如下http://blog.163.com/cp7618@yeah/blog/static/7023477720142154449893/?COLLCC=1318255100& 这个工具可 ...

  7. Git SSH Key 生成步骤

    it是分布式的代码管理工具,远程的代码管理是基于ssh的,所以要使用远程的git则需要ssh的配置. github的ssh配置如下: 一 . 设置git的user name和email: $ git ...

  8. ethtool 命令详解

    命令描述: ethtool 是用于查询及设置网卡参数的命令. 使用概要:ethtool ethx       //查询ethx网口基本设置,其中 x 是对应网卡的编号,如eth0.eth1等等 转自: ...

  9. smarty模版出现错误提示出现了不期望的字符

    2013年7月5日 08:38:49 提示 unexpected "字符或字符串" 查找前边的代码,看是否有字符串单引号或双引号没有成对出现的情况

  10. 2.django笔记之缓存,session,cookie,ajax

    一.django缓存 1.缓存 缓存的意义在于把昂贵的计算结果保存起来一遍下次的访问,有缓存的站点的流程大概是这样子的: 给定一个url,检查页面是否在缓存中 如果在,返回缓存的页面 否则,生成该页面 ...