js闭包vs Java内部类
前言:
昨天写了一个关于Java内部的博客,在内部类的最后一点中谈到了Java闭包的概念,他是这样定义闭包的:闭包是一个可调用的对象,它记录了一些信息,这些信息来自创建它的作用域。结合Java的内部类可以很好的理解这一点(如有需要可参考ThinkInJava之内部类)。突然之间想到js中的闭包,一直都无法很好的理解,故借此又看了一下js中的闭包,对我个人而言,感悟良多,借此也与大家分享一下,希望可以帮助大家,并一起快乐的学习成长,天天向上。
零:js闭包概念(通过Java闭包和js嵌套函数和图一分析 :纯个人 见解,欢迎评论和建议)
js:闭包是一个返回给调用者的对象,而这个返回对象携带了一些调用者无法获取的信息
一:js中的对象定义(因为是针对闭包的学习就只简单介绍一种定义方法)
<script> function Car() { //定义class var color = "blue"; //定义属性 } Car.prototype.getColor = function() { //通过原型定义方法 console.info(this.color) return this.color; } var oCar1 = new Car();//创建对象实例 oCar1.getColor(); //调用方法 </script>
二:js中的变量作用域(全局变量和局部变量)
var a = "我是全局变量"; function myFunction() { var b = "我是局部变量" return a ; }
function my2(){
consoke.info(b) #报错
}
2.1 全局变量a:即属性window的属性,在同一页面内所有的js脚本,都共享同一个window对象,故共享全局变量a.
2.2 局部变量b :局部变量只能用于定义它函数内部。对于其他的函数或脚本代码是不可用的。
备注:变量声明时如果不使用 var 关键字,那么它就是一个全局变量,即便它在函数内定义。
三:计数器困境(引入问题)
解题思路:需要一个变量,这个变量需要在方法内访问并加一,多次调用该变量就是多次加一的和,故不能把该变量定义在方法的内部,如果把该变量定义在方法的内部就不能实现多次调用返回多次调用的和,故把该方法定义为全局变量如下,但这样定义该变量即不安全如调用方法2
var counter = 0; function add() { return counter += 1; } function myFunction(){ document.getElementById("demo").innerHTML = add(); } function myFunction2(){ counter = 100; document.getElementById("demo").innerHTML = add(); } myFunction(); myFunction();//实现多次调用返回,多次调用的和 ##counter =2 myFunction2();//但如果调用该方法,就不返回多次调用的和 #因为是全局变量,任何脚本都可更改该变量的值,这样及其不不安全。 ##counter =101
解题思路2:如果能把count变量隐藏起来不让其它js方法修改它不就行了吗?如果我们熟悉Java语言,用Java就很容易解决该问题。因为Java提供的修饰符private可以控制属性的访问限制。并定义一个唯一public方法设置该属性(就是把属性定义为私有的,并提供唯一的get和set方法,就这么简单)。然而如果把问题抛给js就很难解决这个问题了,以为js没有提供这样的修饰符,来控制访问属性。如何解决类似Java private成员的问题请看下面
四:JS的 内嵌函数(类比Java内部类 ,我们发现嵌套函数及其的像Java的内部类😂)
JavaScript 支持嵌套函数。嵌套函数可以访问上一层的函数变量(我们可以这样理解:内部的变量可以访问其外面的变量,而外面的不能访问内部的。
//一个简单的js嵌套函数 function add() { //外部类 var counter = 0; function plus() { //嵌套函数 内部类 counter += 1; //嵌套函数可以访问其外部的变量 } plus(); return counter; }
类比Java :Java内部类可以访问外围对象的所有属性包括私有属性,js的嵌套函数好行也有这个属性😄。
五:js立刻执行函数也叫函数的自我调用(顾名思义就是立刻执行),下面是一个立刻只能函数的写法
var aa = ( function(){ alert("sssss"); return {}; } )() //备注:当我们刷新当前页面时就会执行function方法并返回{}对象 //不需要手动调用该方法
立即执行函数的写法有很多中以上是最常用的方式()(),下面也是立即函数的写法
1 !function foo(){...}();
2 +function foo(){...}();
通过立刻执行函数,并且返回一个空的对象,结合内嵌函数的特性,我们可知这个对象是可以访问外围的属性和方法的。
然后我们分析:利用嵌套函数和闭包的特性来分析下图
{//区域A(window) {//区域B return {//区域C 区域C返回到了区域A } } }
图一
结论如下
1. 区域A不能访问区域B定义的数据,故B就对A隐藏了。然而区域C可以访问区域B定义的数据,
2.区域C同过return返回给了区别A,如果区域C是一个方法,则A就可以调用这个方法,而这个方法是唯一能访问到B区域的(B提供了一个public方法共全局访问)。故B又提供了对A访问的方法。
这样就形成了一个js的闭包:(即闭包是一个返回给调用者的对象,而这个返回对象携带了一些调用者无法获取的信息)
备注:js方法也是对象
六:JS闭包,解决计数器困境
代码如下
var add = (function () { var counter = 0; return function () {return counter += 1;} })(); add(); add(); add(); // 计数器为 3
备注1:js函数分类
函数声明:function fname(){...}; 使用function关键字声明一个函数,再指定一个函数名。
函数表达式:var fname=function(){...}; 使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予给一个变量。
匿名函数:function(){}; 使用function关键字声明一个函数,但未给函数命名。(匿名函数也属于函数表达式。)
备注2:js解析机制
js:解析机制:分为编译和执行两个阶段。
先编译:有人也称预编译,js解析器会扫描整个js文件,把以var (定义变量)和function(定义方法)开头语句做变量提升。
执行 :给定义的变量赋值,或执行相关方法(以括号‘()’结尾的语句,会当作方法来执行)
利用js解析机制来来回答为什么函数会自我调用
在执行阶段,js解析器发现该函数 1 : 没有以var或function开头。
2 : 并且以'()'括号结尾
如果满足以上两个条件,没有特殊原因都会称为立刻执行函数。
备注3: js立即执行函数 vs Java单例模式
如果属性java的读者一定接触过单例模式:即一个应用系统中只允许拥有一个唯一的某个类型的实例对象,具体写法再次就不多介绍了。通过分析和观察立即执行函数,因为该函数的执行是在js解析器加载的时候执行的(可以类比为Java应用程序启动时加载单例),并且很难手动再次加载它(我是没有发现方法😄),故我们可以理解立即函数就是单例设计模型(没有研究过js的设计模型,也不知道有没有😄)。
***************************************欢迎读者给出建议***********************************