奇怪的JavaScript习语 - "/xyz/.test(function(){xyz;})"是什么意思?

46

John Resig编写了一个很棒的类函数swanky。我正在尝试弄清楚发生了什么,并且几乎已经弄清楚所有东西,除了一行:

fnTest = /xyz/.test(function () {xyz;}) ? /\b_super\b/ : /.*/;

一些事情立即浮现在脑海中。首先,xyz从未初始化为变量; 那么为什么这个可以工作呢?其次,为什么要将/xyz/与未返回任何内容的东西进行测试(没有返回语句)。除非有我不知道的JavaScript的巧妙属性(这是可能的,我自认为在JS方面相当擅长,并且可以解释我遇到的大多数代码,但这并不意味着我和John Resig生活在同一个珠穆朗玛峰大小的山上)。

对于那些好奇的人,这是来自john resigs网站的完整未编辑代码John Resig Simple Javascript Inheritance

(function () {
  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

  // The base Class implementation (does nothing)
  this.Class = function(){};

  // Create a new Class that inherits from this class
  Class.extend = function(prop) {
    var _super = this.prototype;

    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;

    // Copy the properties over onto the new prototype
    for (var name in prop) {
      // Check if we're overwriting an existing function
      prototype[name] = typeof prop[name] == "function" &&
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          return function() {
            var tmp = this._super;

            // Add a new ._super() method that is the same method
            // but on the super-class
            this._super = _super[name];

            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            var ret = fn.apply(this, arguments);       
            this._super = tmp;

            return ret;
          };
        })(name, prop[name]) :
        prop[name];
    }

    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }

    // Populate our constructed prototype object
    Class.prototype = prototype;

    // Enforce the constructor to be what we expect
    Class.constructor = Class;

    // And make this class extendable
    Class.extend = arguments.callee;

    return Class;
  };

})();

1
读者(不)正常检查? :) - mykhal
我把代码放在<code> ... </code>标签中,不知道为什么它会出现这么多问题(注意>..<不会被评估为我认为它们应该的样子),唉,今天真是我的倒霉日。 - Akidi
1个回答

51

这只是一种快速而不完善的方法,用于检查"函数反编译"是否有效。

RegExp.prototype.test 方法将接收到的参数转换为字符串,函数中的 xyz 引用从未被评估。

为什么需要检查这个?

因为 Function.prototype.toString 方法返回一个实现相关的函数表示,在某些实现中,例如较旧版本的Safari、移动版Opera和一些Blackberry浏览器,它们实际上并没有返回任何有用的信息。


2
@mykhal,xyz是你实际上正在寻找的内容,以了解函数反编译是否正常工作。它只是检查将函数转换为字符串后,生成的字符串是否包含函数体... - Christian C. Salvadó
4
补充说明一下:为了使测试通过jslint,必须是/var xyz/.test(function () { var xyz; }) ? /\b_super\b/ : /[\D|\d]*/; 为什么选择\D|\d?因为其中一个捕获所有数字,另一个捕获其他所有内容,相当于.*,但这被认为是“不安全”的,因为没有定义限制。我花了很长时间才意识到他们所说的“不安全”是什么意思。我投了您一票,并将其标记为答案,非常感谢CMS。 - Akidi
在这段代码中我不理解的是为什么他甚至需要 fnTest。难道 typeof foo === "function" 不足以涵盖所有情况吗? - Thomas Eding
3
@trinithis,好的,“typeof foo == 'function`”可以工作,但问题是即使它们不使用“_super”,你最终仍将包装每个函数对象。Resig只是检查每个函数,看它是否在其主体中使用“_super”标识符。如果“_super”出现在函数的主体中,那么他会将其包装到另一个函数中,在其中首先公开“类”的“super”,通过赋值“this._super”。然后他执行被检查的原始函数,证明它使用了“_super”,最后恢复“this._super”的值... - Christian C. Salvadó
@Akidi 我们在一年前的代码库中发现了这个 var xyz; 变量,但我们认为它只是试图通过不必要地将类函数包装在另一个函数中来使调试更容易或提高性能。然而,我们决定将其删除,因为我们可以在 Chrome 中黑盒脚本,并且如果脚本被压缩(如我们在生产中所做的那样),它总是返回 false。 - Wet Noodles
显示剩余5条评论

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