Skip to content

Commit c2bce43

Browse files
author
pandamicro
committed
Improve Class construction performance
1 parent 56710d4 commit c2bce43

File tree

2 files changed

+167
-146
lines changed

2 files changed

+167
-146
lines changed

CCBoot.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2238,21 +2238,23 @@ cc.game = /** @lends cc.game# */{
22382238
* @constant
22392239
* @type {Object}
22402240
*
2241-
* @prop {String} engineDir - In debug mode, if you use the whole engine to develop your game, you should specify its relative path with "engineDir".
2242-
* @prop {String} modules - Defines which modules you will need in your game, it's useful only on web
2243-
* @prop {String} debugMode - Debug mode, see DEBUG_MODE_XXX constant definitions.
2244-
* @prop {String} showFPS - Left bottom corner fps information will show when "showFPS" equals true, otherwise it will be hide.
2245-
* @prop {String} frameRate - Sets the wanted frame rate for your game, but the real fps depends on your game implementation and the running environment.
2246-
* @prop {String} id - Sets the id of your canvas element on the web page, it's useful only on web.
2247-
* @prop {String} renderMode - Sets the renderer type, only useful on web, 0: Automatic, 1: Canvas, 2: WebGL
2248-
* @prop {String} jsList - Sets the list of js files in your game.
2241+
* @prop {String} engineDir - In debug mode, if you use the whole engine to develop your game, you should specify its relative path with "engineDir".
2242+
* @prop {String} modules - Defines which modules you will need in your game, it's useful only on web
2243+
* @prop {String} debugMode - Debug mode, see DEBUG_MODE_XXX constant definitions.
2244+
* @prop {String} exposeClassName - Expose class name to chrome debug tools
2245+
* @prop {String} showFPS - Left bottom corner fps information will show when "showFPS" equals true, otherwise it will be hide.
2246+
* @prop {String} frameRate - Sets the wanted frame rate for your game, but the real fps depends on your game implementation and the running environment.
2247+
* @prop {String} id - Sets the id of your canvas element on the web page, it's useful only on web.
2248+
* @prop {String} renderMode - Sets the renderer type, only useful on web, 0: Automatic, 1: Canvas, 2: WebGL
2249+
* @prop {String} jsList - Sets the list of js files in your game.
22492250
*/
22502251
CONFIG_KEY: {
22512252
width: "width",
22522253
height: "height",
22532254
engineDir: "engineDir",
22542255
modules: "modules",
22552256
debugMode: "debugMode",
2257+
exposeClassName: "exposeClassName",
22562258
showFPS: "showFPS",
22572259
frameRate: "frameRate",
22582260
id: "id",

cocos2d/core/platform/CCClass.js

Lines changed: 157 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,116 @@
2626

2727
var cc = cc || {};
2828

29+
/**
30+
* Common getter setter configuration function
31+
* @function
32+
* @param {Object} proto A class prototype or an object to config<br/>
33+
* @param {String} prop Property name
34+
* @param {function} getter Getter function for the property
35+
* @param {function} setter Setter function for the property
36+
* @param {String} getterName Name of getter function for the property
37+
* @param {String} setterName Name of setter function for the property
38+
*/
39+
cc.defineGetterSetter = function (proto, prop, getter, setter, getterName, setterName) {
40+
if (proto.__defineGetter__) {
41+
getter && proto.__defineGetter__(prop, getter);
42+
setter && proto.__defineSetter__(prop, setter);
43+
} else if (Object.defineProperty) {
44+
var desc = {enumerable: false, configurable: true};
45+
getter && (desc.get = getter);
46+
setter && (desc.set = setter);
47+
Object.defineProperty(proto, prop, desc);
48+
} else {
49+
throw new Error("browser does not support getters");
50+
}
51+
52+
if (!getterName && !setterName) {
53+
// Lookup getter/setter function
54+
var hasGetter = (getter != null), hasSetter = (setter != undefined), props = Object.getOwnPropertyNames(proto);
55+
for (var i = 0; i < props.length; i++) {
56+
var name = props[i];
57+
58+
if ((proto.__lookupGetter__ ? proto.__lookupGetter__(name)
59+
: Object.getOwnPropertyDescriptor(proto, name))
60+
|| typeof proto[name] !== "function")
61+
continue;
62+
63+
var func = proto[name];
64+
if (hasGetter && func === getter) {
65+
getterName = name;
66+
if (!hasSetter || setterName) break;
67+
}
68+
if (hasSetter && func === setter) {
69+
setterName = name;
70+
if (!hasGetter || getterName) break;
71+
}
72+
}
73+
}
74+
75+
// Found getter/setter
76+
var ctor = proto.constructor;
77+
if (getterName) {
78+
if (!ctor.__getters__) {
79+
ctor.__getters__ = {};
80+
}
81+
ctor.__getters__[getterName] = prop;
82+
}
83+
if (setterName) {
84+
if (!ctor.__setters__) {
85+
ctor.__setters__ = {};
86+
}
87+
ctor.__setters__[setterName] = prop;
88+
}
89+
};
90+
91+
/**
92+
* Create a new object and copy all properties in an exist object to the new object
93+
* @function
94+
* @param {object|Array} obj The source object
95+
* @return {Array|object} The created object
96+
*/
97+
cc.clone = function (obj) {
98+
// Cloning is better if the new object is having the same prototype chain
99+
// as the copied obj (or otherwise, the cloned object is certainly going to
100+
// have a different hidden class). Play with C1/C2 of the
101+
// PerformanceVirtualMachineTests suite to see how this makes an impact
102+
// under extreme conditions.
103+
//
104+
// Object.create(Object.getPrototypeOf(obj)) doesn't work well because the
105+
// prototype lacks a link to the constructor (Carakan, V8) so the new
106+
// object wouldn't have the hidden class that's associated with the
107+
// constructor (also, for whatever reasons, utilizing
108+
// Object.create(Object.getPrototypeOf(obj)) + Object.defineProperty is even
109+
// slower than the original in V8). Therefore, we call the constructor, but
110+
// there is a big caveat - it is possible that the this.init() in the
111+
// constructor would throw with no argument. It is also possible that a
112+
// derived class forgets to set "constructor" on the prototype. We ignore
113+
// these possibities for and the ultimate solution is a standardized
114+
// Object.clone(<object>).
115+
var newObj = (obj.constructor) ? new obj.constructor : {};
116+
117+
// Assuming that the constuctor above initialized all properies on obj, the
118+
// following keyed assignments won't turn newObj into dictionary mode
119+
// because they're not *appending new properties* but *assigning existing
120+
// ones* (note that appending indexed properties is another story). See
121+
// CCClass.js for a link to the devils when the assumption fails.
122+
for (var key in obj) {
123+
var copy = obj[key];
124+
// Beware that typeof null == "object" !
125+
if (((typeof copy) === "object") && copy && !(copy instanceof cc.Node) && !(copy instanceof HTMLElement)) {
126+
newObj[key] = cc.clone(copy);
127+
} else {
128+
newObj[key] = copy;
129+
}
130+
}
131+
return newObj;
132+
};
133+
134+
cc.inject = function (srcPrototype, destPrototype) {
135+
for (var key in srcPrototype)
136+
destPrototype[key] = srcPrototype[key];
137+
};
138+
29139
/**
30140
* @namespace
31141
* @name ClassManager
@@ -71,43 +181,63 @@ var ClassManager = {
71181
// don't run the init constructor)
72182
var prototype = Object.create(_super);
73183

74-
var classId = ClassManager.getNewID();
75-
ClassManager[classId] = _super;
76184
// Copy the properties over onto the new prototype. We make function
77185
// properties non-eumerable as this makes typeof === 'function' check
78186
// unnecessary in the for...in loop used 1) for generating Class()
79187
// 2) for cc.clone and perhaps more. It is also required to make
80188
// these function properties cacheable in Carakan.
81-
var desc = { writable: true, enumerable: false, configurable: true };
82-
83-
prototype.__instanceId = null;
84-
85-
// The dummy Class constructor
86-
function Class() {
87-
this.__instanceId = ClassManager.getNewInstanceId();
88-
// All construction is actually done in the init method
89-
if (this.ctor)
90-
this.ctor.apply(this, arguments);
91-
}
189+
var desc = {writable: true, enumerable: false, configurable: true};
190+
191+
// The dummy Class constructor
192+
var Class;
193+
if (cc.game.config[cc.game.CONFIG_KEY.exposeClassName]) {
194+
var constructor = "(function " + (props._className || "Class") + " (arg0, arg1, arg2, arg3, arg4, arg5) {\n";
195+
constructor += " this.__instanceId = ClassManager.getNewInstanceId();\n";
196+
constructor += " if (this.ctor) {\n";
197+
constructor += " switch (arguments.length) {\n";
198+
constructor += " case 0: this.ctor(); break;\n";
199+
constructor += " case 1: this.ctor(arg0); break;\n";
200+
constructor += " case 3: this.ctor(arg0, arg1, arg2); break;\n";
201+
constructor += " case 4: this.ctor(arg0, arg1, arg2, arg3); break;\n";
202+
constructor += " case 5: this.ctor(arg0, arg1, arg2, arg3, arg4); break;\n";
203+
constructor += " default: this.ctor.apply(this, arguments);\n";
204+
constructor += " }\n";
205+
constructor += " }\n";
206+
constructor += "})";
207+
Class = eval(constructor);
208+
}
209+
else {
210+
Class = function (arg0, arg1, arg2, arg3, arg4) {
211+
this.__instanceId = ClassManager.getNewInstanceId();
212+
if (this.ctor) {
213+
switch (arguments.length) {
214+
case 0: this.ctor(); break;
215+
case 1: this.ctor(arg0); break;
216+
case 2: this.ctor(arg0, arg1); break;
217+
case 3: this.ctor(arg0, arg1, arg2); break;
218+
case 4: this.ctor(arg0, arg1, arg2, arg3); break;
219+
case 5: this.ctor(arg0, arg1, arg2, arg3, arg4); break;
220+
default: this.ctor.apply(this, arguments);
221+
}
222+
}
223+
};
224+
}
92225

93-
Class.id = classId;
94-
// desc = { writable: true, enumerable: false, configurable: true,
95-
// value: XXX }; Again, we make this non-enumerable.
96-
desc.value = classId;
97-
Object.defineProperty(prototype, '__pid', desc);
226+
desc.value = ClassManager.getNewID();
227+
Object.defineProperty(prototype, '__pid', desc);
98228

99-
// Populate our constructed prototype object
100-
Class.prototype = prototype;
229+
// Populate our constructed prototype object
230+
Class.prototype = prototype;
101231

102-
// Enforce the constructor to be what we expect
103-
desc.value = Class;
104-
Object.defineProperty(Class.prototype, 'constructor', desc);
232+
// Enforce the constructor to be what we expect
233+
desc.value = Class;
234+
Object.defineProperty(prototype, 'constructor', desc);
105235

106-
// Copy getter/setter
107-
this.__getters__ && (Class.__getters__ = cc.clone(this.__getters__));
108-
this.__setters__ && (Class.__setters__ = cc.clone(this.__setters__));
236+
// Copy getter/setter
237+
this.__getters__ && (Class.__getters__ = cc.clone(this.__getters__));
238+
this.__setters__ && (Class.__setters__ = cc.clone(this.__setters__));
109239

110-
for(var idx = 0, li = arguments.length; idx < li; ++idx) {
240+
for (var idx = 0, li = arguments.length; idx < li; ++idx) {
111241
var prop = arguments[idx];
112242
for (var name in prop) {
113243
var isFunc = (typeof prop[name] === "function");
@@ -179,114 +309,3 @@ var ClassManager = {
179309
};
180310
})();
181311

182-
/**
183-
* Common getter setter configuration function
184-
* @function
185-
* @param {Object} proto A class prototype or an object to config<br/>
186-
* @param {String} prop Property name
187-
* @param {function} getter Getter function for the property
188-
* @param {function} setter Setter function for the property
189-
* @param {String} getterName Name of getter function for the property
190-
* @param {String} setterName Name of setter function for the property
191-
*/
192-
cc.defineGetterSetter = function (proto, prop, getter, setter, getterName, setterName){
193-
if (proto.__defineGetter__) {
194-
getter && proto.__defineGetter__(prop, getter);
195-
setter && proto.__defineSetter__(prop, setter);
196-
} else if (Object.defineProperty) {
197-
var desc = { enumerable: false, configurable: true };
198-
getter && (desc.get = getter);
199-
setter && (desc.set = setter);
200-
Object.defineProperty(proto, prop, desc);
201-
} else {
202-
throw new Error("browser does not support getters");
203-
}
204-
205-
if(!getterName && !setterName) {
206-
// Lookup getter/setter function
207-
var hasGetter = (getter != null), hasSetter = (setter != undefined), props = Object.getOwnPropertyNames(proto);
208-
for (var i = 0; i < props.length; i++) {
209-
var name = props[i];
210-
211-
if( (proto.__lookupGetter__ ? proto.__lookupGetter__(name)
212-
: Object.getOwnPropertyDescriptor(proto, name))
213-
|| typeof proto[name] !== "function" )
214-
continue;
215-
216-
var func = proto[name];
217-
if (hasGetter && func === getter) {
218-
getterName = name;
219-
if(!hasSetter || setterName) break;
220-
}
221-
if (hasSetter && func === setter) {
222-
setterName = name;
223-
if(!hasGetter || getterName) break;
224-
}
225-
}
226-
}
227-
228-
// Found getter/setter
229-
var ctor = proto.constructor;
230-
if (getterName) {
231-
if (!ctor.__getters__) {
232-
ctor.__getters__ = {};
233-
}
234-
ctor.__getters__[getterName] = prop;
235-
}
236-
if (setterName) {
237-
if (!ctor.__setters__) {
238-
ctor.__setters__ = {};
239-
}
240-
ctor.__setters__[setterName] = prop;
241-
}
242-
};
243-
244-
/**
245-
* Create a new object and copy all properties in an exist object to the new object
246-
* @function
247-
* @param {object|Array} obj The source object
248-
* @return {Array|object} The created object
249-
*/
250-
cc.clone = function (obj) {
251-
// Cloning is better if the new object is having the same prototype chain
252-
// as the copied obj (or otherwise, the cloned object is certainly going to
253-
// have a different hidden class). Play with C1/C2 of the
254-
// PerformanceVirtualMachineTests suite to see how this makes an impact
255-
// under extreme conditions.
256-
//
257-
// Object.create(Object.getPrototypeOf(obj)) doesn't work well because the
258-
// prototype lacks a link to the constructor (Carakan, V8) so the new
259-
// object wouldn't have the hidden class that's associated with the
260-
// constructor (also, for whatever reasons, utilizing
261-
// Object.create(Object.getPrototypeOf(obj)) + Object.defineProperty is even
262-
// slower than the original in V8). Therefore, we call the constructor, but
263-
// there is a big caveat - it is possible that the this.init() in the
264-
// constructor would throw with no argument. It is also possible that a
265-
// derived class forgets to set "constructor" on the prototype. We ignore
266-
// these possibities for and the ultimate solution is a standardized
267-
// Object.clone(<object>).
268-
var newObj = (obj.constructor) ? new obj.constructor : {};
269-
270-
// Assuming that the constuctor above initialized all properies on obj, the
271-
// following keyed assignments won't turn newObj into dictionary mode
272-
// because they're not *appending new properties* but *assigning existing
273-
// ones* (note that appending indexed properties is another story). See
274-
// CCClass.js for a link to the devils when the assumption fails.
275-
for (var key in obj) {
276-
var copy = obj[key];
277-
// Beware that typeof null == "object" !
278-
if (((typeof copy) === "object") && copy &&
279-
!(copy instanceof cc.Node) && !(copy instanceof HTMLElement)) {
280-
newObj[key] = cc.clone(copy);
281-
} else {
282-
newObj[key] = copy;
283-
}
284-
}
285-
return newObj;
286-
};
287-
288-
cc.inject = function(srcPrototype, destPrototype){
289-
for(var key in srcPrototype)
290-
destPrototype[key] = srcPrototype[key];
291-
};
292-

0 commit comments

Comments
 (0)