使用Perl解释JavaScript闭包

4
我毫不困惑地理解了关于perl闭包的工作原理,就像下面这个例子一样。
use 5.012;
use strict;
use warnings;

sub countdown {
        my $start = shift;
        return sub { $start-- }
}

my $c10 = countdown(3);
say while( $_ = $c10->() );

我正在尝试理解下一段Javascript代码:
var runInSandbox = (function(js, inputPath) {

  (function() {
    if ((!context.initialized__QUERY)) {
      return createContext();
    };
  })();
  (function() {
    if (typeof(inputPath) !== 'undefined') {
      (process.argv)[1] = inputPath;;
      (context)["__dirname"] = path.dirname(inputPath);;
      return (module)["filename"] = inputPath;;
    };
  })();
  return vm.runInContext(js, context, "sibilant");
});

没有机会!:( 请有人将上述内容改写为 perl ?我了解一些 Perl,因此对于我来说,理解 JS 基础知识和如下结构将非常有用:

(...)() - more precisely
(function(){.....})()

双倍 ((在if语句中

    if ((!context.initialized__QUERY)) {

下一个
      (context)["__dirname"] = something ;;

或者

       return (module)["filename"] = inputPath;; // why double ;;?

如果有人能向我推荐像这样的资源: Perl程序员学习JavaScript - 那就太好了 ;)

附注:JS(缩写)来自此处:https://github.com/jbr/sibilant/blob/master/lib/cli.js

2个回答

7

我并不是非常精通Perl closures,但我会尽力为您解开迷团。

以下是格式:

(function(...) {
 ...
})();

是一个自调用匿名函数1。这意味着您编写了一个匿名函数,然后立即调用它。通常情况下,这样做是为了封装2。例如,如果您最终创建了一堆变量,但不想污染全局命名空间,可以将其放在匿名的自调用函数中。然而,在这种情况下,我并不明白为什么第一个调用根本不必要,因为它只是检查一个标志或其他东西。更奇怪的是,这些自调用函数中的return。它们没有被分配给任何东西。我猜createContext()初始化context变量,但其中的return实际上是无用的。以下内容同理:

return (module)["filename"] = inputPath;;

就双重括号(())而言,它们似乎很大程度上是不必要的,所以我不确定作者最初为什么要把它放在那里。例如:
if ((!context.initialized__QUERY)) 

和任何不同的是:

if (!context.initialized__QUERY) 

此外,以下内容中的括号以及双分号也是不必要的:
(context)["__dirname"] = something ;;

说实在的,这个代码看起来像是写得不好的Javascript,或者可能是自动生成的Javascript(最有可能的情况)。 你可以重写它,像这样:
var runInSandbox = function(js, inputPath) {

    if (!context.initialized__QUERY) {
       createContext();
    };

    if (typeof inputPath !== 'undefined') {
       process.argv[1] = inputPath;
       context["__dirname"] = path.dirname(inputPath);
       module["filename"] = inputPath;
    };

    return vm.runInContext(js, context, "sibilant");
};

注:

  1. 在 Perl 中,可以写成 sub { ... }->()
  2. 在 Perl 中,应该使用 { my $var; ... } 替代 sub { my $var; ... }->(),并且应该使用 do { my $var; ...; EXPR } 替代 sub { my $var; ...; return EXPR; }->()

回复:“说实话,它看起来就像是写得很差的Javascript代码”,或者是以某种方式自动生成的。 - ruakh
1
我不懂JS,但如果你的重写与OP问题中的内容相同,那么你的代码是可理解和干净的 ;) - clt60
1
@ruakh 刚刚检查了一下 - 你是对的 - 上面的代码是生成的。接受这个答案是因为它帮助我理解了自调用函数。非常感谢你。 ;) - novacik
1
我在脚注中添加了几个与Perl相关的链接。 - ikegami

0

我认为这是在Perl中应该看起来的样子。

my $runInSandbox = sub {
     local($js, $inputPath) = ($_[0], $_[1]);
     sub{if (!$context.initialized_QUERY) {
         &createContext();
     }}->();
     sub{ if (defined($inputPath) {
         $process{"argv"][1] = inputPath;
         $context{"__dirname"} = &$path{"dirname"}(inputPath);
         $module["filename"] = inputPath;
     } }->();
     return &$vm{"runInContext"}($js,$context,"sibilant");
};

这甚至无法编译。&{...}是一个解引用代码引用的语法。你可能想到了do{...}块,它们很像sub{...}->(),但在这里完全没有必要。 - amon
谢谢。我试图逐字翻译JS,但我想我的Perl比我想象的更生疏。;) - user1958756

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接