Inheritance in JavaScript: How can I call the object parent? - javascript

I'm building a multi-source audio player in JavaScript and my goal is to code several providers classes (for Youtube, Soundcloud, etc.) that would extend a base class, so each provider would have the same methods and properties, but I would be able to customize them for each provider.
Here's a simplified example.
The base class looks like this :
function Player_Provider(slug) {
this.slug = slug;
console.log("Player_Provider init: " + this.slug);
};
Player_Provider.prototype.loadUrl = function(url) {
console.log("provider "+this.slug+" - load URL: " + url);
};
I "extend" this class for providers; for example:
function Player_Provider_Youtube() {
Player_Provider.call(this, 'youtube');
}
Player_Provider_Youtube.prototype = Object.create(Player_Provider.prototype); //inherit methods
Player_Provider_Youtube.prototype.constructor = Player_Provider_Youtube; //fix constructor
Player_Provider_Youtube.prototype.loadUrl = function(url) {
Player_Provider.prototype.loadUrl(url);
}
And then I register it like this:
var providers = [];
providers.youtube = new Player_Provider_Youtube();
providers.youtube.loadUrl("https://www.youtube.com/watch?v=5SIQPfeUTtg");
Which outputs in the console:
Player_Provider init: youtube
provider undefined - load URL: https://www.youtube.com/watch?v=5SIQPfeUTtg
As you can see, the console outputs:
"provider undefined - load URL..."
when I would like it to output:
"provider youtube - load URL..."
The idea here is that in each function of the provider (which would every time override a function from the base class), I would call the "parent" function first, at least to output a console message like here; and eventually to run some code - in the idea of having the most clean code possible.
I'm more comfortable with PHP and it's the first time I'm trying to do that kind of stuff using JavaScript.
How would you do this and why is my variable undefined?

In ES5 and earlier, it's a pain. You use call or apply, and as you've found, it's really verbose.
Player_Provider_Youtube.prototype.loadUrl = function(url) {
Player_Provider.prototype.loadUrl.call(this, url);
// ------------------------------^^^^^^^^^^^
}
I found that painful enough I wrote a library to deal with it (here), but it's obsolete now.
But, here in 2017, you don't do that anymore; instead, you use ES2015's class syntax and transpile (with something like Babel) so it runs on older JavaScript engines:
class Parent {
method() {
console.log("Parent method");
}
}
class Child extends Parent {
method() {
console.log("Child method");
super.method();
}
}
new Child().method();
That handles all the nasty plumbing for you. It's still the same prototypical inheritance + constructor functions that it was before ES2015, it's just much simpler syntax for it. (And enables a couple of things we couldn't do with the old syntax.)

I was just doing a JSFiddle and T.J.Crowder beat me to it!
function Player_Provider(slug) {
this.slug = slug;
console.log("Player_Provider init: " + this.slug);
};
Player_Provider.prototype.loadUrl = function(url) {
console.log("provider "+this.slug+" - load URL: " + url);
};
function Player_Provider_Youtube() {
Player_Provider.call(this, 'youtube');
}
Player_Provider_Youtube.prototype = Object.create(Player_Provider.prototype); //inherit methods
Player_Provider_Youtube.prototype.constructor = Player_Provider_Youtube; //fix constructor
Player_Provider_Youtube.prototype.loadUrl = function(url) {
Player_Provider.prototype.loadUrl.call(this, url);
}
var providers = [];
providers.youtube = new Player_Provider_Youtube();
providers.youtube.loadUrl("https://www.youtube.com/watch?v=5SIQPfeUTtg");
https://jsfiddle.net/4Lmkuwts/

Related

RequireJS managing large modules

Only now I have considered using RequireJS and AMD modules.
So far - all the things have been managed through few global variables and self invoking functions.
Example, for how my module would looke like:
function HugeModule() {
//usage = new HugeModule();
};
HugeModule.prototype.functionX = function() {
//Lets say - around 50 functions for HugeModule prototype
};
HugeModule.SubModule = function() {
//usage = new HugeModule.SubModule();
//And here could be multiple subModules like this
};
HugeModule.SubModule.prototype.functionX = function() {
//Lets say - around 20 functions for HugeModule.SubModule prototype
};
Now I would have written it like this, I would have split it between at least 4 files:
//HugeModule.js
var HugeModule = (function() {
function HugeModule() {
//usage = new HugeModule();
};
return HugeModule;
})();
//HugeModule.somePrototypeFunctions.js
(function() {
HugeModule.prototype.functionX = function() {
//Lets say - around 50 functions for HugeModule prototype
};
})();
//HugeModule.SubModule.js
(function() {
HugeModule.SubModule = function() {
//usage = new HugeModule.SubModule();
//And here could be multiple subModules like this
};
})();
//HugeModule.SubModule.someOtherPrototypeFunctions.js
(function() {
HugeModule.SubModule.prototype.functionX = function() {
//Lets say - around 20 functions for HugeModule.SubModule prototype
};
})();
I would really like to write these modules with AMD modules and RequireJS, I have a basic idea how they should be written, but I am not sure - how would I split them between multiple modules.
I could write it like this:
define([], function() {
function HugeModule() {
//usage = new HugeModule();
};
HugeModule.prototype.functionX = function() {
//Lets say - around 50 functions for HugeModule prototype
};
return HugeModule;
});
but I would like to split it between multiple files. I would prefer not to use build tools that concatenates files.
What I would like is one requirable module - HugeModule and it would resolve all the dependencies for HugeModule.somePrototypeFunctions, and HugeModule.SubModule (and this would resolve dependencie for HugeModule.SubModule.someOtherPrototypeFunctions)
How should I resolve this?
First an important caveat: what you are trying to do does not lend itself well to how ES6 classes work. If you are ever to write ES6 classes or write in a language that has a class syntax similar to ES6 (TypeScript, for instance, has classes that are ES6 + type annotations), you'll run into having to work around the class syntax or run into transpilation problems. Think about refactoring your HugeModule into multiple smaller classes to avoid these problems. (See here for a discussion of the problem in the context of TypeScript.)
If the caveat above is not a concern, you can achieve your goal by organizing your code like the following. I've used this pattern for many years successfully.
HugeModule.js just combines the parts of the class and provide a facade for the rest of the code:
define(["./HugeModuleCore", "./HugeModuleA", "./HugeModuleB"], function (HugeModuleCore) {
return HugeModuleCore;
});
HugeModuleCore.js creates the class and creates some "core" methods on it:
define([], function () {
function HugeModule() {
};
HugeModule.prototype.someCoreFunction = function() {
};
return HugeModule;
});
HugeModuleA.js adds some category of methods to the core:
define(["./HugeModuleCore"], function (HugeModule) {
HugeModule.prototype.someFunction = function() {
};
// You don't really need to return anything here.
});
HugeModuleB.js adds some other category of methods to the core:
define(["./HugeModuleCore"], function (HugeModule) {
HugeModule.prototype.someOtherFunction = function() {
};
// You don't really need to return anything here.
});

What is the best method for loading in dependencies & instantiating object only once in Javascript?

I'd like to explore battle-tested, proven design patterns in Javascript that ensure loading in dependencies only once (if they were already loaded in) & instantiates an object only once as well in the DOM.
Specifically, i have following:
// Block A in the DOM
<script>
//instantiate my golden object as follows
var some_unique_options = {
"option1": "some-val",
"option2": "some-val"
}
$.ajaxSetup({cache:true});
$.getScript('my_golden_script.js', function(){
golden_object.init(some_unique_options);
});
</script>
// Block B in the DOM
<script>
//instantiate my golden object as follows
var another_unique_options ={
"option1": "some-val",
"option2": "some-val"
}
$.ajaxSetup({cache:true});
$.getScript('my_golden_script.js', function(){
golden_object.init(another_unique_options);
});
</script>
Currently, my golden_object implements a singleton as per: http://robdodson.me/javascript-design-patterns-singleton/
Intended behavior here is that, when the above Bock A & B execute top to bottom in the DOM, golden_object.init in Block B would leverage resources defined during the operation of Block A. Business requirement is as such that Block A & B would be identical, and there could be more in the DOM. After several days of researching, I'm still unable to come up with a solid structure. My singleton keeps re-creating itself as new, and only if i call golden_object.init(); in the devtools console, it would use the existing object in the DOM. How can i get this to work the way it's supposed to? what/where am i making a mistake here?
FYI, golden_object.js does carry $.fn.someOtherObj & couple of dependencies via $.getScript() as well as the definition of the singleton (i wanted Singleton obj to be user interface)
Million thanks to your insights & suggestions!
You could create a wrapper around jQuery.getScript that ensures the script is loaded only once, and, in the case of the second and further instances, calls the callback once the first script has loaded
jQuery.getScriptOnce = (function($) {
var urls = {};
return function(url, callback) {
var obj = urls[url];
if (!obj) {
urls[url] = new Promise(function(resolve, reject) {
$.getScript(url, function(script, textStatus, jqXHR) {
resolve({script: script, textStatus: textStatus, jqXHR: jqXHR});
callback(script, textStatus, jqXHR);
});
});
} else {
obj.then(function(val) {
callback(val.script, val.textStatus, val.jqXHR);
});
}
};
})(jQuery);
this has the benefit of only needing to change all occurrences of $.getScript to $.getScriptOnce
Note: I haven't checked to see if jQuery.getScript returns a jQuery promise, if it does, then the code can be a lot simpler
Just tested and it seems getScript DOES return a jquery promise - so you can simply do
jQuery.getScriptOnce = (function($) {
var urls = {};
return function(url) {
return urls[url] = urls[url] || $.getScript(url);
};
})(jQuery);
but you'll change
$.getScript('my_golden_script.js', function(){
golden_object.init(some_unique_options);
});
to
$.getScriptOnce('my_golden_script.js').then(function(){
golden_object.init(some_unique_options);
});

Javascript prototype function override when x

In my case, I'm using the Phaser framework.
So in this example I'm extending the Group class of phaser. Every 'actor' class (Sprite, Group, ...) calls upon the update() prototype every few miliseconds.
My idea was to extend this function only when the application runs on a desktop (so not on a phone).
for example:
var MousePointer = function (game, parent, name) {
Phaser.Group.call(this, game, parent, name);
this.init();
};
MousePointer.prototype = Object.create(Phaser.Group.prototype);
MousePointer.prototype.constructor = MousePointer;
MousePointer.prototype.init = function () {
// ... init
};
MousePointer.prototype.update = function () {
// Do something when on desktop
};
I can't possibly use an if clausule in the update() function to check whether the player is on dekstop/tablet/phone. So is there a way to actually override the prototype on initialisation?
for example (pseudocode):
if(onPhone)
MousePointer.prototype.update = parent.prototype.update;
else
MousePointer.prototype.update = this.update;
Well, you've kind of already written the answer for yourself, haven't you? This code (not inside the init method).
if(onPhone) {
MousePointer.prototype.update = function(){//Phone implementation};
} else {
MousePointer.prototype.update = function(){//Other implementation};
}
I advise against starting off with the "regular" function and then potentially overriding it, since you're just declaring it for nothing.
I think a better way to do this would be to write two different classes that shares the same parent, and then write different update() implementations for them. Then you can just do something like:
if(phone) {
var obj = new PhoneMousePointerObject();
} else {
var obj = new DesktopMousePointerObject();
}
// ... later
obj.update()

How to write flexible and generic javascript module or plugin

I want to write a javascript/jquery plugin so that it is generic enough to be used in any framework such as angularjs, backbonejs, ember etc. I should be generic enough so that it should use directives if it is used with angular and backbone native functionality when it is used with backbone. Is it possible if yes then could someone guide me how?
The most natural way I can think of is just to write it in vanilla JS. That will make it work in every framework without needing to worry about it.
If you want to go ahead with this route though, I'd use a driver-style implementation where you pipe everything to a specific driver for a particular framework. You'd define every method you want for each Driver, then the calls get forwarded on automatically to the correct Driver.
var myPlugin;
(function() {
myPlugin = function(framework) {
var me = {},
framework = framework || 'angular';
me.frameworks = {
angular: new AngularDriver,
backbone: new BackboneDriver,
ember: new EmberDriver
};
// Call a method framework-agnostically
me.fire = function(method, args) {
if (!me.frameworks.hasOwnProperty(framework)) {
console.log('Error: Framework not recognised.');
return;
}
if (!me.frameworks[framework].hasOwnProperty(method)) {
console.log('Error: Method not found in ' + framework + '.');
return;
}
me.frameworks[framework][method].apply(this, args);
}
return me;
}
function AngularDriver() {
var me = {};
me.test = function() {
console.log('Hello from the Angular Driver');
}
return me;
}
function BackboneDriver() {
var me = {};
me.test = function() {
console.log('Hello from the Backbone Driver');
}
return me;
}
function EmberDriver() {
var me = {};
me.test = function(arg) {
console.log('Hello from the ' + arg + ' Ember Driver');
}
return me;
}
})();
var instance = new myPlugin();
instance.fire('test');
instance = new myPlugin('ember');
instance.fire('test', ['best']);
It's entirely possible that there's a slightly cleaner way to implement the myPlugin.fire function, if anyone else can improve that bit so the syntax of instance.fire('test', ['best']) is a bit cleaner, feel free :-)

How should I implement OOP patterns to interactive web applications (with the aide of jQuery)?

Sometimes, using jQuery induces you to abuse its power (at least for me because of its selector matching capability). Event handlers here and there. Utility functions here and everywhere. Code coherence can almost seem nonexistent. I want to alleviate that problem by implementing OOP patterns, but since I have C++ and python background, implementing it in javascript is weirding me out a little bit.
The code below uses OOP patterns, but I'm not entirely sure if my implementations are good practices. The reason I'm doubting my implementations is because of the 3rd comment in my last stackoverflow question. I know it's only one certain detail in my code he commented on, but it also makes me wonder about the other patterns I'm implementing in my code.
I would really appreciate if you could point out the flaws and pitfalls in my patterns and/or if you have any suggestions. Many thanks in advance.
(this code is an simplification of something I'm developing, but the idea is similar)
Live Example
$(function(){
var stream = new Stream();
});
/* Stream Class
------------------------------------------*/
function Stream(){
// Disables multiple Stream objects
if (this.singleton)
return
else
this.__proto__.singleton = true;
this.elements = jQueryMapping(this.selectors) // Converts a map of selectors to a map of jQuery objects
this.initEvents();
}
Stream.prototype.singleton = false;
Stream.prototype.selectors = {
stream : '#stream',
post_content : '#post-content',
add_post: '#add-post',
// ... more action selectors
}
Stream.prototype.initEvents = function(){
this.elements.add_post.click(this, this.addPost);
// ... more action event-listeners
}
Stream.prototype.addPost = function(e){
var self = e.data;
var post_content = self.elements.post_content.val();
if (post_content)
self.elements.stream.append(new Post(post_content));
}
/* Post Class
------------------------------------------*/
function Post(post_content){
this.$element = $('<li>')
.text(post_content)
.append('<button class="delete-post">Delete</button>');
this.elements = jQueryMapping(this.selectors, this.$element);
this.initEvents();
return this.$element;
}
Post.prototype.selectors = {
delete_post: 'button.delete-post',
// ... more action selectors
}
Post.prototype.initEvents = function(){
this.elements.delete_post.click(this.deletePost);
// ... more action event-listeners
}
Post.prototype.deletePost = function(){
$(this).parent().slideUp();
}
/* Utils
------------------------------------------*/
function jQueryMapping(map, context){
// Converts a map of selectors to a map of jQuery objects
var $map = {};
$.each(map, function(key, value){
$map[key] = (context) ? $(value, context) : $(value);
});
return $map;
}
I believe your code is over engineered. I've re factored and it simplified it as can be seen here. If you really want a heavy OOP setup I recommend you use a clientside MVC (Backbone, knockout, etc) construct to do it properly or keep it light instead.
I'll proceed with general feedback on your code.
/* Stream Class
------------------------------------------*/
function Stream(){
// Disables multiple Stream objects
if (this.singleton)
return
else
this.__proto__.singleton = true;
this.elements = jQueryMapping(this.selectors) // Converts a map of selectors to a map of jQuery objects
this.initEvents();
}
There is no reason to use a singleton like this. It's also very bad to use .__proto__
I would recommend pattern like this instead.
var Stream = (function() {
var Stream = function() { ... };
// prototype stuff
var stream = new Stream();
return function() {
return stream;
};
})());
Storing a hash of data like that on the prototype is unneccesary.
Stream.prototype.selectors = {
stream : '#stream',
post_content : '#post-content',
add_post: '#add-post',
// ... more action selectors
}
You can include this as a defaults hash instead.
(function() {
var defaults = {
stream : '#stream',
post_content : '#post-content',
add_post: '#add-post',
// ... more action selectors
}
function Stream() {
...
this.elements = jQueryMapping(defaults);
}
}());
Your utility function could be optimised slightly.
$map[key] = (context) ? $(value, context) : $(value);
This could be rewritten as
$map[key] = $(value, context)
Since if context is undefined you just pass in an undefined paramater which is the same as passing in no parameter.
The title of this reads "for beginners", but I've found this section on design patterns, and this section on design patterns using jQuery useful.

Categories

Resources