JS閉包原理與應(yīng)用經(jīng)典示例
本文實(shí)例講述了JS閉包原理與應(yīng)用。分享給大家供大家參考,具體如下:
一、先來看一個(gè)例子:
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代碼運(yùn)行工具:http://tools.jb51.net/code/HtmlJsRun測(cè)試上述代碼,可得到如下運(yùn)行結(jié)果:

一直以來,我都是以為只有用匿名函數(shù)才能算是閉包,但是其實(shí)不一定要用匿名函數(shù)的,就是一般的函數(shù)就可以,前提是它得被包含在另一個(gè)函數(shù)中。
在foo返回后,它的作用域被保存下來了,但只有它返回的的那個(gè)函數(shù)能夠訪問這個(gè)作用域。在前面的示例中,baz和balt各有各的作用域及a的一個(gè)副本,而且只有他們自己能對(duì)其進(jìn)行修改。
其實(shí)就是說我們對(duì)foo函數(shù)的引用的調(diào)用并不會(huì)對(duì)其他引用有任何影響。
二、封裝和隱藏信息
看了上面的例子,我們可以考慮采用匿名函數(shù)來進(jìn)行封裝和隱藏私有變量。
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代碼運(yùn)行工具:http://tools.jb51.net/code/HtmlJsRun測(cè)試上述代碼,可得到如下運(yùn)行結(jié)果:

我們通過在構(gòu)造器中用var聲明了這些變量和checkIsbn函數(shù),因此他們就變成了私有的屬性。需要訪問這些變量和函數(shù)的方法只需要在Book中聲明即可。這些方法也被陳偉特權(quán)方法。而任何不需要訪問私有屬性的方法都要在Book.prototype中聲明。例如display。但這里也存在個(gè)問題:就是每生成一個(gè)新的對(duì)象實(shí)例都將為每一個(gè)私有方法和特權(quán)方法生成一個(gè)新的副本。這會(huì)比其他做法耗費(fèi)更多內(nèi)存,因此只宜用在真正需要私有成員的場(chǎng)合。另外,這種模式也不適合派生子類,因?yàn)榕缮淖宇惒⒉荒茉L問超類的任何私有屬性和方法。故在JavaScript中用閉包實(shí)現(xiàn)私有成員導(dǎo)致派生問題被稱為“繼承破壞封裝”。
三、改進(jìn)
這里與上一種大體類似,但是也有一些重要的區(qū)別。這里私有成員和特權(quán)成員仍被聲明在構(gòu)造器中,但是構(gòu)造器已經(jīng)變成一個(gè)內(nèi)嵌函數(shù)了,并且被作為包含它的函數(shù)的返回值賦給變量Book.這就是創(chuàng)建了一個(gè)閉包,你可以把靜態(tài)的私有成員函數(shù)聲明在里面。
checkIsbn函數(shù)被設(shè)置為靜態(tài)方法,是因?yàn)闆]必要為每個(gè)實(shí)例都生成這個(gè)方法的一個(gè)副本。此外還有靜態(tài)屬性numBooks限制了構(gòu)造器總的調(diào)用次數(shù)
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代碼運(yùn)行工具:http://tools.jb51.net/code/HtmlJsRun測(cè)試上述代碼,可得到如下運(yùn)行結(jié)果:

更多關(guān)于JavaScript相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《javascript面向?qū)ο笕腴T教程》、《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》、《JavaScript遍歷算法與技巧總結(jié)》及《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》
希望本文所述對(duì)大家JavaScript程序設(shè)計(jì)有所幫助。
相關(guān)文章
基于JavaScript實(shí)現(xiàn)瀑布流布局
這篇文章主要為大家詳細(xì)介紹了基于JavaScript實(shí)現(xiàn)瀑布流布局,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08
JS+HTML5 FileReader實(shí)現(xiàn)文件上傳前本地預(yù)覽功能
這篇文章主要為大家詳細(xì)介紹了JS+HTML5 FileReader實(shí)現(xiàn)文件上傳前本地預(yù)覽功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
原生js實(shí)現(xiàn)tab選項(xiàng)卡切換
這篇文章主要為大家詳細(xì)介紹了原生js實(shí)現(xiàn)tab選項(xiàng)卡,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08
微信小程序事件對(duì)象中e.target和e.currentTarget的區(qū)別詳解
這篇文章主要介紹了微信小程序事件對(duì)象中e.target和e.currentTarget的區(qū)別詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05
javascript 網(wǎng)頁進(jìn)度條簡(jiǎn)單實(shí)例
這篇文章主要介紹了javascript 網(wǎng)頁進(jìn)度條簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-02-02
JavaScript中利用for循環(huán)遍歷數(shù)組
這篇文章主要為大家詳細(xì)介紹了JavaScript中利用for循環(huán)遍歷數(shù)組,最好不要使用for in遍歷,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01

