refactor(BaseClass): reduces amount of created objects/functions
Utilizes prototype & Symbol instead of closure
MODIFY
www/BaseClass.js
www/BaseClass.js
+var VARS_FIELD = typeof Symbol === 'undefined' ? '__vars' + Date.now() : Symbol.for('vars');
+var SUBSCRIPTIONS_FIELD = typeof Symbol === 'undefined' ? '__subs' + Date.now() : Symbol.for('subscriptions');
-var _globalListeners = {};
+function BaseClass() {
+ this[VARS_FIELD] = {};
+ this[SUBSCRIPTIONS_FIELD] = {};
+ this.errorHandler = this.errorHandler.bind(this);
-function globalEventListener(e) {
- var eventName = e.type;
- if (!_globalListeners[eventName]) {
- return;
+ Object.defineProperty(this, 'hashCode', { value: Math.floor(Date.now() * Math.random()) })
}
- var hashCode = e.myself.hashCode;
- if (!hashCode || !_globalListeners[eventName][hashCode]) {
- return;
- }
- var callbacks = _globalListeners[eventName][hashCode];
- for (var i = 0; i < callbacks.length; i++) {
- callbacks[i].listener(e);
- }
-}
+BaseClass.prototype = {
+ empty: function() {
+ var vars = this[VARS_FIELD];
-var BaseClass = function() {
- var self = this;
- var _vars = {};
+ Object.keys(vars).forEach(function(name) {
+ vars[name] = null;
+ });
+ },
- var hashCode = Math.floor(Date.now() * Math.random());
+ get: function(key) {
+ return this[VARS_FIELD].hasOwnProperty(key) ? this[VARS_FIELD][key] : null;
+ },
- Object.defineProperty(self, "hashCode", {
- value: hashCode,
- writable: false
- });
- self.empty = function() {
- for (var key in Object.keys(_vars)) {
- _vars[key] = null;
- delete _vars[key];
- }
- };
+ set: function(key, value, noNotify) {
+ var prev = this.get(key);
+
+ this[VARS_FIELD][key] = value;
- self.get = function(key) {
- return key in _vars ? _vars[key] : null;
- };
- self.set = function(key, value, noNotify) {
- var prev = _vars[key];
- _vars[key] = value;
if (!noNotify && prev !== value) {
- self.trigger(key + "_changed", prev, value);
+ this.trigger(key + '_changed', prev, value);
}
- };
- self.bindTo = function(key, target, targetKey, noNotify) {
- targetKey = targetKey === undefined || targetKey === null ? key : targetKey;
- self.on(key + "_changed", function(prevValue, newValue) {
- target.set(targetKey, newValue, noNotify === true);
+
+ return this;
+ },
+
+ bindTo: function(key, target, targetKey, noNotify) {
+ var targetKey = targetKey || key;
+
+ this.on(key + '_changed', function(oldValue, value) {
+ target.set(targetKey, value, noNotify);
});
- };
+ },
- self.trigger = function(eventName) {
- if (!eventName || !_globalListeners[eventName] || !_globalListeners[eventName][hashCode]) {
- return;
+ trigger: function(eventName) {
+ if (!eventName || !this[SUBSCRIPTIONS_FIELD][eventName]) {
+ return this;
}
- var args = [];
- for (var i = 1; i < arguments.length; i++) {
- args.push(arguments[i]);
- }
- var event = document.createEvent('Event');
- event.initEvent(eventName, false, false);
- event.mydata = args;
- event.myself = self;
-
- var callbacks = _globalListeners[eventName][hashCode];
- for (var i = 0; i < callbacks.length; i++) {
- callbacks[i].listener(event);
+ var listeners = this[SUBSCRIPTIONS_FIELD][eventName];
+ var i = listeners.length;
+ var args = Array.prototype.slice.call(arguments, 1);
+
+ while (i--) {
+ listeners[i].apply(this, args);
}
+
+ return this;
+ },
+
+ on: function(eventName, listener) {
+ this[SUBSCRIPTIONS_FIELD][eventName] = this[SUBSCRIPTIONS_FIELD][eventName] || [];
+ var topic = this[SUBSCRIPTIONS_FIELD][eventName];
+ var index = topic.push(listener);
+ var self = this;
+
+ return function() {
+ self.off(eventName, listener);
};
- self.on = function(eventName, callback) {
- if (!eventName || !callback || typeof callback !== "function") {
- return;
- }
+ },
- _globalListeners[eventName] = _globalListeners[eventName] || {};
+ off: function(eventName, listener) {
+ if (!eventName && !listener) {
+ this[SUBSCRIPTIONS_FIELD] = {};
+ } else if (eventName && !listener) {
+ this[SUBSCRIPTIONS_FIELD][eventName] = null;
+ } else if (this[SUBSCRIPTIONS_FIELD][eventName]) {
+ var index = this[SUBSCRIPTIONS_FIELD][eventName].indexOf(listener);
- var listener = function(e) {
- if (!e.myself || e.myself !== self) {
- return;
+ if (index !== -1) {
+ this[SUBSCRIPTIONS_FIELD][eventName].splice(index, 1);
}
- var evt = Object.create(e);
- var mydata = evt.mydata;
- delete evt.mydata;
- delete evt.myself;
- self.event = evt;
- callback.apply(self, mydata);
- };
- if (!(hashCode in _globalListeners[eventName])) {
- document.addEventListener(eventName, globalEventListener, false);
- _globalListeners[eventName][hashCode] = [];
}
- _globalListeners[eventName][hashCode].push({
- 'callback': callback,
- 'listener': listener
+
+ return this;
+ },
+
+ one: function(eventName, listener) {
+ var unlisten = this.on(eventName, function() {
+ unlisten();
+ listener.apply(this, arguments);
});
- };
- self.addEventListener = self.on;
-
- self.off = function(eventName, callback) {
- var i, j, callbacks;
- if (typeof eventName === "string") {
- if (eventName in _globalListeners) {
-
- if (typeof callback === "function") {
- callbacks = _globalListeners[eventName][hashCode] ||[];
- for (i = 0; i < callbacks.length; i++) {
- if (callbacks[i].callback === callback) {
- callbacks.splice(i, 1);
- break;
- }
- }
- if (callbacks.length === 0) {
- delete _globalListeners[eventName][hashCode];
- }
- } else {
- delete _globalListeners[eventName][hashCode];
- }
- if (Object.keys(_globalListeners[eventName]) === 0) {
- document.removeEventListener(eventName, globalEventListener);
- }
- }
- } else {
- //Remove all event listeners
- var eventNames = Object.keys(_globalListeners);
- for (j = 0; j < eventNames.length; j++) {
- eventName = eventNames[j];
- delete _globalListeners[eventName][hashCode];
- if (Object.keys(_globalListeners[eventName]) === 0) {
- document.removeEventListener(eventName, globalEventListener);
- }
- }
- }
- };
- self.removeEventListener = self.off;
+ return this;
+ },
+ destroy: function() {
+ this.off();
+ this.empty();
+ },
- self.one = function(eventName, callback) {
+ errorHandler: function(error) {
+ if (error) {
+ console.log(error);
+ this.trigger('error', error instanceof Error ? error : createError(error));
+ }
- var listener = function(e) {
- if (!e.myself || e.myself !== self) {
- return;
+ return false;
}
- var evt = Object.create(e);
- var mydata = evt.mydata;
- delete evt.mydata;
- delete evt.myself;
- self.event = evt;
- callback.apply(self, mydata);
- self.off(eventName, callback);
};
- _globalListeners[eventName] = _globalListeners[eventName] || {};
+BaseClass.prototype.addEventListener = BaseClass.prototype.on;
+BaseClass.prototype.addEventListenerOnce = BaseClass.prototype.one;
+BaseClass.prototype.removeEventListener = BaseClass.prototype.off;
- if (!(hashCode in _globalListeners[eventName])) {
- document.addEventListener(eventName, globalEventListener, false);
- _globalListeners[eventName][hashCode] = [];
- }
- _globalListeners[eventName][hashCode].push({
- 'callback': callback,
- 'listener': listener
+function createError(message, methodName, args) {
+ var error = new Error(methodName ? [
+ 'Got error with message: "', message, '" ',
+ 'after calling "', methodName, '"'
+ ].join('') : message);
+
+ Object.defineProperties(error, {
+ methodName: { value: methodName },
+ args: { value: args }
});
- };
- self.addEventListenerOnce = self.one;
- self.errorHandler = function(msg) {
- if (msg) {
- console.log(msg);
- self.trigger('error', msg);
+ return error;
}
- return false;
- };
-
- return self;
-};
-
module.exports = BaseClass;

refactor(BaseClass): reduces amount of created objects/functions

Utilizes prototype & Symbol instead of closure