Giới thiệu PHP và JavaScript Closures, cài đặt Drupal 8

PHP closures

PHP closures are pretty simple as they are barely more than syntactic sugar over the following:

class Something {
  function __construct($x) {
    $this->x = $x;
  }
  function __invoke($y) {
    extract(get_object_vars($this));
    // Your closure here.
  }
}

Becomes:

function something ($y) use ($x) {
  // Your closure here.
}

So closures are objects with a small difference: they are automatically constructed and once constructed they can not be changed and the only thing you can do with them is call them. Now it should be easy to see how variables work: variables given in use() are copied to properties on the object. If $x is an object itself then of course only its handler is copied so changing from inside the closure affects it everywhere else, exactly like how objects work in any other operation. All this is quite consistent on how PHP works and relatively simple to understand.

function foo() {
  $x = 1;
  $y = function() use (&$x) {
    $x++;
    print "in $x\n";
  };
  $y();
  print "$x\n";
  return $y;
}
$func = foo();
$func();
print "$x\n";

JavaScript

JavaScript is just a little different. First of all, there is no explicit import, every variable from the parent scope is imported. Second, since everything is an object, changing these variables affects the variables in the parent scope.

function foo() {
  var x = 1;
  var y = function() { x++; console.log('in' + x);}
  y();
  console.log(x);
  return y;
}
func = foo();
func();
console.log(x);

Flow Control

In both languages returning from a closure will simply return to the caller. If the closure is called in a loop then the loop will continue. Short of throwing an exception the closure can’t stop such a loop. See Smalltalk for an example of a language where this is different. Obviously, Common Lisp can do both kinds of returns and the syntax is succinct and easy to understand. Obviously again, Ruby can do both and the syntax is extremely obscure.

About $this / this

Since PHP 5.4, you can use $this in closure. Just imagine that one is passed in via use() and everything will be fine. So $this always means the object it is defined in even if the closure is passed to another method on another object. If necessary then a new closure can be created with a new $this variable: Closure::bind($closure, $newthis) or $closure->bindTo($newthis):

class foo {
  protected $x = 1;
  function bar() {
    return function() { 
      $this->x++; 
      print "$this->x\n";};
  }
}
$func = (new foo)->bar();
$func();
class bar {
  protected $x = 10;
}
$func2 = $func->bindTo(new bar, "bar"); // "bar" allows the closure to access protected things
$func2();

JavaScript this means the defining scope however it can be changed when calling the closures via the call or apply methods of the closure. This doesn't have a PHP equivalent. Your favorite framework or native DOM handling will often do this for you. each in jQuery sets this to the current object, event handlers will get the current event in this etc. ES5.1 in 2011 introduced the bind method on function objects which behaves exactly like bindTo in PHP: something.bind(newThis) returns a new closure with this being set to newThis. Examples:

function foo() {
  x = 1;
  var y = function() { this.x++; console.log(this.x);}
  y();
  return y;
}
var func = foo();
func();
var func = function() { this.x++; console.log(this.x);}
func.call({x:1});
o = {x:10};
var func2 = func.bind(o);
func2();