1. 什么是闭包? 在了解闭包之前,应当了解一下JavaScript的作用域和作用域链的概念。详细可看《JavaScript作用域及作用域链的详解》 闭包就是能够读取其他函数内部变量的函数。 由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成”定义在一个函数内部的函数”。 所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
2.从代码理解闭包 1 2 3 4 5 6 7 8 var scope = "global scope"; //全局变量 function checkscope(){ var scope = "local scope"; //局部变量 function f(){return scope} //返回local scope return f } var C = checkscope() //返回local scope C()
这是简单的闭包,函数checkscope的内部函数f被函数checkscope外的一个变量C引用。C能够读取checkscope函数内部变量的函数f。 这就形成了闭包的概念,闭包就是能够读取其他函数内部变量的函数
。
3.闭包的用途 3.1 用闭包模拟私有方法,私有变量 使用闭包来定义公共函数,并令其可以访问私有函数和变量
。
以下使用例子Counter
,实现一个计数器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var Counter = (function() { var privateCounter = 0; //私有变量 function changeBy(val) { //私有方法 privateCounter += val; } return { increment: function() { //计数器加1 changeBy(1); }, value: function() { return privateCounter; } } })(); Counter.value(); //公有方法 Counter.increment(); //公有方法 Counter.privateCounter() //undefined
Counter包含两个私有项:名为privateCounter的变量和名为 changeBy的函数
。这两项都无法在这个匿名函数外部直接访问。必须通过匿名函数返回的两个公共函数访问。
3.2 立即执行函数 a.什么是立即执行函数? 立即执行函数表达式,Immediately-Invoked Function Expression,简写为IIFE
。
b.立即执行函数的作用? IIFE只有一个作用:创建一个独立的作用域
。 因为JavaScript中只有全局作用域和函数作用域
。为了避免变量的污染,尽可能地少设置全局变量。所以我们就用立即执行函数表达式。
c.IIFE的经典场景 1 2 3 4 5 6 7 8 9 var arr = []; for(var i = 0; i< 5; i++){ (function(i){ arr[i] = function(){ return i; } })(i) } arr[2]() // 2
d.闭包与立即执行函数表达式的关系 闭包与立即执行函数它们是合作的关系,经常会看到立即执行函数中会存在函数,那这个函数就是闭包了
。
3.3. 封装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var person=function() { var name='Jessy';// 定义成员函数,封装属性,外界无法直接使用 return{ setName:function(data) { name=data; }, getName:function() { return name; } } }(); person.name//undefined,不能访问 person.getName()//只能通过get/set方法调用 person.setName('Jessy')
3.4. 实现类和继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 //Person 类 function Person(){ var name = "Jessy"; return{ setName:function(data) { name=data; }, getName:function() { return name; } } } //定义子类 var Jessy = function(){} //集成Person类 Jessy .prototype = new Person() //定义子类方法 Jessy.prototype.eat = function(){ console.log("eat....."); } var jessy = new Jessy() //子类实例可以方法自身定义的eat方法和从父类中继承而来的setName/getName jessy.eat() jessy.getName() jessy.setName("Jessy")
作者: Jessy Hong