首页 »PHP教程 » javascript闭包:PHP V5.3 中的新特性 第 2 部分: 闭包及 lambda 函数 »正文
javascript闭包:PHP V5.3 中的新特性 第 2 部分: 闭包及 lambda 函数
来源: 发布时间:星期三, 2009年1月21日 浏览:185次 评论:0
闭包  和 lambda  绝对不是新出现  概念;它们均来自  编程领域   编程 是  种编程风格  它将关注点从执行命令转移到表达式计算  这些表达式是使用  构成   结合这些  可以得到我们要查找  结果  这种编程风格最常用于学术目   但是也可以在人工智能和数学领域中见到  并且可以在用 Erlang、Haskell 及 Scheme 等语言编写  商业应用  中找到  闭包 最初是在 20 世纪 60 年代作为 Scheme   部分开发   Scheme 是最著名   编程语言的   Lambda  和闭包通常出现在允许将  处理为第  类值(First-  value)  语言中  这意味着  可以动态创建并作为参数传递给其他语言  从那时起  闭包及 lambda  已经找到了走出  编程世界并进入 JavaScript、Python 和 Ruby 等语言  思路方法  JavaScript 是支持闭包和 lambda   最常见语言的   JavaScript 实际使用这些  作为支持面向对象  编程思路方法  把  嵌套到其他  中以用作私有成员  清单 1 提供了 JavaScript 如何使用闭包  举例  清单 1. 使用闭包构建 JavaScript 对象 var Example = function { this.public = function { "This is a public method"; }; var private = function { "This is a private method"; }; }; Example.public // s "This is a public method" Example.private // error - doesn't work 如清单 1 中所示  Example 对象  成员  被定义为闭包  由于私有思路方法作用于局部变量(和绑定到使用此关键字  Example 对象  公共思路方法相反)  因此从外部看不到它 
现在我们已经了解了这些概念  历史  让我们查看 PHP 中  lambda   lambda   概念是闭包  基础  并且提供了  种比 PHP 中已有  create_function  改进了很多  动态创建   思路方法  Lambda  Lambda  (或者通常所谓  “匿名  ”)是可以随时定义  简单抛弃型   并且通常都和变量绑定   本身仅存在于定义   变量范围内  因此当该变量超出范围时   也超出范围  lambda   理念源于 20 世纪 30 年代  数学研究  它被称为 lambda 演算  用于研究  定义和应用  以及递归概念  lambda 演算用于开发  编程语言  例如 Lisp 和 Scheme  对于大多数例子来说  尤其对于接受回调   许多 PHP  来说  Lambda  非常方便   .gif' />_map  就是这样  种   它允许我们遍历  并将回调  应用到   每个元素上  在早期版本  PHP 中  这些   最大问题是没有  种清晰  方式定义回调  ;我们坚持使用以下 3种解决思路方法  其中  种: 我们可以在代码中  其他位置定义回调   因此我们知道它可用  这有些麻烦   它把   实现部分移到了其他位置  这样做对于可读性和可维护性极为不便  尤其是不打算在其他位置使用此  时 我们可以在同  个代码块中定义回调   但是使用  个名称  虽然这样做有助于把内容放在  起  但是需要在定义周围添加  块以避免名称空间冲突  清单 2 展示了这种思路方法  清单 2. 在同  个代码块中定义指定  回调 function quoteWords { (!function_exists ('quoteWordsHelper')) { function quoteWordsHelper($ ) { preg_replace('/(w)/','"$1"',$ ); } } .gif' />_map('quoteWordsHelper', $text); }
我们可以使用 create_function  (从 V4 开始就是 PHP   部分)在运行时创建   虽然在功能上此  执行了所需操作  但是它有  些缺点   个主要缺点是  它在运行时而非编译时编译  不允许操作码缓存Cache来缓存Cache   它  语法智能(syntax-wise)也非常糟糕  并且大多数 IDE 中   串高亮显示功能完全不起作用 虽然接受回调    功能十分强大  但是没有  种好思路方法可以执行  次性回调   而无需执行  些非常笨拙  工作  使用 PHP V5.3  我们可以使用 lambda  以更规则  思路方法重新执行以上举例  清单 3. 使用 lambda  用于回调  quoteWords  function quoteWords { .gif' />_map('quoteWordsHelper', function ($ ) { preg_replace('/(w)/','"$1"',$ ); }); } 我们看到了定义这些   更规则  语法  这可以通过操作码缓存Cache来优化性能  我们也已经得到了改进  可读性以及和  串高亮显示功能  兼容性  让我们在此基础上了解如何在 PHP 中使用闭包  闭包 Lambda  本身并没有添加以前不能执行  功能  正如我们所见  我们可以使用 create_function  执行所有这项操作  尽管后果是使用更糟糕  语法并且性能更加不理想  但是它们仍然是抛弃型  并且不维护任何类型  状态  这限制了我们可以用它们执行  操作  因此出现了闭包并使 lambda  得到增强  闭包是在它自己  环境中执行计算    它有  个或多个绑定变量可以在   时访问  它们来自  编程世界  其中涉及大量概念  闭包类似于 lambda   但是在和定义闭包  外部环境中  变量进行交互方面更加智能 
让我们看  看如何在 PHP 中定义闭包  清单 4 显示了从外部环境导入变量并将其简单地输出到屏幕上  闭包举例  清单 4. 简单闭包举例 $ = "Hello World!"; $closure = function use ($ ) { echo $ ; }; $closure ; Output: Hello World! 从外部环境中导入  变量是在闭包  定义  use 子句中指定   默认情况下  它们是由值传递   意味着如果要更新传递到闭包  定义内  值  则不更新外部值  但是  我们可以通过在变量前放置 & 运算符来完成此操作  这种思路方法在  定义中用于表示按引用传递  清单 5 显示了这种思路方法  举例  清单 5. 通过引用传递变量  闭包 $x = 1 $closure = function use (&$x) { $x; } echo $x . "n"; $closure ; echo $x . "n"; $closure ; echo $x . "n"; Output: 1 2 3 可以看到  闭包使用外部变量 $x 并在每次  闭包时递增该变量  我们可以将按值和按引用传递  变量轻松地混合到 use 子句中  并且可以顺利地处理这些变量  我们也可以拥有直接返回闭包    如清单 6 所示  在本例中  闭包  生命周期实际上比定义闭包  思路方法长  清单 6.  返回  闭包 function getAppender($baseString) { function($appendString) use ($baseString) { $baseString . $appendString; }; } 闭包和对象 闭包不但是过程式编程  有用工具  而且是面向对象编程  有用工具  在这种情况下使用闭包和在类外部使用闭包实现  目  相同:包含在小范围内绑定  特定   在对象外部和在对象内部使用  样简单 
在对象内定义时  非常方便   点是闭包通过 $this 变量拥有对对象  完全访问权  而无需显式导入  清单 7 演示了该举例  清单 7. 对象内  闭包 Dog { private $_name; protected $_color; public function __construct($name, $color) { $this->_name = $name; $this->_color = $color; } public function greet($greeting) { function use ($greeting) { echo "$greeting, I am a {$this->_color} dog named {$this->_name}."; }; } } $dog = Dog("Rover","red"); $dog->greet("Hello"); Output: Hello, I am a red dog named Rover. 在这里  我们在闭包内显式使用提供给 greet  思路方法  欢迎词  闭包在该思路方法内定义  我们还在闭包内获取狗  颜色和名字  传递到构造  中并存储到对象中  在类中定义  闭包基本上和在对象外部定义  闭包相同  惟   区别的处在于通过 $this 变量自动导入对象  我们可以通过将闭包定义为静态闭包禁用此行为  清单 8. 静态闭包 House { public function pa ($color) { function use ($color) { echo "Pa ing the house $color...."; }; } } $house = House ; $house->pa ('red'); Output: Pa ing the house red....
此举例类似于清单 5 中定义  Dog 类  最大  差别是在闭包内不使用对象  任何属性   它被定义为静态类  在对象内使用静态闭包和使用非静态闭包相比  最大优点是节省内存  由于无需将对象导入闭包中  因此可以节省大量内存  尤其是在拥有许多不需要此功能  闭包时  针对对象  另  个优点是添加名为 __invoke  魔术思路方法  此思路方法允许对象本身被  为闭包  如果定义了此思路方法  则在该上下文中  对象时将使用此思路方法  清单 9 演示了举例  清单 9. 使用 __invoke  思路方法 Dog { public function __invoke { echo "I am a dog!"; } } $dog = Dog ; $dog ; 将清单 9 中所示  对象引用  为变量将自动  __invoke  魔术思路方法  使类本身用作闭包  闭包可以很好地和面向对象  代码以及面向过程  代码整合  让我们看  看闭包如何和 PHP  强大 Reflection API 交互  闭包和反射 PHP 有  个有用  反射 API  它允许我们对类、接口、  和思路方法执行反向工程  按照设计  闭包是匿名   这意味着它们不显示在反射 API 中  但是  新 getClosure  思路方法已经添加到 PHP 中  ReflectionMethod 和 ReflectionFunction 类中  可以从指定   或思路方法动态创建闭包  它在此上下文中用作宏  通过闭包   思路方法将在定义它  上下文中执行    清单 10 显示了此思路方法如何运行  清单 10. 使用 getClosure  思路方法 Counter { private $x; public function __construct { $this->x = 0; } public function increment { $this->x ; } public function currentValue { echo $this->x . "n"; } } $ = ReflectionClass('Counter'); $method = $ ->getMethod('currentValue'); $closure = $method->getClosure $closure ; $ ->increment ; $closure ; Output: 0 1
这种思路方法   个有趣  副作用是允许通过闭包访问类  私有成员和受保护成员  这有利于对类执行单元测试  清单 11 展示了对类  私有思路方法  访问  清单 11. 访问类  私有思路方法 Example { .... private function secret { echo "I'm an method that's hiding!"; } ... } $ = ReflectionClass('Example'); $method = $ ->getMethod('secret'); $closure = $method->getClosure $closure ; Output: I'm an method that's hiding! 此外  可以使用反射 API 来内省(  rospect)闭包本身  如清单 12 所示  只需将对闭包  变量引用传递到 ReflectionMethod 类  构造  中  清单 12. 使用反射 API 内省闭包 $closure = function ($x, $y = 1) {}; $m = ReflectionMethod($closure); Reflection::export ($m); Output: Method [ < ernal> public method __invoke ] { - Parameters [2] { Parameter #0 [ <required> $x ] Parameter #1 [ <optional> $y ] } } 有关向后兼容性值得注意   点是  PHP 引擎现在保留类名 Closure 并用于存储闭包  因此使用该名称  所有类都需要重命名  正如我们所见  反射 API 能够通过现有  和思路方法动态创建闭包  从而为闭包提供强大  支持  它们还可以像普通   样内省到闭包中  为什么使用闭包? 如在 lambda   举例中所示  闭包  最明显使用方法的  是少数 PHP  接受回调  作为参数  但是  在需要把逻辑封装到自己  范围内  情况下  闭包会十分有用  重构旧代码以进行简化并提高可读性就是这样  个例子  查看以下举例  该举例显示了在运行  些 SQL 查询时使用  记录  
清单 13. 记录 SQL 查询  代码 $db = mysqli_connect("server","user","pass"); Logger::log('debug','database','Connected to database'); $db->query('insert o parts (part, description) values ('Hammer','Pounds nails'); Logger::log('debug','database','Insert Hammer o to parts table'); $db->query('insert o parts (part, description) values ('Drill','Puts holes in wood'); Logger::log('debug','database','Insert Drill o to parts table'); $db->query('insert o parts (part, description) values ('Saw','Cuts wood'); Logger::log('debug','database','Insert Saw o to parts table'); 从清单 13 中可以看出执行操作  重复程度  对 Logger::log  执行  每次  都有相同  前两个实参  为了解决此问题  我们可以把该思路方法  放入闭包并转而针对该闭包执行   得到  代码如下所示: 清单 14. 记录 SQL 查询  重构代码 $logdb = function ($ ) { Logger::log('debug','database',$ ); }; $db = mysqli_connect("server","user","pass"); $logdb('Connected to database'); $db->query('insert o parts (part, description) values ('Hammer','Pounds nails'); $logdb('Insert Hammer o to parts table'); $db->query('insert o parts (part, description) values ('Drill','Puts holes in wood'); $logdb('Insert Drill o to parts table'); $db->query('insert o parts (part, description) values ('Saw','Cuts wood'); $logdb('Insert Saw o to parts table'); 代码不但在外观上更加规则  而且更易于更改 SQL 查询日志  日志级别   现在只需要在  个位置进行更改  结束语 本文演示了闭包在 PHP V5.3 代码中作为  编程构造时多么有用  我们讨论了 lambda  及闭包和这些  相比  优点  对象和闭包可以很好地结合使用  比如我们在面向对象  代码内对闭包  特殊处理  我们看到了如何使用反射 API 创建动态闭包  以及如何内省现有  闭包
相关文章
读者评论
发表评论
|
|