亚洲精品中文字幕无乱码_久久亚洲精品无码AV大片_最新国产免费Av网址_国产精品3级片

php語言

PHP中閉包的一些常見問題

時間:2024-11-01 04:45:24 php語言 我要投稿
  • 相關(guān)推薦

PHP中閉包的一些常見問題

  PHP具有非常強大的功能,所有的CGI的功能PHP都能實現(xiàn),而且支持幾乎所有流行的數(shù)據(jù)庫以及操作系統(tǒng)。最重要的是PHP可以用C、C++進行程序的擴展!以下是小編為大家搜索整理的PHP中閉包的一些常見問題,希望能給大家?guī)韼椭?更多精彩內(nèi)容請持續(xù)關(guān)注我們應(yīng)屆畢業(yè)生考試網(wǎng)!

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

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

  john resig解釋: 閉包就是內(nèi)部函數(shù)可以訪問外部函數(shù)中所定義的變量,即使該函數(shù)已經(jīng)執(zhí)行結(jié)束。

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

  在js中...執(zhí)行一個函數(shù)A...當(dāng)函數(shù)A執(zhí)行完后...理論上來講...改函數(shù)A內(nèi)所有被定義的 臨時變量都將被 當(dāng)成可回收的垃圾等待垃圾回收....然而在這個過程..有一種臨時變量是無法被垃圾回收的...當(dāng)A函數(shù)中有一個內(nèi)部函數(shù)a時.a函數(shù)內(nèi)引用了A中定義的臨時變量...并且a函數(shù)在A函數(shù)執(zhí)行完后..仍然可以被外部訪問到時...被a函數(shù)所引用的臨時變量就無法被當(dāng)成垃圾等待垃圾回收.. 而a函數(shù)可以被外部訪問的同時..就生成了一個閉包...

  舉個例子吧..也是比較經(jīng)典的例子

  //函數(shù)A 執(zhí)行完后 它將返回一個函數(shù)a

  function A(){

  //定義一個臨時變量

  var x = 1;

  //返回一個內(nèi)部函數(shù)a

  //執(zhí)行時打印臨時變量x

  return function a(){

  console.log( x );

  };

  }

  //執(zhí)行A 得到內(nèi)部函數(shù)a

  //此時內(nèi)部函數(shù)a被返回...它引用了臨時變量x

  //理論上A執(zhí)行后 x做為臨時變量將被當(dāng)成垃圾等待垃圾回收

  //但是由于內(nèi)部函數(shù)a引用了x 所以此時就生成了一個閉包

  var a = A();

  //執(zhí)行a 打印1

  a(); //1

  閉包并非定義函數(shù)時就生成的...而是在執(zhí)行過程中 當(dāng)a函數(shù)被當(dāng)成一個返回值被返回時 才會生成一個閉包..

  閉包容易誤解的地方:

  1。 閉包總是在匿名函數(shù)中生成的

  閉包并非都是在匿名函數(shù)中生成的..比如上一段代碼中...被返回的函數(shù)有命名-a

  2。 閉包在定義時產(chǎn)生的...

  閉包并非是在定義時產(chǎn)生的...而是在內(nèi)部函數(shù)可被外部訪問到時才會產(chǎn)生...

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

  不否認閉包很強大.....但是并非用的越多就是越好的...使用閉包..會造成調(diào)試?yán)щy..所以要習(xí)慣做標(biāo)識..另外...使用閉包會涉及到 增長函數(shù)作用域的 造成內(nèi)部函數(shù)訪問全局變量變慢的問題...

  PHP中的閉包

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

  /**

  * 一個curry的加法函數(shù)

  * @param unknown_type $start 起始值

  * @return unknown 返回一個匿名函數(shù)

  */

  function add( $start = 0 ){

  $sum = $start;

  //該函數(shù)接受n個值..執(zhí)行后返回值為n值和$sum的總和

  return function () use ( &$sum ){

  //獲取所有參數(shù)

  $args = func_get_args();

  for( $i = 0; $i < count($args); $i++ ){

  $sum += (int)$args[$i];

  }

  return $sum;

  };

  }

  //初始化值為1

  $add = add( 1 );

  //在初始化值的基礎(chǔ)上加上1 2 3

  $add( 1, 2, 3 );

  //再加一次3 輸出

  echo $add( 3 ); //10

  ?> 這段代碼的作用是 每調(diào)用一次add函數(shù)都會生成一個相應(yīng)的$sum 每個函數(shù)執(zhí)行后不沖突 可避免使用static變量 而且sum不會隨函數(shù)執(zhí)行結(jié)束而消失 從而實現(xiàn)函數(shù)柯里化

  閉包的使用

  1. 函數(shù)柯里化

  閉包在js中經(jīng)常會被用過函數(shù)柯里化

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

  //add函數(shù) 返回一個匿名函數(shù)

  function add( start ){

  var sum = start || 0;

  //該函數(shù)接受n個參數(shù) 返回值為n個參數(shù)的和+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 ) );

  玩?zhèn)有意思的函數(shù) 這個是別人曾經(jīng)給我出的一道題目 當(dāng)時我也沒想出來...(壓根把tostring這方法給忘了.)

  題目需求要求可以這樣調(diào)用(當(dāng)時的需求只要求傳一個參數(shù))

  //獲取curry后的函數(shù)

  var a = add( 1 );

  //調(diào)用多次相加

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

  //直接輸出函數(shù)

  console.log( a ); //19

  //繼續(xù)相加后輸出

  console.log( a( 1, 2, 3 )( 1, 2, 3 ) ); //31

  實現(xiàn)如下

  //add函數(shù) 返回一個匿名函數(shù)

  function add( start ){

  var sum = start || 0;

  //該函數(shù)接受n個參數(shù) 返回值為函數(shù)本身

  //直接輸出函數(shù)時 打印sum的值

  return function(){

  //參數(shù)相加

  for( var i = 0, j = arguments.length; i < j; i++ ){

  sum += Number( arguments[i] );

  }

  //獲取函數(shù)本身

  var func = arguments.callee;

  //重寫函數(shù)tostring方法 用于打印函數(shù)

  func.toString = function(){

  return sum;

  };

  //返回函數(shù)本身

  return func;

  }

  }

  2。模擬對象中的私有屬性和方法

  寫之前先解釋下 js非一門OO語言 它是一門基于對象的語言

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

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

  //smarty模板引擎 模擬

  function Smarty(){

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

  //左標(biāo)簽

  this.leftLimiter = '{';

  //右標(biāo)簽

  this.rightLimiter = '}';

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

  //緩存assign方法調(diào)用后的賦值

  var cacheData = {};

  //公用方法 assign

  //準(zhǔn)確來講..它叫做一個特權(quán)方法 可訪問內(nèi)部私有屬性的方法叫做特權(quán)方法

  //實例化smarty構(gòu)造函數(shù)時 由于它是一個公用方法 可被外部訪問

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

  this.assign = function( name, value ){

  //緩存賦值

  cacheData[name] = value;

  }

  //私有方法 fetch 編譯解析模板內(nèi)容 返回結(jié)果 不輸出

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

  function fetch( tpl ){

  //do something

  return tpl;

  }

  //公用方法 輸出

  this.display = function( tpl ){

  //調(diào)用內(nèi)部私有方法 直接輸出

  console.log( fetch( tpl ) );

  }

  }

  //實例化smarty

  var template = new Smarty();

  //設(shè)置左標(biāo)簽

  template.leftLimiter = '<{';

  //設(shè)置右標(biāo)簽

  template.rightLimiter = '}>';

  //賦值

  template.assign( 'name', 'jsyczhanghao' );

  //賦值

  template.assign( 'age', 23 );

  //輸出最終編譯結(jié)果

  template.display( document.getElementById( 'test' ).innerHTML );

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

  //模擬Zend framework 前端控制器

  //定義一個匿名函數(shù) 定義完立即執(zhí)行(function( window ){

  //Zend_Controller主構(gòu)造函數(shù) //在js中無法設(shè)置私有的構(gòu)造函數(shù)

  //所以必須將構(gòu)造函數(shù)設(shè)置為 非公開 才可以不讓外部調(diào)用的程序直接實例化構(gòu)造函數(shù) 在公開對象中提供一個公開方法 間接去調(diào)用

  var Zend_Controller = function(){

  //設(shè)置控制器的路徑

  this.setControllerDirectory = function(){};

  //分發(fā)路由

  this.dispatch = function(){

  console.log( 1 );

  };

  };

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

  //它為一個Zend_Controller的實例

  var intance;

  //公開類 前端控制器

  var Zend_Controller_Front = function(){};

  //獲取實例 一個共有靜態(tài)方法

  //可被外部調(diào)用的方法 生成閉包 臨時變量instance和Zend_Controller不會消失

  Zend_Controller_Front.getInstance = function(){

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

  //否則 先創(chuàng)建再返回

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

  };

  //實際的js中習(xí)慣會把單例模式會這么寫

  //將Zend_Controller_Front直接寫成一個對象 getinstance自然就成了一個公用方法 可直接調(diào)用

  //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。事件回調(diào)函數(shù)中的使用

  //更新元素內(nèi)容 ajax

  //第一個參數(shù)為dom元素

  //第二個參數(shù)發(fā)送的url

  function updateElement( elem, url ){

  //jquery中ajax的get方法

  //在 #js的異步機制和大數(shù)據(jù)量的處理方案# 中有說到

  //實際上在get方法過后...該函數(shù)已執(zhí)行后

  //get方法第2個參數(shù)的匿名函數(shù) 將會被丟到 UI隊列的最后面等待合適的機會觸發(fā)

  //該機會就是ajax成功發(fā)送并且成功返回狀態(tài)值時觸發(fā)

  //由于匿名函數(shù)并非立即執(zhí)行 且依賴于elem參數(shù) 所以elem不會被當(dāng)垃圾進行回收 并在此生成一個閉包

  //必須等到 匿名函數(shù)成功執(zhí)行后才會被釋放..

  $.get( url, function( data ){

  //ajax發(fā)送成功后 將返回的值 寫到元素中

  elem.innerHTML = data;

  });

  } 以上是閉包絕大部分會出現(xiàn)的場景

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

  來看個問題吧:針對 #js的異步機制和大數(shù)據(jù)量的處理方案# 中的一段代碼段

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

  //為test0-test9綁定click事件

  document.getElementById( 'test' + i ).onclick = function(){

  //打印對應(yīng)的i

  console.log( i );

  };

  }

  這段代碼執(zhí)行后 點擊test0-test9并非象預(yù)期那樣.. 依次打印出0-9 而是每一個元素點擊后都打印了10

  造成的原因就是 綁定click事件時 回調(diào)函數(shù)并未執(zhí)行 當(dāng)回調(diào)函數(shù)執(zhí)行時 i已經(jīng)變成了10 所以打印的結(jié)果都會變成10

  解決方法:

  思路: 如果能找到一種方式可以將每一次的i都緩存起來 并且一直到click事件觸發(fā)的時候 它都一直不會消失 不就完了么

  我們都知道 一個函數(shù)作用域內(nèi)執(zhí)行完后..作用域中的所有臨時變量都會消失 但是有一種不讓臨時變量消失的方式就是使用閉包。。而上面講閉包的使用場景時 其中有一條就是事件回調(diào)函數(shù) 當(dāng)一個事件回調(diào)函數(shù)位于一個作用域內(nèi)的時候...作用域執(zhí)行外后 由于回調(diào)函數(shù)并未馬上執(zhí)行..而是等到相應(yīng)事件觸發(fā)時才執(zhí)行...當(dāng)回調(diào)函數(shù)依賴該作用域內(nèi)的臨時變量時...導(dǎo)致該作用域內(nèi)部使用的臨時變量無法馬上被當(dāng)垃圾回收(意味著該臨時變量不會消失)

  目前我們擁有一個事件回調(diào)函數(shù) 要做的就是需要讓這個事件回調(diào)函數(shù)位于一個函數(shù)作用域內(nèi)

  代碼:

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

  //為test0-test9綁定click事件

  function(){

  document.getElementById( 'test' + i ).onclick = function(){

  //打印對應(yīng)的i

  console.log( i );

  };

  };

  }

  這樣 事件綁定就位于一個匿名函數(shù)中了...但是這樣肯定不行...因為函數(shù)都沒有執(zhí)行...函數(shù)內(nèi)的代碼肯定不會起作用....也就是說..這段代碼能夠正常執(zhí)行 不報錯..但是不會為每一個元素綁定一個事件..因為它的外部函數(shù)沒有執(zhí)行

  繼續(xù)修改:

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

  //為test0-test9綁定click事件

  (function(){

  document.getElementById( 'test' + i ).onclick = function(){

  //打印對應(yīng)的i

  console.log( i );

  };

  })();

  }

  恩 這次看起來差不多了....綁定事件的行為位于一個匿名函數(shù)中..并且匿名函數(shù)定義后立即執(zhí)行....

  但是目前 綁定事件內(nèi)的變量i并不是 匿名函數(shù)中所產(chǎn)生的臨時變量 i是一個全局變量 i不會因為匿名函數(shù)的執(zhí)行而一直保持 你所希望的值

  所以我們需要在匿名函數(shù)內(nèi)定義一個臨時變量 該臨時變量的值和當(dāng)前相應(yīng)的i值相等即可 將i直接賦值給該臨時變量就可以了..

  最終修改代碼:

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

  //為test0-test9綁定click事件

  (function(){

  var j = i;

  document.getElementById( 'test' + j ).onclick = function(){

  //打印對應(yīng)的i

  console.log( j );

  };

  })();

  }其實不一定要直接賦值 當(dāng)一個參數(shù)傳進去也行代碼如下(執(zhí)行結(jié)果一樣..過程也沒什么區(qū)別..只是寫法不同)for( var i = 0; i < 10; i++ ){

  //為test0-test9綁定click事件

  (function( j ){

  document.getElementById( 'test' + j ).onclick = function(){

  //打印對應(yīng)的i

  console.log( j );

  };

  })( i );

  }

  其實還有一種不使用閉包的方式...在事件的回調(diào)函數(shù)中直接引用 dom對象的一個屬性即可 因為dom對象是一直存在的 而指向當(dāng)前的dom對象使用this即可for( var i = 0; i < 10; i++ ){

  //為test0-test9綁定click事件

  var elem = document.getElementById( 'test' + i );

  elem.index = i;

  elem.onclick = function(){

  //打印對應(yīng)的i

  console.log( this.index );

  };

  }

【PHP中閉包的一些常見問題】相關(guān)文章:

PHP中多態(tài)如何實現(xiàn)09-04

PHP中實現(xiàn)頁面跳轉(zhuǎn)07-06

PHP中this關(guān)鍵字06-08

PHP中的trait是什么08-13

php中fsockopen用法實例06-20

PHP前端開發(fā)中的性能05-25

php中引用的用法分析06-22

對php一些服務(wù)器端特性配置來加強php的安全06-14

php中目錄文件操作詳談09-20

php中的daddslashes()和saddslashes()的區(qū)別08-24