javascript 闭包的理解
1,什么是闭包
“官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
官方的解释实在是太高大上了,反正我是没有理解。闭包简单理解就是函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。说的在直白一点就是当function嵌套function的时候,内部函数可以访问外部函数里面的变量。
可以把闭包和面向对象结合起来,把闭包的外部函数想象成一个class,外部函数里面的变量想象成私有变量,闭包函数想象成一个普通方法,如果想实现某种逻辑的话,可以在普通方法里面实现,去实例化外部函数并调用普通方法即可, 我觉得这样更容易理解闭包。
闭包的作用就是在a执行完并返回后,闭包使得Javascript的垃圾回收机制GC不会收回a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量。
接下来让我们先来看个例子
function fun(){ var n=0; this.plus = function(){ return n+=1; } } var _fun = new fun(); console.log(_fun.plus()) console.log(_fun.plus()) console.log(_fun.plus())
上面的demo就是个闭包,有权限访问另一个函数作用域内变量的函数都是闭包,因为变量n一直被plus引用,因此会一直在内存中存在不会被回收。(由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题。)
2,闭包的几种方式
// 第一种 function add1(){ this.counter = 0; } // 在Javascript中每个函数都有一个Portotype属性,而对象没有,这个我们后面用面向对象再来解释 add1.prototype.plus=function(){ return this.counter+=1; } var _add1 = new add1(); console.log(_add1.plus()) console.log(_add1.plus())
// 第二种 var add2 = function(){ var obj = {}; obj.counter = 10; obj.plus = function(){ return this.counter+=1; } return obj; } var _add2 = new add2(); console.log(_add2.plus()) console.log(_add2.plus())
// 第三种 var add3 = new Function("this.counter = 20;this.plus = function(){return this.counter+=1;}"); var _add3 = new add3(); console.log(_add3.plus()) console.log(_add3.plus())
// 第四种 var add4={ "counter":30, "plus":function(){ return this.counter +=1; } } console.log(add4.plus()) console.log(add4.plus())
// 第五种 自调用函数 var add5 = (function(){ var counter = 40; return function(){ return counter += 1; } })() console.log(add5()) console.log(add5())
3,prototype 让我们用面向对象的方式来解释
上面第一种方法中使用到了prototype,这里我们来说下如何理解它。
var obj = function(){ }; obj .show = function(){ alert("我是静态方法"); }; obj .prototype.display = function(){ alert("我是实例方法"); };
obj.display(); // obj.Display is not a function
obj.show(); //正确
var _obj = new obj();
_obj.display(); //正确
_obj.show(); //_obj.Show is not a function
1、不使用prototype属性定义的对象方法,是静态方法,只能直接用类名进行调用!另外,此静态方法中无法使用this变量来调用对象其他的属性!
2、使用prototype属性定义的对象方法,是非静态方法,只有在实例化后才能使用!其方法内部可以this来引用对象自身中的其他属性!
4,作用域
var dom = function(){ var Name = "Default"; this.Sex = "Boy"; this.success = function(){ alert("Success"); }; }; alert(dom.Name); // Undefined
alert(dom.Sex); //Undefined
在Javascript中每个function都会形成一个作用域,而这些变量声明在函数中,所以就处于这个函数的作用域中,外部是无法访问的。要想访问变量,就必须new一个实例出来。
最后再来让我们看一个闭包的例子
// 闭包 var person = (function(){ var name="default"; //变量作用域为函数内部,外部无法访问 return { getName:function(){ return name; }, setName:function(_name){ name=_name; } } })() console.log(person.name) console.log(person.getName()) person.setName("jhone") console.log(person.getName())
// 顺便写个继承吧 var boy = function(){} boy.prototype = person; boy.prototype.say = function(){ console.log("my name is " + this.getName()); } var _boy = new boy(); _boy.setName("jack") _boy.say();
5, js 中的只读属性
// 只读属性 set get 是es5的中对象的特性 obj = { get x() {return 6}, get y() {return 7} }; console.log(obj.x) console.log(obj.y)
obj = {}; Object.defineProperty(obj, "x", {value:4, writable:false}); //只读属性 console.log(obj.x)
// 使用闭包实现 var fun = (function(){ var age = 27; return function(){ return age; } })(); console.log(fun())