Javascript prototype vs $.extend
Related Questions, but no mention of my specific 'bug' -
prototypes vs extending objects
Advantages of using prototype, vs defining methods straight in the constructor?
I have a performance issue I don't understand. I was creating a class with the ability to be both callable and have methods (similar to jQuery).
I allowed an 'opt out' of the callable part, assuming that it would be slower to extend a function with methods than to use a prototype. This assumption is correct in the most basic case.
However, when I actually timed my code and looked at the memory usage, I was surprised to discover the prototype approach being slower AND taking up more memory.
After looking into it, it became clear that it was only less efficient when calling the 'init' method that binds a DOM element to 'this'. If you comment out the call to init (line 49 in the fiddle), the prototype method is much faster, as I would expect.
jsfiddle here - http://jsfiddle.net/pnsfogem/1/
Edit: jsPerf laying out all of Bergi's suggestions below - http://jsperf.com/different-types-of-constructors-vs-extend
After running these perfs, it does look like it's only in the version of Chrome I'm running..
and all of the code needed to replicate.
var methods = {
select: function(selector){
var $this = this;
var scopedMethods = {
newMethod1: function( newParam ){
$this.newMethod1( selector, newParam );
},
newMethod2: function( newParam ){
$this.newMethod2( selector, newParam );
}
};
return scopedMethods;
},
init: function(){
// console.log(this); // Looks correct for all 2000 elements
this.$el = $( "<div>" ).appendTo( $("body") );
},
newMethod1: function( selector, newParam ){
// do stuff
},
newMethod2: function( selector, newParam ){
// do stuff
}
};
function getConstructor( noQuery ){
var returnedInstance;
if ( noQuery ){
var constructor = function(){};
constructor.prototype = methods;
returnedInstance = new constructor();
// Usage:
// var s = getConstructor( 'justPrototype' );
// s.newMethod1( '#whatever', 'stuff' );
}
else {
returnedInstance = function(){
return returnedInstance.select.apply( returnedInstance, arguments );
};
$.extend( returnedInstance, methods );
// Can use either of these ways:
// var s = getConstructor();
// s.newMethod1( '#whatever', 'stuff' );
// s( '#whatever' ).newMethod1( 'stuff' );
}
returnedInstance.init();
return returnedInstance;
}
// When calling init
// This is both faster and more memory efficient. Why?
var arr1 = [];
console.time('normal');
for (var i=0; i<1000;i++){
arr1.push( getConstructor() );
}
console.timeEnd('normal');
// arr1[0].$el != arr1[1].$el
var arr2 = [];
console.time('prototype');
for (var i=0; i<1000;i++){
arr2.push( getConstructor( 'justPrototype' ) );
}
console.timeEnd('prototype');
// arr2[0].$el != arr2[1].$el
so, my question is
Why is this the case? Am I doing something wrong?
Once they're instantiated, I would expect them to handle adding new properties to be relatively the same performance wise, but it seems to slow down the prototype approach by 10x.
( Obviously, there's other benefits / trade-offs to allowing access to a prototype vs having a function with no prototype, but I think they're outside the scope of this question )
I was creating a class with the ability to be both callable and have methods (similar to jQuery).
Not really. jQuery collection instances are not callable.
Am I doing something wrong?
Your "prototype" branch looks a bit odd. On every invocation, you're creating a new constructor
function. Making that global should help a little bit. Also, the empty constructor with extra init
method is a very unusual pattern. You might want to call the init
method from the constructor (see below), or even directly use
var constructor = methods.init;
constructor.prototype = methods;
function get() {
…
return new constructor();
}
If you used the pattern simply to create an object with methods
as a prototype, you rather should use Object.create
:
… returnedInstance = Object.create(methods);
Once they're instantiated, I would expect them to handle adding new properties to be relatively the same performance wise, but it seems to slow down the prototype approach by 10x.
No, properties are optimized a lot. Using unusual patterns can cause dramatic slowdowns. In V8 (Google Chrome's JS engine) for example, properties that are created during a constructor call are optimized, and no further slots are left open. When you craete a new property after the object was constructed, it might need to change its internal structure to a less optimized (but more add-property-friendly), which is rather slow. If this guess if correct, you should be able to see a significant speed-up by using
function constructor() {
this.init();
}
constructor.prototype = methods;
…
return new constructor();
链接地址: http://www.djcxy.com/p/64166.html