php語言

當前位置 /首頁/計算機/php語言/列表

PHP中閉包的一些常見問題

PHP具有非常強大的功能,所有的CGI的功能PHP都能實現,而且支援幾乎所有流行的資料庫以及作業系統。最重要的是PHP可以用C、C++進行程式的擴充套件!以下是小編為大家搜尋整理的PHP中閉包的一些常見問題,希望能給大家帶來幫助!更多精彩內容請持續關注我們應屆畢業生考試網!

PHP中閉包的一些常見問題

首先說明下...閉包是js高階特性之一...但並非js獨有, python, php(5.3以上版本) 都是支援閉包的..

官方解釋: 所謂“閉包”,指的是一個擁有許多變數和綁定了這些變數的環境的表示式(通常是一個函式),因而這些變數也是該表示式的一部分

john resig解釋: 閉包就是內部函式可以訪問外部函式中所定義的變數,即使該函式已經執行結束。

如果你還是不能明白上面那句話...那麼我就換句話來說:

在js中...執行一個函式A...當函式A執行完後...理論上來講...改函式A內所有被定義的 臨時變數都將被 當成可回收的垃圾等待垃圾回收....然而在這個過程..有一種臨時變數是無法被垃圾回收的...當A函式中有一個內部函式a時.a函式內引用了A中定義的臨時變數...並且a函式在A函式執行完後..仍然可以被外部訪問到時...被a函式所引用的臨時變數就無法被當成垃圾等待垃圾回收.. 而a函式可以被外部訪問的同時..就生成了一個閉包...

舉個例子吧..也是比較經典的例子

//函式A 執行完後 它將返回一個函式a

function A(){

//定義一個臨時變數

var x = 1;

//返回一個內部函式a

//執行時列印臨時變數x

return function a(){

( x );

};

}

//執行A 得到內部函式a

//此時內部函式a被返回...它引用了臨時變數x

//理論上A執行後 x做為臨時變數將被當成垃圾等待垃圾回收

//但是由於內部函式a引用了x 所以此時就生成了一個閉包

var a = A();

//執行a 列印1

a(); //1

閉包並非定義函式時就生成的...而是在執行過程中 當a函式被當成一個返回值被返回時 才會生成一個閉包..

  閉包容易誤解的地方:

  1。 閉包總是在匿名函式中生成的

閉包並非都是在匿名函式中生成的..比如上一段程式碼中...被返回的函式有命名-a

  2。 閉包在定義時產生的...

閉包並非是在定義時產生的`...而是在內部函式可被外部訪問到時才會產生...

  3。 閉包很強大..用的越多就越牛A(==!)

不否認閉包很強大.....但是並非用的越多就是越好的...使用閉包..會造成除錯困難..所以要習慣做標識..另外...使用閉包會涉及到 增長函式作用域的 造成內部函式訪問全域性變數變慢的問題...

PHP中的閉包

php-5.3 以上版本其中一個更新就是使php支援了簡單的閉包

/**

* 一個curry的加法函式

* @param unknown_type $start 起始值

* @return unknown 返回一個匿名函式

*/

function add( $start = 0 ){

$sum = $start;

//該函式接受n個值..執行後返回值為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函式都會生成一個相應的$sum 每個函式執行後不衝突 可避免使用static變數 而且sum不會隨函式執行結束而消失 從而實現函式柯里化

閉包的使用

1. 函式柯里化

閉包在js中經常會被用過函式柯里化

比如上面php的那段程式碼中 改成js則是:

//add函式 返回一個匿名函式

function add( start ){

var sum = start || 0;

//該函式接受n個引數 返回值為n個引數的和+sum的值

return function(){

for( var i = 0, j = th; i < j; i++ ){

sum += Number( arguments[i] );

}

return sum;

}

}

var a = add( 1 );

a( 1, 2, 3 );

( a( 3 ) );

玩個有意思的函式 這個是別人曾經給我出的一道題目 當時我也沒想出來...(壓根把tostring這方法給忘了.)

題目需求要求可以這樣呼叫(當時的需求只要求傳一個引數)

//獲取curry後的函式

var a = add( 1 );

//呼叫多次相加

a( 1, 2, 3 )( 1, 2, 3 )( 1, 2, 3 );

//直接輸出函式

( a ); //19

//繼續相加後輸出

( a( 1, 2, 3 )( 1, 2, 3 ) ); //31

實現如下

//add函式 返回一個匿名函式

function add( start ){

var sum = start || 0;

//該函式接受n個引數 返回值為函式本身

//直接輸出函式時 列印sum的值

return function(){

//引數相加

for( var i = 0, j = th; i < j; i++ ){

sum += Number( arguments[i] );

}

//獲取函式本身

var func = ee;

//重寫函式tostring方法 用於列印函式

ring = function(){

return sum;

};

//返回函式本身

return func;

}

}

2。模擬物件中的私有屬性和方法

寫之前先解釋下 js非一門OO語言 它是一門基於物件的語言

如 var i = 0; 則i是一個數值型物件 轉成物件寫法則是 var i = new Number(1); 前一種叫過直接量表示法 同JSON(js物件字面量,表示js中物件的直接量表示方法) 直接量表示的速度要比 new 快

(1)模擬私有屬性和私有方法

//smarty模板引擎 模擬

function Smarty(){

//公有屬性 可被外部直接訪問

//左標籤

Limiter = '{';

//右標籤

tLimiter = '}';

//私有屬性 不可被外部直接訪問

//快取assign方法呼叫後的賦值

var cacheData = {};

//公用方法 assign

//準確來講..它叫做一個特權方法 可訪問內部私有屬性的方法叫做特權方法

//例項化smarty建構函式時 由於它是一個公用方法 可被外部訪問

//並且引用了cacheData臨時變數 所以cacheData不會垃圾回收 此時生成一個閉包

gn = function( name, value ){

//快取賦值

cacheData[name] = value;

}

//私有方法 fetch 編譯解析模板內容 返回結果 不輸出

//假設它是一個私有方法 不能被外部直接訪問

function fetch( tpl ){

//do something

return tpl;

}

//公用方法 輸出

lay = function( tpl ){

//呼叫內部私有方法 直接輸出

( fetch( tpl ) );

}

}

//例項化smarty

var template = new Smarty();

//設定左標籤

Limiter = '<{';

//設定右標籤

tLimiter = '}>';

//賦值

gn( 'name', 'jsyczhanghao' );

//賦值

gn( 'age', 23 );

//輸出最終編譯結果

lay( lementById( 'test' )rHTML );

(2)模擬私有靜態方法(單例模式-Zend framework 模擬前端控制器 phper你懂的..)

//模擬Zend framework 前端控制器

//定義一個匿名函式 定義完立即執行(function( window ){

//Zend_Controller主建構函式 //在js中無法設定私有的建構函式

//所以必須將建構函式設定為 非公開 才可以不讓外部呼叫的程式直接例項化建構函式 在公開物件中提供一個公開方法 間接去呼叫

var Zend_Controller = function(){

//設定控制器的路徑

ontrollerDirectory = function(){};

//分發路由

atch = function(){

( 1 );

};

};

//前端控制器的私有靜態屬性 外部不可直接訪問

//它為一個Zend_Controller的例項

var intance;

//公開類 前端控制器

var Zend_Controller_Front = function(){};

//獲取例項 一個共有靜態方法

//可被外部呼叫的方法 生成閉包 臨時變數instance和Zend_Controller不會消失

Zend_Controller_nstance = function(){

//返回如果已存在例項 則直接返回

//否則 先建立再返回

return instance || ( instance = new Zend_Controller() );

};

//實際的js中習慣會把單例模式會這麼寫

//將Zend_Controller_Front直接寫成一個物件 getinstance自然就成了一個公用方法 可直接呼叫

//_Controller_Front = {

// getInstance: function(){

// return instance || ( instance = new Zend_Controller() );

// }

//};

_Controller_Front = Zend_Controller_Front;

})( this );

var zend_instance = Zend_Controller_nstance();

zend_ontrollerDirectory( '/root' );

zend_atch();

3。事件回撥函式中的使用

//更新元素內容 ajax

//第一個引數為dom元素

//第二個引數傳送的url

function updateElement( elem, url ){

//jquery中ajax的get方法

//在 #js的非同步機制和大資料量的處理方案# 中有說到

//實際上在get方法過後...該函式已執行後

//get方法第2個引數的匿名函式 將會被丟到 UI佇列的最後面等待合適的機會觸發

//該機會就是ajax成功傳送並且成功返回狀態值時觸發

//由於匿名函式並非立即執行 且依賴於elem引數 所以elem不會被當垃圾進行回收 並在此生成一個閉包

//必須等到 匿名函式成功執行後才會被釋放..

$( url, function( data ){

//ajax傳送成功後 將返回的值 寫到元素中

rHTML = data;

});

} 以上是閉包絕大部分會出現的場景

#############################################################################################################

來看個問題吧:針對 #js的非同步機制和大資料量的處理方案# 中的一段程式碼段

for( var i = 0; i < 10; i++ ){

//為test0-test9繫結click事件

lementById( 'test' + i )ick = function(){

//列印對應的i

( i );

};

}

這段程式碼執行後 點選test0-test9並非象預期那樣.. 依次打印出0-9 而是每一個元素點選後都列印了10

造成的原因就是 繫結click事件時 回撥函式並未執行 當回撥函式執行時 i已經變成了10 所以列印的結果都會變成10

解決方法:

思路: 如果能找到一種方式可以將每一次的i都快取起來 並且一直到click事件觸發的時候 它都一直不會消失 不就完了麼

我們都知道 一個函式作用域內執行完後..作用域中的所有臨時變數都會消失 但是有一種不讓臨時變數消失的方式就是使用閉包。。而上面講閉包的使用場景時 其中有一條就是事件回撥函式 當一個事件回撥函式位於一個作用域內的時候...作用域執行外後 由於回撥函式並未馬上執行..而是等到相應事件觸發時才執行...當回撥函式依賴該作用域內的臨時變數時...導致該作用域內部使用的臨時變數無法馬上被當垃圾回收(意味著該臨時變數不會消失)

目前我們擁有一個事件回撥函式 要做的就是需要讓這個事件回撥函式位於一個函式作用域內

程式碼:

for( var i = 0; i < 10; i++ ){

//為test0-test9繫結click事件

function(){

lementById( 'test' + i )ick = function(){

//列印對應的i

( i );

};

};

}

這樣 事件繫結就位於一個匿名函式中了...但是這樣肯定不行...因為函式都沒有執行...函式內的程式碼肯定不會起作用....也就是說..這段程式碼能夠正常執行 不報錯..但是不會為每一個元素繫結一個事件..因為它的外部函式沒有執行

繼續修改:

for( var i = 0; i < 10; i++ ){

//為test0-test9繫結click事件

(function(){

lementById( 'test' + i )ick = function(){

//列印對應的i

( i );

};

})();

}

恩 這次看起來差不多了....繫結事件的行為位於一個匿名函式中..並且匿名函式定義後立即執行....

但是目前 繫結事件內的變數i並不是 匿名函式中所產生的臨時變數 i是一個全域性變數 i不會因為匿名函式的執行而一直保持 你所希望的值

所以我們需要在匿名函式內定義一個臨時變數 該臨時變數的值和當前相應的i值相等即可 將i直接賦值給該臨時變數就可以了..

最終修改程式碼:

for( var i = 0; i < 10; i++ ){

//為test0-test9繫結click事件

(function(){

var j = i;

lementById( 'test' + j )ick = function(){

//列印對應的i

( j );

};

})();

}其實不一定要直接賦值 當一個引數傳進去也行程式碼如下(執行結果一樣..過程也沒什麼區別..只是寫法不同)for( var i = 0; i < 10; i++ ){

//為test0-test9繫結click事件

(function( j ){

lementById( 'test' + j )ick = function(){

//列印對應的i

( j );

};

})( i );

}

其實還有一種不使用閉包的方式...在事件的回撥函式中直接引用 dom物件的一個屬性即可 因為dom物件是一直存在的 而指向當前的dom物件使用this即可for( var i = 0; i < 10; i++ ){

//為test0-test9繫結click事件

var elem = lementById( 'test' + i );

x = i;

ick = function(){

//列印對應的i

( x );

};

}

TAG標籤:常見問題 中閉 PHP #