闭包

来源:互联网 发布:蓝月传奇淘宝礼包 编辑:程序博客网 时间:2024/05/20 01:45

有点蒙蔽,总结一哈,也不知道对不对。。

闭包:有权访问另一个函数作用域中的变量的函数。

一、基础闭包。

由一下俩特性:

1. 内部函数可以用外层的变量(作用链current > out > outout > outoutout ...)

2. 函数名是一个指针,所以可以很多名字指向一个函数体

组合改编(把内部函数作为返回值)一下成为最简单的闭包:

function outFunc(){var woshiwaimiandebianliang = "a";return inFunc;   //千万~!不能写括号,写括号代表调用,只一个名字代表指向的函数体,返回的是函数体function inFunc(){       woshiwaimiandebianliang = "我在里面,我可以用外面的变量";}/*  可以省略内部函数的函数名return function(){woshiwaimiandebianliang = "我在里面,我可以用外面的变量";}*/}var wokeyizhixiangshangmiannagehanshu = outFunc();


二、闭包与变量

副作用:闭包只能取得函数中任何变量的最后一个值。
function outFunc(){var result = new Array();for(var i=0; i<10; i++){result[i] = function(){return i;};}return result;}

高程:因为每个函数的作用域链中都保存着outFunc()函数的活动对象,所以每次引用的都是同一个i(最后一个:10),即每次都返回10;
详解:首先要知道一点:仅仅声明某一个函数,引擎并不会对函数内部的任何变量进行查找或赋值操作。只会对函数内部的语法错误进行检查(如果往内部函数加上非法语句,那么不用调用也会报错)(摘自网络),只有在运行该函数的时候才会对内部的变量进行查找操作(感觉是编译原理?)。所以是酱滴:result[3] = function(){ return i ;//i不是常量而是变量}; 只有在运行是才会在作用域链中查找i,找到在外层此时已经为10的i。

解决:
用一个函数强行使i成为常量(传参的方式):
function outFunc(){var result = new Array();for(var i=0; i<10; i++){result[i] = fn(i);}function fn(num){return function(){return num;};};return result;}

简写成匿名函数:
function outFunc(){var result = new Array();for(var i=0; i<10; i++){result[i] = function(num){return function(){return num;};}(i);}return result;}

可见:闭包与变量的经典问题 http://www.cnblogs.com/kindofblue/p/4907847.html



三、this对象

var name = "The Window";var object = {name : "My Object",getNameFunc : function(){return function(){return this.name;};}};alert(object.getNameFunc()); //function(){return this.name;}; 调用该函数返回一个函数alert(object.getNameFunc()()); //The Window  调用该函数返回的函数
问题在于为什么第二个输出为"The Window"而不是"My Object"。
解析:首先要明确this返回的是 调用本函数的对象。所以当运行时会在作用域链中查找this首先找函数本身的this(肯定会找到所以不会再向上层查找),调用本函数的对象是windows,所以返回windows下的name。
解决:若想返回object的name只需利用闭包的性质 在object内存下此时的对象(var that = this;)然后再返回的函数中返回that的name。


四、内存泄漏

function assignHandler(){var element = document.getElementById("someElement");element.onclick = function(){alert(element.id);};}
垃圾回收:当一个对象的引用为0的时候,该对象所占内存会被回收。

因为闭包的特性(外层函数结束,也可以通过调用内层函数访问外层函数的变量),因此总会保留一个闭包函数对该对象的引用,即element的引用永远不会为0;

解决:

function assignHandler(){var element = document.getElementById("someElement");var id = element.id;element.onclick = function(){alert(id);};element = null;}
用一个变量代替对外层对象的使用。element=null是因为:闭包会引用包含函数的整个活动对象(即使闭包函数没直接用element,也会保存),所以要设置为null.

五、模仿块级作用域

形式:

(function(){

//这里是块级作用域;<— 函数表达式后可跟(),运行;function被当成函数声明的开始,函数声明不可以跟()运行;所以用括号包裹成为函数表达式。

})();

作用:限制向全局作用域中添加过多的变量和函数;也可以用来减少闭包占用的内存问题


六、私有变量

可以通过闭包创建用于访问私有变量/函数的共有方法——特权方法

function Person(name){//私有变量和私有函数var privateVariable = 10;function think(){return "Person can think";}//特权方法this.setName = function(value){name = value;};this.getName = function(){return name;};}var person = new Person("hhh");alert(person.getName()); //hhhperson.setName("lll");alert(person.getName()); //lll

缺点:必须使用构造函数模式


七、静态私有变量

(function(){    var name = '';       Person = function(value){        name  = value;         }    Person.prototype.getName = function(){        return name;    }    Person.prototype.setName = function(value){          name = value;    }})();var person1 = new Person("Nicholas");alert(person1.getName()); //Nicholasperson1.setName("Greg");alert(person1.getName()); //Gregvar person2 = new Person('Michael');alert(person1.getName()); //Michaelalert(person2.getName()); //Michael

懒得写了:http://www.cnblogs.com/tianxintian22/archive/2015/12/14/5045017.html


八、模块模式

为单例创建私有变量和特权方法

function BaseComponent(){//code}function OtherComponent(){//code}var application = function(){//私有成员变量var components = new Array();//初始化components.push(new BaseComponent());   //公有return{getComponentCount : function(){return components.length;},registerComponent : function(component){     //维护私有变量if(typeof component == "object"){   components.push(component);}}};}();

对单例进行初始化又需要维护私有变量。


九、增强的模块模式 

适用于单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强。

var application = function(){//私有变量和函数var components = new Array();//初始化components.push(new BaseComponent());//创建application的一个局部副本var app = new BaseComponent();//公共接口app.getComponentCount = function(){return components.length;};app.registerComponent = function(component){if(typeof component == "object"){components.push(component);}};//返回这个副本return app;}();





原创粉丝点击