- 相關(guān)推薦
PHP中閉包的一些常見(jiàn)問(wèn)題
PHP具有非常強大的功能,所有的CGI的功能PHP都能實(shí)現,而且支持幾乎所有流行的數據庫以及操作系統。最重要的是PHP可以用C、C++進(jìn)行程序的擴展!以下是小編為大家搜索整理的PHP中閉包的一些常見(jiàn)問(wèn)題,希望能給大家帶來(lái)幫助!更多精彩內容請持續關(guān)注我們應屆畢業(yè)生考試網(wǎng)!
首先說(shuō)明下...閉包是js高級特性之一...但并非js獨有...perl, python, php(5.3以上版本) 都是支持閉包的..
官方解釋?zhuān)?所謂“閉包”,指的是一個(gè)擁有許多變量和綁定了這些變量的環(huán)境的表達式(通常是一個(gè)函數),因而這些變量也是該表達式的一部分
john resig解釋?zhuān)?閉包就是內部函數可以訪(fǎng)問(wèn)外部函數中所定義的變量,即使該函數已經(jīng)執行結束。
如果你還是不能明白上面那句話(huà)...那么我就換句話(huà)來(lái)說(shuō):
在js中...執行一個(gè)函數A...當函數A執行完后...理論上來(lái)講...改函數A內所有被定義的 臨時(shí)變量都將被 當成可回收的垃圾等待垃圾回收....然而在這個(gè)過(guò)程..有一種臨時(shí)變量是無(wú)法被垃圾回收的...當A函數中有一個(gè)內部函數a時(shí).a函數內引用了A中定義的臨時(shí)變量...并且a函數在A(yíng)函數執行完后..仍然可以被外部訪(fǎng)問(wèn)到時(shí)...被a函數所引用的臨時(shí)變量就無(wú)法被當成垃圾等待垃圾回收.. 而a函數可以被外部訪(fǎng)問(wèn)的同時(shí)..就生成了一個(gè)閉包...
舉個(gè)例子吧..也是比較經(jīng)典的例子
//函數A 執行完后 它將返回一個(gè)函數a
function A(){
//定義一個(gè)臨時(shí)變量
var x = 1;
//返回一個(gè)內部函數a
//執行時(shí)打印臨時(shí)變量x
return function a(){
console.log( x );
};
}
//執行A 得到內部函數a
//此時(shí)內部函數a被返回...它引用了臨時(shí)變量x
//理論上A執行后 x做為臨時(shí)變量將被當成垃圾等待垃圾回收
//但是由于內部函數a引用了x 所以此時(shí)就生成了一個(gè)閉包
var a = A();
//執行a 打印1
a(); //1
閉包并非定義函數時(shí)就生成的...而是在執行過(guò)程中 當a函數被當成一個(gè)返回值被返回時(shí) 才會(huì )生成一個(gè)閉包..
閉包容易誤解的地方:
1。 閉包總是在匿名函數中生成的
閉包并非都是在匿名函數中生成的..比如上一段代碼中...被返回的函數有命名-a
2。 閉包在定義時(shí)產(chǎn)生的...
閉包并非是在定義時(shí)產(chǎn)生的...而是在內部函數可被外部訪(fǎng)問(wèn)到時(shí)才會(huì )產(chǎn)生...
3。 閉包很強大..用的越多就越牛A(==!)
不否認閉包很強大.....但是并非用的越多就是越好的...使用閉包..會(huì )造成調試困難..所以要習慣做標識..另外...使用閉包會(huì )涉及到 增長(cháng)函數作用域的 造成內部函數訪(fǎng)問(wèn)全局變量變慢的問(wèn)題...
PHP中的閉包
php-5.3 以上版本其中一個(gè)更新就是使php支持了簡(jiǎn)單的閉包
/**
* 一個(gè)curry的加法函數
* @param unknown_type $start 起始值
* @return unknown 返回一個(gè)匿名函數
*/
function add( $start = 0 ){
$sum = $start;
//該函數接受n個(gè)值..執行后返回值為n值和$sum的總和
return function () use ( &$sum ){
//獲取所有參數
$args = func_get_args();
for( $i = 0; $i < count($args); $i++ ){
$sum += (int)$args[$i];
}
return $sum;
};
}
//初始化值為1
$add = add( 1 );
//在初始化值的基礎上加上1 2 3
$add( 1, 2, 3 );
//再加一次3 輸出
echo $add( 3 ); //10
?> 這段代碼的作用是 每調用一次add函數都會(huì )生成一個(gè)相應的$sum 每個(gè)函數執行后不沖突 可避免使用static變量 而且sum不會(huì )隨函數執行結束而消失 從而實(shí)現函數柯里化
閉包的使用
1. 函數柯里化
閉包在js中經(jīng)常會(huì )被用過(guò)函數柯里化
比如上面php的那段代碼中 改成js則是:
//add函數 返回一個(gè)匿名函數
function add( start ){
var sum = start || 0;
//該函數接受n個(gè)參數 返回值為n個(gè)參數的和+sum的值
return function(){
for( var i = 0, j = arguments.length; i < j; i++ ){
sum += Number( arguments[i] );
}
return sum;
}
}
var a = add( 1 );
a( 1, 2, 3 );
console.log( a( 3 ) );
玩個(gè)有意思的函數 這個(gè)是別人曾經(jīng)給我出的一道題目 當時(shí)我也沒(méi)想出來(lái)...(壓根把tostring這方法給忘了.)
題目需求要求可以這樣調用(當時(shí)的需求只要求傳一個(gè)參數)
//獲取curry后的函數
var a = add( 1 );
//調用多次相加
a( 1, 2, 3 )( 1, 2, 3 )( 1, 2, 3 );
//直接輸出函數
console.log( a ); //19
//繼續相加后輸出
console.log( a( 1, 2, 3 )( 1, 2, 3 ) ); //31
實(shí)現如下
//add函數 返回一個(gè)匿名函數
function add( start ){
var sum = start || 0;
//該函數接受n個(gè)參數 返回值為函數本身
//直接輸出函數時(shí) 打印sum的值
return function(){
//參數相加
for( var i = 0, j = arguments.length; i < j; i++ ){
sum += Number( arguments[i] );
}
//獲取函數本身
var func = arguments.callee;
//重寫(xiě)函數tostring方法 用于打印函數
func.toString = function(){
return sum;
};
//返回函數本身
return func;
}
}
2。模擬對象中的私有屬性和方法
寫(xiě)之前先解釋下 js非一門(mén)OO語(yǔ)言 它是一門(mén)基于對象的語(yǔ)言
如 var i = 0; 則i是一個(gè)數值型對象 轉成對象寫(xiě)法則是 var i = new Number(1); 前一種叫過(guò)直接量表示法 同JSON(js對象字面量,表示js中對象的直接量表示方法) 直接量表示的速度要比 new 快
(1)模擬私有屬性和私有方法
//smarty模板引擎 模擬
function Smarty(){
//公有屬性 可被外部直接訪(fǎng)問(wèn)
//左標簽
this.leftLimiter = '{';
//右標簽
this.rightLimiter = '}';
//私有屬性 不可被外部直接訪(fǎng)問(wèn)
//緩存assign方法調用后的賦值
var cacheData = {};
//公用方法 assign
//準確來(lái)講..它叫做一個(gè)特權方法 可訪(fǎng)問(wèn)內部私有屬性的方法叫做特權方法
//實(shí)例化smarty構造函數時(shí) 由于它是一個(gè)公用方法 可被外部訪(fǎng)問(wèn)
//并且引用了cacheData臨時(shí)變量 所以cacheData不會(huì )垃圾回收 此時(shí)生成一個(gè)閉包
this.assign = function( name, value ){
//緩存賦值
cacheData[name] = value;
}
//私有方法 fetch 編譯解析模板內容 返回結果 不輸出
//假設它是一個(gè)私有方法 不能被外部直接訪(fǎng)問(wèn)
function fetch( tpl ){
//do something
return tpl;
}
//公用方法 輸出
this.display = function( tpl ){
//調用內部私有方法 直接輸出
console.log( fetch( tpl ) );
}
}
//實(shí)例化smarty
var template = new Smarty();
//設置左標簽
template.leftLimiter = '<{';
//設置右標簽
template.rightLimiter = '}>';
//賦值
template.assign( 'name', 'jsyczhanghao' );
//賦值
template.assign( 'age', 23 );
//輸出最終編譯結果
template.display( document.getElementById( 'test' ).innerHTML );
(2)模擬私有靜態(tài)方法(單例模式-Zend framework 模擬前端控制器 phper你懂的..)
//模擬Zend framework 前端控制器
//定義一個(gè)匿名函數 定義完立即執行(function( window ){
//Zend_Controller主構造函數 //在js中無(wú)法設置私有的構造函數
//所以必須將構造函數設置為 非公開(kāi) 才可以不讓外部調用的程序直接實(shí)例化構造函數 在公開(kāi)對象中提供一個(gè)公開(kāi)方法 間接去調用
var Zend_Controller = function(){
//設置控制器的路徑
this.setControllerDirectory = function(){};
//分發(fā)路由
this.dispatch = function(){
console.log( 1 );
};
};
//前端控制器的私有靜態(tài)屬性 外部不可直接訪(fǎng)問(wèn)
//它為一個(gè)Zend_Controller的實(shí)例
var intance;
//公開(kāi)類(lèi) 前端控制器
var Zend_Controller_Front = function(){};
//獲取實(shí)例 一個(gè)共有靜態(tài)方法
//可被外部調用的方法 生成閉包 臨時(shí)變量instance和Zend_Controller不會(huì )消失
Zend_Controller_Front.getInstance = function(){
//返回如果已存在實(shí)例 則直接返回
//否則 先創(chuàng )建再返回
return instance || ( instance = new Zend_Controller() );
};
//實(shí)際的js中習慣會(huì )把單例模式會(huì )這么寫(xiě)
//將Zend_Controller_Front直接寫(xiě)成一個(gè)對象 getinstance自然就成了一個(gè)公用方法 可直接調用
//window.Zend_Controller_Front = {
// getInstance: function(){
// return instance || ( instance = new Zend_Controller() );
// }
//};
window.Zend_Controller_Front = Zend_Controller_Front;
})( this );
var zend_instance = Zend_Controller_Front.getInstance();
zend_instance.setControllerDirectory( '/root' );
zend_instance.dispatch();
3。事件回調函數中的使用
//更新元素內容 ajax
//第一個(gè)參數為dom元素
//第二個(gè)參數發(fā)送的url
function updateElement( elem, url ){
//jquery中ajax的get方法
//在 #js的異步機制和大數據量的處理方案# 中有說(shuō)到
//實(shí)際上在get方法過(guò)后...該函數已執行后
//get方法第2個(gè)參數的匿名函數 將會(huì )被丟到 UI隊列的最后面等待合適的機會(huì )觸發(fā)
//該機會(huì )就是ajax成功發(fā)送并且成功返回狀態(tài)值時(shí)觸發(fā)
//由于匿名函數并非立即執行 且依賴(lài)于elem參數 所以elem不會(huì )被當垃圾進(jìn)行回收 并在此生成一個(gè)閉包
//必須等到 匿名函數成功執行后才會(huì )被釋放..
$.get( url, function( data ){
//ajax發(fā)送成功后 將返回的值 寫(xiě)到元素中
elem.innerHTML = data;
});
} 以上是閉包絕大部分會(huì )出現的場(chǎng)景
#############################################################################################################
來(lái)看個(gè)問(wèn)題吧:針對 #js的異步機制和大數據量的處理方案# 中的一段代碼段
for( var i = 0; i < 10; i++ ){
//為test0-test9綁定click事件
document.getElementById( 'test' + i ).onclick = function(){
//打印對應的i
console.log( i );
};
}
這段代碼執行后 點(diǎn)擊test0-test9并非象預期那樣.. 依次打印出0-9 而是每一個(gè)元素點(diǎn)擊后都打印了10
造成的原因就是 綁定click事件時(shí) 回調函數并未執行 當回調函數執行時(shí) i已經(jīng)變成了10 所以打印的結果都會(huì )變成10
解決方法:
思路: 如果能找到一種方式可以將每一次的i都緩存起來(lái) 并且一直到click事件觸發(fā)的時(shí)候 它都一直不會(huì )消失 不就完了么
我們都知道 一個(gè)函數作用域內執行完后..作用域中的所有臨時(shí)變量都會(huì )消失 但是有一種不讓臨時(shí)變量消失的方式就是使用閉包。。而上面講閉包的使用場(chǎng)景時(shí) 其中有一條就是事件回調函數 當一個(gè)事件回調函數位于一個(gè)作用域內的時(shí)候...作用域執行外后 由于回調函數并未馬上執行..而是等到相應事件觸發(fā)時(shí)才執行...當回調函數依賴(lài)該作用域內的臨時(shí)變量時(shí)...導致該作用域內部使用的臨時(shí)變量無(wú)法馬上被當垃圾回收(意味著(zhù)該臨時(shí)變量不會(huì )消失)
目前我們擁有一個(gè)事件回調函數 要做的就是需要讓這個(gè)事件回調函數位于一個(gè)函數作用域內
代碼:
for( var i = 0; i < 10; i++ ){
//為test0-test9綁定click事件
function(){
document.getElementById( 'test' + i ).onclick = function(){
//打印對應的i
console.log( i );
};
};
}
這樣 事件綁定就位于一個(gè)匿名函數中了...但是這樣肯定不行...因為函數都沒(méi)有執行...函數內的代碼肯定不會(huì )起作用....也就是說(shuō)..這段代碼能夠正常執行 不報錯..但是不會(huì )為每一個(gè)元素綁定一個(gè)事件..因為它的外部函數沒(méi)有執行
繼續修改:
for( var i = 0; i < 10; i++ ){
//為test0-test9綁定click事件
(function(){
document.getElementById( 'test' + i ).onclick = function(){
//打印對應的i
console.log( i );
};
})();
}
恩 這次看起來(lái)差不多了....綁定事件的行為位于一個(gè)匿名函數中..并且匿名函數定義后立即執行....
但是目前 綁定事件內的變量i并不是 匿名函數中所產(chǎn)生的臨時(shí)變量 i是一個(gè)全局變量 i不會(huì )因為匿名函數的執行而一直保持 你所希望的值
所以我們需要在匿名函數內定義一個(gè)臨時(shí)變量 該臨時(shí)變量的值和當前相應的i值相等即可 將i直接賦值給該臨時(shí)變量就可以了..
最終修改代碼:
for( var i = 0; i < 10; i++ ){
//為test0-test9綁定click事件
(function(){
var j = i;
document.getElementById( 'test' + j ).onclick = function(){
//打印對應的i
console.log( j );
};
})();
}其實(shí)不一定要直接賦值 當一個(gè)參數傳進(jìn)去也行代碼如下(執行結果一樣..過(guò)程也沒(méi)什么區別..只是寫(xiě)法不同)for( var i = 0; i < 10; i++ ){
//為test0-test9綁定click事件
(function( j ){
document.getElementById( 'test' + j ).onclick = function(){
//打印對應的i
console.log( j );
};
})( i );
}
其實(shí)還有一種不使用閉包的方式...在事件的回調函數中直接引用 dom對象的一個(gè)屬性即可 因為dom對象是一直存在的 而指向當前的dom對象使用this即可for( var i = 0; i < 10; i++ ){
//為test0-test9綁定click事件
var elem = document.getElementById( 'test' + i );
elem.index = i;
elem.onclick = function(){
//打印對應的i
console.log( this.index );
};
}
【PHP中閉包的一些常見(jiàn)問(wèn)題】相關(guān)文章:
javascript中js閉包的深入理解06-16
PHP中的Trait09-29
細數PHP程序的一些缺陷08-27
PHP中php://input和$-POST的區別08-26
PHP中的表單處理09-19
Session在PHP中的使用07-24
PHP中list的方法07-05
PHP中Json應用09-05
PHP中$-SERVER的詳解06-25