goog.inherit Vs. simple inherit

JavaScript performance comparison

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;
   
    };
   
    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)
      }
    });
};
</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…

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