goog.inherit Vs. simple inherit

JavaScript performance comparison

Revision 2 of this test case created by Benny Bennet

Info

Performance comparision between goog.inherit and John Resigs simple inherit, and tweaked version of it.

Based on this jsperf: http://jsperf.com/goog-inherits-vs-john-resig-extend/3

Preparation code

 
<script>
Benchmark.prototype.setup = function() {
    var goog = {};
    goog.inherits = function(childCtor, parentCtor) { /** @constructor */
   
      function tempCtor() {};
      tempCtor.prototype = parentCtor.prototype;
      childCtor.superClass_ = parentCtor.prototype;
      childCtor.prototype = new tempCtor(); /** @override */
      childCtor.prototype.constructor = childCtor;
    };
    goog.base = function(me, opt_methodName, var_args) {
      var caller = arguments.callee.caller;
      if (caller.superClass_) {
        // This is a constructor. Call the superclass constructor.
        return caller.superClass_.constructor.apply(
        me, Array.prototype.slice.call(arguments, 1));
      }
   
      var args = Array.prototype.slice.call(arguments, 2);
      var foundCaller = false;
      for (var ctor = me.constructor;
      ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) {
        if (ctor.prototype[opt_methodName] === caller) {
          foundCaller = true;
        } else if (foundCaller) {
          return ctor.prototype[opt_methodName].apply(me, args);
        }
      }
   
      // If we did not find the caller in the prototype chain,
      // then one of two things happened:
      // 1) The caller is an instance method.
      // 2) This method was not called by the right caller.
      if (me[opt_methodName] === caller) {
        return me.constructor.prototype[opt_methodName].apply(me, args);
      } else {
        throw Error('goog.base called from a method of one name ' + 'to a method of a different name');
      }
    };
   
    var fnTest = /xyz/.test(function() {
      xyz;
    }) ? /\b_super\b/ : /.*/;
   
    Class = function() {};
    Class.extend = function extend(prop) {
      var _super = this.prototype;
   
      function Child() {
        if (this.init) this.init.apply(this, arguments);
      }
      Child.prototype = Object.create(this.prototype);
      Child.prototype.constructor = Child;
      Child.extend = extend;
   
      for (var name in prop) {
        // Check if we're overwriting an existing function
        if (typeof prop[name] == "function" && typeof _super[name] == "function" && /\b_super\b/.test(prop[name])) {
          Child.prototype[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]);
        } else {
          Child.prototype[name] = prop[name];
        }
      }
      return Child;
    }
   
   
    ClassJR = function() {};
   
    // Create a new Class that inherits from this class
    ClassJR.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.prototype.constructor = Class;
   
      // And make this class extendable
      Class.extend = arguments.callee;
   
      return Class;
    };
   
    ClassSG = function() {};
   
   
   
    // Create a new Class that inherits from this class
    ClassSG.extend = function(prop) {
   
      var _super = this.prototype;
   
   
   
      // Instantiate a base class (but only create the instance,
      // don't run the init constructor)
      var Surrogate = function() {
          this.constructor = {};
          };
   
      Surrogate.prototype = this.prototype;
   
      prototype = new Surrogate;
   
   
   
      // 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 (this.init)
   
        this.init.apply(this, arguments);
   
      }
   
   
   
      // Populate our constructed prototype object
      Class.prototype = prototype;
   
   
   
      // Enforce the constructor to be what we expect
      Class.prototype.constructor = Class;
   
   
   
      // And make this class extendable
      Class.extend = arguments.callee;
   
   
   
      return Class;
   
    };
   
    BBClass = function( ctor ) {
   
            ctor.prototype = Object.create({});
            ctor.extend = function( parent ) {
                function ChildBB() { parent.prototype.constructor(); ctor.call(this); };
                ChildBB.prototype = (typeof Object.create !== 'function') ?
                    (function (parent) { function F() {}; F.prototype = parent.prototype; return new F(); })( parent )
                    : Object.create(parent.prototype);
                ChildBB.prototype.constructor =  ChildBB;
                return ChildBB;
            }
            ctor.prototype.constructor = ctor;
            return ctor;
        };
   
    var googA = function() {
        this.a = 'a';
        this.b = 'b';
        this.c = this.a + this.b;
        };
    googA.prototype.foo = function() {
      return "foo"
    };
    googA.prototype.hello = function() {
      return "hello";
    };
    googA.prototype.foobar = function() {
      return "foobar";
    };
    googA.prototype.foobar2 = function() {
      return "foobar2";
    };
   
    var googB = function() {
        googA.call(this);
        };
    goog.inherits(googB, googA);
    googB.prototype.foo = function() {
      return googA.prototype.foo.call(this);
    };
    googB.prototype.bar = function() {
      return "bar";
    };
    googB.prototype.foobar = function() {
      return googB.superClass_.foobar.call(this);
    };
    googB.prototype.foobar2 = function() {
      return googA.prototype.foobar2.call(this);
    };
   
    var extendA = Class.extend({
      init: function() {
        //this is constructor
        this.a = 'a';
        this.b = 'b';
        this.c = this.a + this.b;
      },
      foo: function() {
        return "foo";
      },
      hello: function() {
        return "hello";
      },
      foobar: function() {
        return "foobar";
      }
    });
   
    var extendB = extendA.extend({
      init: function() {
        this._super();
      },
      foo: function() {
        this._super();
      },
      bar: function() {
        return "bar";
      },
      foobar: function() {
        return extendA.prototype.foobar.call(this)
      }
    });
   
    var extendJRA = ClassJR.extend({
      init: function() {
        //this is constructor
        this.a = 'a';
        this.b = 'b';
        this.c = this.a + this.b;
      },
      foo: function() {
        return "foo";
      },
      hello: function() {
        return "hello";
      },
      foobar: function() {
        return "foobar";
      }
    });
   
    var extendJRB = extendJRA.extend({
      init: function() {
        this._super();
      },
      foo: function() {
        this._super();
      },
      bar: function() {
        return "bar";
      },
      foobar: function() {
        return extendJRA.prototype.foobar.call(this)
      }
    });
   
    var extendSGA = ClassSG.extend({
      init: function() {
        //this is constructor
        this.a = 'a';
        this.b = 'b';
        this.c = this.a + this.b;
      },
      foo: function() {
        return "foo";
      },
      hello: function() {
        return "hello";
      },
      foobar: function() {
        return "foobar";
      }
    });
   
    var extendSGB = extendSGA.extend({
      init: function() {
        this._super();
      },
      foo: function() {
        this._super();
      },
      bar: function() {
        return "bar";
      },
      foobar: function() {
        return extendSGA.prototype.foobar.call(this)
      }
    });
   
    var bbClassA = BBClass(function() {
      //this is constructor
      this.a = 'a';
      this.b = 'b';
      this.c = this.a + this.b;
    });
    bbClassA.prototype = {
      foo: function() {
        return "foo";
      },
      hello: function() {
        return "hello";
      },
      foobar: function() {
        return "foobar";
      }
    };
   
    var bbClassB = BBClass(function() {}).extend(bbClassA);
    bbClassB.prototype = {
      foo: function() {
        this._super();
      },
      bar: function() {
        return "bar";
      },
      foobar: function() {
        return extendSGA.prototype.foobar.call(this)
      }
    };
};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
1.1 goog.inherit define classes
var googA = function() {
    this.a = 'a';
    this.b = 'b';
    this.c = this.a + this.b;
    };
googA.prototype.foo = function() {
  return "foo"
};
googA.prototype.hello = function() {
  return "hello";
};
googA.prototype.foobar = function() {
  return "foobar";
};
googA.prototype.foobar2 = function() {
  return "foobar2";
};

var googB = function() {
    googA.call(this);
    };
goog.inherits(googB, googA);
googB.prototype.foo = function() {
  return googA.prototype.foo.call(this);
};
googB.prototype.bar = function() {
  return "bar";
};
googB.prototype.foobar = function() {
  return googB.superClass_.foobar.call(this);
};
googB.prototype.foobar2 = function() {
  return googA.prototype.foobar2.call(this);
};
pending…
1.2 Extend define classes
var extendA = Class.extend({
  init: function() {
    //this is constructor
    this.a = 'a';
    this.b = 'b';
    this.c = this.a + this.b;
  },
  foo: function() {
    return "foo";
  },
  hello: function() {
    return "hello";
  },
  foobar: function() {
    return "foobar";
  }
});

var extendB = extendA.extend({
  init: function() {
    this._super();
  },
  foo: function() {
    this._super();
  },
  bar: function() {
    return "bar";
  },
  foobar: function() {
    return extendA.prototype.foobar.call(this)
  }
});
pending…
1.3 John Resig Extend define classes
var extendJRA = ClassJR.extend({
  init: function() {
    //this is constructor
    this.a = 'a';
    this.b = 'b';
    this.c = this.a + this.b;
  },
  foo: function() {
    return "foo";
  },
  hello: function() {
    return "hello";
  },
  foobar: function() {
    return "foobar";
  }
});

var extendJRB = extendJRA.extend({
  init: function() {
    this._super();
  },
  foo: function() {
    this._super();
  },
  bar: function() {
    return "bar";
  },
  foobar: function() {
    return extendJRA.prototype.foobar.call(this)
  }
});
pending…
1.4 Surrogate Extend define
var extendSGA = ClassSG.extend({
  init: function() {
    //this is constructor
    this.a = 'a';
    this.b = 'b';
    this.c = this.a + this.b;
  },
  foo: function() {
    return "foo";
  },
  hello: function() {
    return "hello";
  },
  foobar: function() {
    return "foobar";
  }
});

var extendSGB = extendSGA.extend({
  init: function() {
    this._super();
  },
  foo: function() {
    this._super();
  },
  bar: function() {
    return "bar";
  },
  foobar: function() {
    return extendSGA.prototype.foobar.call(this)
  }
});
pending…
2.1 Goog inherit object creation
var GA = new googA();
var GB = new googB();
pending…
2.2 Extend object creation
var EA = new extendA();
var EB = new extendB();
pending…
2.3 JR Extend object creation
var EJRA = new extendJRA();
var EJRB = new extendJRB();
pending…
2.4 Surrogate object creation
var ESGA = new extendSGA();
var ESGB = new extendSGB();
pending…
1.5 bennybennet define class
var bbClassA = BBClass(function() {
  //this is constructor
  this.a = 'a';
  this.b = 'b';
  this.c = this.a + this.b;
});
bbClassA.prototype = {
  foo: function() {
    return "foo";
  },
  hello: function() {
    return "hello";
  },
  foobar: function() {
    return "foobar";
  }
};

var bbClassB = BBClass(function() {}).extend(bbClassA);
bbClassB.prototype = {
  foo: function() {
    this._super();
  },
  bar: function() {
    return "bar";
  },
  foobar: function() {
    return extendSGA.prototype.foobar.call(this)
  }
};
pending…
2.5 bennybennet object creation
var BBA = new bbClassA();
var BBB = new bbClassB();
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment