JS闭包原理与应用经典示例


Posted in Javascript onDecember 20, 2018

本文实例讲述了JS闭包原理与应用。分享给大家供大家参考,具体如下:

一、先来看一个例子:

function foo() {
   var a = 10;
   function bar() {
    a *= 2;
    return a;
   }
   return bar;
}
var baz = foo(); // baz is now a reference to function bar.
console.log(baz()); // returns 20.
console.log(baz()); // returns 40.
console.log(baz()); // returns 80.
var blat = foo(); // blat is another reference to bar.
console.log(blat()); // returns 20, because a new copy of a is being used.

这里使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码,可得到如下运行结果:

JS闭包原理与应用经典示例

一直以来,我都是以为只有用匿名函数才能算是闭包,但是其实不一定要用匿名函数的,就是一般的函数就可以,前提是它得被包含在另一个函数中。

在foo返回后,它的作用域被保存下来了,但只有它返回的的那个函数能够访问这个作用域。在前面的示例中,baz和balt各有各的作用域及a的一个副本,而且只有他们自己能对其进行修改。

其实就是说我们对foo函数的引用的调用并不会对其他引用有任何影响。

二、封装和隐藏信息

看了上面的例子,我们可以考虑采用匿名函数来进行封装和隐藏私有变量。

var Book = function(newIsbn, newTitle, newAuthor) { // implements Publication
 // Private attributes.
 var isbn, title, author;
 // Private method.
 function checkIsbn(isbn) {
  //...
  return true;
 }
 // Privileged methods.
 this.getIsbn = function() {
  return isbn;
 };
 this.setIsbn = function(newIsbn) {
  if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.');
  isbn = newIsbn;
 };
 this.getTitle = function() {
  return title;
 };
 this.setTitle = function(newTitle) {
  title = newTitle || 'No title specified';
 };
 this.getAuthor = function() {
  return author;
 };
 this.setAuthor = function(newAuthor) {
  author = newAuthor || 'No author specified';
 };
 // Constructor code.
 this.setIsbn(newIsbn);
 this.setTitle(newTitle);
 this.setAuthor(newAuthor);
};
// Public, non-privileged methods.
Book.prototype = {
 display: function() {
  //...
 }
};
var mybook=new Book("myisbtn","mytittle","myauthor");
console.log(mybook.getAuthor());

使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码,可得到如下运行结果:

JS闭包原理与应用经典示例

我们通过在构造器中用var声明了这些变量和checkIsbn函数,因此他们就变成了私有的属性。需要访问这些变量和函数的方法只需要在Book中声明即可。这些方法也被陈伟特权方法。而任何不需要访问私有属性的方法都要在Book.prototype中声明。例如display。但这里也存在个问题:就是每生成一个新的对象实例都将为每一个私有方法和特权方法生成一个新的副本。这会比其他做法耗费更多内存,因此只宜用在真正需要私有成员的场合。另外,这种模式也不适合派生子类,因为派生的子类并不能访问超类的任何私有属性和方法。故在JavaScript中用闭包实现私有成员导致派生问题被称为“继承破坏封装”。

三、改进

这里与上一种大体类似,但是也有一些重要的区别。这里私有成员和特权成员仍被声明在构造器中,但是构造器已经变成一个内嵌函数了,并且被作为包含它的函数的返回值赋给变量Book.这就是创建了一个闭包,你可以把静态的私有成员函数声明在里面。

checkIsbn函数被设置为静态方法,是因为没必要为每个实例都生成这个方法的一个副本。此外还有静态属性numBooks限制了构造器总的调用次数

var Book = (function() {
 // Private static attributes.
 var numOfBooks = 0;
 // Private static method.
 function checkIsbn(isbn) {
  // ...
  return true;
 }
 // Return the constructor.
 return function(newIsbn, newTitle, newAuthor) { // implements Publication
  // Private attributes.
  var isbn, title, author;
  // Privileged methods.
  this.getIsbn = function() {
   return isbn;
  };
  this.setIsbn = function(newIsbn) {
   if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.');
   isbn = newIsbn;
  };
  this.getTitle = function() {
   return title;
  };
  this.setTitle = function(newTitle) {
   title = newTitle || 'No title specified';
  };
  this.getAuthor = function() {
   return author;
  };
  this.setAuthor = function(newAuthor) {
   author = newAuthor || 'No author specified';
  };
  // Constructor code.
  numOfBooks++; // Keep track of how many Books have been instantiated
         // with the private static attribute.
  if(numOfBooks > 1) throw new Error('Book: Only 1 instances of Book can be '
    + 'created.');
  this.setIsbn(newIsbn);
  this.setTitle(newTitle);
  this.setAuthor(newAuthor);
 }
})();
// Public static method.
Book.convertToTitleCase = function(inputString) {
 //...
 console.log("convertToTitleCase");
};
// Public, non-privileged methods.
Book.prototype = {
 display: function() {
  //...
  console.log("display");
 }
};
var mybook=new Book("myisbtn","mytittle","myauthor");
console.log(mybook.getAuthor());  //myauthor
mybook.display();          //display
//mybook.convertToTitleCase();   //mybook.convertToTitleCase is not a function
var mybook2= new Book("my2","tittle2","myauthor2");
console.log(mybook2.getAuthor());  //Only 1 instances of Book can be created.

使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码,可得到如下运行结果:

JS闭包原理与应用经典示例

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
为超链接加上disabled后的故事
Dec 10 Javascript
javascript 内存回收机制理解
Jan 17 Javascript
动态加载js、css等文件跨iframe实现
Feb 24 Javascript
JavaScript中的条件判断语句使用详解
Jun 03 Javascript
jQuery网页选项卡插件rTabs用法实例分析
Aug 26 Javascript
基于jQuery实现的查看全文功能【实用】
Dec 11 Javascript
基于BootStrap与jQuery.validate实现表单提交校验功能
Dec 22 Javascript
bootstrap侧边栏圆点导航
Jan 11 Javascript
详解vue-cli下ESlint 配置说明
Sep 03 Javascript
mpvue写一个CPASS小程序的示例
Sep 04 Javascript
JavaScript事件委托实现原理及优点进行
Aug 29 Javascript
Ajax常用封装库——Axios的使用
May 08 Javascript
Mint UI组件库CheckList使用及踩坑总结
Dec 20 #Javascript
从零开始在NPM上发布一个Vue组件的方法步骤
Dec 20 #Javascript
微信小程序实现swiper切换卡内嵌滚动条不显示的方法示例
Dec 20 #Javascript
微信小程序实现的点击按钮 弹出底部上拉菜单功能示例
Dec 20 #Javascript
vue+Vue Router多级侧导航切换路由(页面)的实现代码
Dec 20 #Javascript
微信小程序module.exports模块化操作实例浅析
Dec 20 #Javascript
JavaScript类的继承操作实例总结
Dec 20 #Javascript
You might like
php算开始时间到过期时间的相隔的天数
2011/01/12 PHP
php urlencode()与urldecode()函数字符编码原理详解
2011/12/06 PHP
PHP __autoload()方法真的影响性能吗?
2012/03/30 PHP
PHP将HTML转换成文本的实现代码
2015/01/21 PHP
mac下多个php版本快速切换的方法
2016/10/09 PHP
php获取微信基础接口凭证Access_token
2018/08/23 PHP
YII2.0框架行为(Behavior)深入详解
2019/07/26 PHP
PHP中的自动加载操作实现方法详解
2019/08/06 PHP
详解php反序列化
2020/06/10 PHP
jQuery 使用个人心得
2009/02/26 Javascript
AngularJS出现$http异步后台无法获取请求参数问题的解决方法
2016/11/03 Javascript
js实现图片左右滚动效果
2017/02/27 Javascript
JS三目运算(三元运算)方法详解
2017/03/01 Javascript
xmlplus组件设计系列之树(Tree)(9)
2017/05/02 Javascript
JavaScript 五大常见函数
2018/03/23 Javascript
axios对请求各种异常情况处理的封装方法
2018/09/25 Javascript
Vuex 单状态库与多模块状态库详解
2018/12/11 Javascript
layer提示框添加多个按钮选择的实例
2019/09/12 Javascript
layer页面跳转,获取html子节点元素的值方法
2019/09/27 Javascript
vue监听键盘事件的相关总结
2021/01/29 Vue.js
python实现机器学习之多元线性回归
2018/09/06 Python
Python 、Pycharm、Anaconda三者的区别与联系、安装过程及注意事项
2019/10/11 Python
使用python实现哈希表、字典、集合操作
2019/12/22 Python
PyTorch 解决Dataset和Dataloader遇到的问题
2020/01/08 Python
为什么python比较流行
2020/06/19 Python
python Pexpect模块的使用
2020/12/25 Python
Html5调用手机摄像头并实现人脸识别的实现
2018/12/21 HTML / CSS
Auguste The Label官网:澳大利亚一家精品女装时尚品牌
2020/06/14 全球购物
汽车销售顾问求职自荐信
2014/01/01 职场文书
一年级语文下册复习计划
2015/01/17 职场文书
北京导游词
2015/02/12 职场文书
2015年大学学生会工作总结
2015/05/13 职场文书
2016优秀护士求职自荐信
2016/01/28 职场文书
微信小程序实现聊天室功能
2021/06/14 Javascript
vue使用refs获取嵌套组件中的值过程
2022/03/31 Vue.js
原生JS实现分页
2022/04/19 Javascript