JavaScript闭包原理与私有成员封装实战

作者:闭包旅人时间:2026-10-22 19:47:58浏览:618

本文深入解析JavaScript闭包原理,通过匿名函数与命名函数的对比示例,阐明作用域链与变量副本机制。进一步探讨如何利用闭包在构造函数中封装私有变量,实现特权方法访问,并分析该模式在内存消耗与继承方面的局限性。最后介绍通过立即执行函数封装静态私有成员的改进方案,以优化性能并限制实例数量。

JavaScript闭包原理与私有成员封装实战

本文通过具体实例解析JavaScript中闭包的核心原理及其实际应用场景,旨在帮助读者深入理解并掌握这一重要特性。

闭包基础示例与机制解析

首先,通过一个基础示例来直观展示闭包的工作机制。


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.jb51.net/code/HtmlJsRun对示例代码进行测试,观察到的运行结果如下:

JavaScript闭包原理与私有成员封装实战

过去常认为只有匿名函数才能构成闭包,但实际上,只要普通函数被包含在另一个函数内部并被返回,即可形成闭包,无需局限于匿名函数。

当foo函数执行返回后,其内部作用域并未被销毁,而是被保留下来。此时,只有foo返回的那个内部函数(如bar)能够访问该作用域中的变量。在前面的示例中,baz和blat各自拥有独立的作用域及变量a的一个副本,且只有它们自身能修改对应的a值。

这意味着,对foo函数引用的多次调用(如baz和blat)互不干扰,每个引用都维护着独立的变量状态。

利用闭包封装与隐藏信息

二、利用闭包封装与隐藏信息

基于上述原理,我们可以利用匿名函数来封装私有变量,从而实现信息的隐藏。


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.jb51.net/code/HtmlJsRun测试相关代码,运行结果如下:

JavaScript闭包原理与私有成员封装实战

在构造函数中,通过var声明变量(如isbn、title、author)及checkIsbn函数,使其成为私有属性。若需访问这些私有变量或函数,必须在构造函数内部定义相应的访问方法,这些方法被称为特权方法。而对于无需访问私有属性的方法(如display),则应在Book.prototype中声明。需要注意的是,这种模式会为每个新对象实例生成私有方法和特权方法的新副本,导致较高的内存消耗,因此仅适用于真正需要私有成员的场合。此外,由于子类无法访问父类的私有属性和方法,这种基于闭包的私有成员实现方式会导致“继承破坏封装”的问题。

静态私有成员与构造器优化方案

三、方案改进

此方案与前一种类似,但存在关键区别。私有成员和特权成员仍声明在构造函数中,但构造函数本身被封装在一个立即执行函数(IIFE)内,并将该构造函数作为返回值赋给变量Book。这创建了一个闭包,允许在此闭包内声明静态的私有成员函数。

checkIsbn函数被设置为静态方法,因为无需为每个实例生成该方法的副本。此外,通过静态属性numBooks(或numOfBooks)可以限制构造器的总调用次数。


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.jb51.net/code/HtmlJsRun测试改进后的代码,运行结果如下:

JavaScript闭包原理与私有成员封装实战

更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

希望本文所述的闭包原理与应用示例能对JavaScript程序设计有所帮助。

作者最新文章