I have a variable in a global scope which is assigned an instance of a class like this:
window.someInstance = new MyClass();
At some point later, I need to replace that variable with a new instance, but is it possible/acceptable to do that from within a method of the class itself? For example:
function MyClass () {
this.myClassMethod = function () {
window.someInstance = new MyClass();
};
}
window.someInstance = new MyClass();
window.someInstance.myClassMethod.call();
An odd scenario I know but it works cleanly, I'm just not sure if this creates any memory or referencing issues?
Only if everyone always accessess the instance indirectly via window.somereference. As soon as anyone does var x = window.someinstance then you lose the indirection and your trick would stop working.
You might acheieve a more robust implementation by placing the indirection in a variable of the instance itself instead of in a global variable
function Instance(){
this.impl = ...;
}
Instance.prototype = {
changeImpl: function(){ this.impl = new Impl(); },
//delegate all other methods
f1: function(){ return this.impl.f1(); }
}
Related
Class Example:
$(document).ready(function(){
var $ = function(id)
{
return document.getElementById(id);
}
class someClass
{
constructor()
{
...
}
someMethod()
{
...
}
}
... // rest of examples within this scope
});
So far I am able to create an instance of the class object when the window loads and then calling a method of that class on a button click event while also binding this:
var obj = new someClass()
$('startButton').onclick = obj.someMethod.bind(obj)
All works fine and well until I want reset by deleting and creating a new instance of that class object. I have attempted a couple of different methods:
First, I attempted to call a function on button click that does one more task than before (instantiates a new object). I tried this both with declaring the variable obj in the global scope specifying var for the type and assigning it to null, and then attempted to re-assign it and bind this on button click. This works up until I attempt to call my setup() method:
$('startButton').onclick = function() {
var obj = new someClass();
var obj.setup.bind(obj); // fails to call
}
I then attempted another route, which more or less landed me in the same spot:
$('startButton').addEventListener('click', function(event) {
var obj = new someClass();
console.log('obj is an instance of someClass?', obj instanceof someClass); // returns true
obj.setup.bind(obj); // fails to call
});
Without creating a new method within someClass that manually resets all of my class attributes back to the initial values (I'd rather re-call the constructor), how can I gracefully instantiate a new class object on button click and be able to bind this?
And when I do, is it okay to re-assign the variable holding the class object without first marking it for GC or deallocating it somehow? Nothing else references it. Apologies in advance if my terminology is off, I'm new to this.
obj.setup.bind(obj) binds a context to a function, but it does not call it. You either need to call it:
obj.setup.bind(obj)();
Or use .call() instead of .bind():
obj.setup.call(obj);
However in this case, since you call the method directly on the instance, there is not really a need to bind anything:
$(document).ready(function() {
var $ = document.getElementById.bind(document); // <-- bind without calling
class someClass {
constructor() {
this.ready = false;
}
setup() {
this.ready = true;
}
}
$('startButton').addEventListener('click', function(event) {
var obj = new someClass();
obj.setup();
console.log(obj.ready); // true
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="startButton">Start</button>
I'm trying to understand Javascript OOP. I'm trying to overwrite a method inside a class. The class has a default functionality when a 'click' in made. I want to override that function, so something new happens when a click is made.
I have a Javascript class that looks like this:
AlertModal = function(){
var x = *this is my close object;
x.onclick = destoryAlert;
function destroyAlert(){
console.log('destroy');
}
}
My HTML file shows:
<script type="text/javascript">
window.alert = function (message) {
var newAlert = new AlertModal();
newAlert.destroyAlert = function(){
console.log('new alert destroy');
};
newAlert.destroyAlert();
};
I get 'new alert destroy' which is great. But when I click the 'x', it says destroy as well. So it is overwritten, but not?! It's like it creates a new 'destroyAlert' function, when it's called, but leaves the default.
Can anyone please show me how I would do this, to create a class, with default functionality, but how to overwrite it if needed?
I'm use to programming in Java and Actionscript, extending classes and overwritting public/protected methods, but doing it Javascript seems so much different and I can't understand the logic to do so.
Thanks,
You can override methods on instance level:
AlertModal = function() {
this.init();
};
AlertModal.prototype.init = function() {
var modal = this;
var x = ...;
x.onclick = function() {
// Note that I'm not using `this` here, because it would
// reference `x` instead of the modal. But we can pass the modal
// from the outer scope. This is called a lexical closure.
modal.destroy();
};
};
AlertModal.prototype.destroy = function() {
console.log('destroy');
};
var myalert = new AlertModal();
myalert.destroy = function() {
console.log('new destroy');
};
myalert.destroy();
But if you want to do the same override in multiple places, it would probably be better to create a specialized OtherAlertModal by inheriting from AlertModal class. Here's a good approach to inheritance in JavaScript: http://ejohn.org/blog/simple-javascript-inheritance/
x.onclick = destroyAlertl
sets x's onclick handler to a reference local function
whereas
newAlert.destroyAlert = ...
sets this object's destroyAlert property set to a different function. It does not change the reference stored in x.onclick.
You need to put the "default" function on the prototype of AlertModal:
AlertModal.prototype.destroyAlert = function() {
...
}
and register the handler differently:
var self = this;
x.onclick = function() {
self.destroyAlert();
}
If you subsequently overwrite the destroyAlert property of such an object then the new function will be called instead.
I am using Lightbox2
https://github.com/lokesh/lightbox2/blob/master/js/lightbox.js
And I don't understand why all the inner members of Lightbox are prototyped (Lightbox.prototype.init) and not simply members (Lightbox.init)?
If they are specific to each instance of lightbox wouldn't it be easier to use this.init?
Confused? Don't be...
Think of it this way:
Lightbox is your class definition, but it's not yet an instance.
Whatever you put directly on the class is like a static member:
Lightbox.staticFunc = function() {
// "this" will not point to instance object
};
Whatever you put on its prototype is a shared instance member:
Lightbox.prototype.instanceFunc = function() {
// "this" will point to object instance so members can be accessed
};
When you create an instance of a class, all instance members are accessible throught this keyword, but static ones through class definition:
var someData = Lightbox.staticFunc();
var l = new Lightbox();
l.instanceFunc();
Does this clear you understanding of prototype members?
Lightbox code then
The code that you've been looking at means this:
// this is a constructor that accesses instance properties (using "this")
// ------
// since properties are accessed via "this.something" means that they are
// not shared between instances but are part of one particular instance
// ------
function Lightbox(options) {
this.options = options;
this.album = [];
this.currentImageIndex = void 0;
this.init();
}
// adding an instance method that will be accessible to lightbox object instance
// that's why it can also access instance members (using "this")
// ------
// all functions that are defined on the prototype are shared between
// all instances so they consume less resources because not every
// object instance created them separately.
// ------
Lightbox.prototype.init = function() {
this.enable();
return this.build();
};
But some parts of this code are a bit confusing i.e.
LightboxOptions = (function() {
function LightboxOptions() {
this.fileLoadingImage = 'images/loading.gif';
this.fileCloseImage = 'images/close.png';
this.resizeDuration = 700;
this.fadeDuration = 500;
this.labelImage = "Image";
this.labelOf = "of";
}
return LightboxOptions;
})();
LightboxOptions class is contained within a function closure even though it doesn't define any private data, so the outer immediately executing function could be omitted in this example while having identical results:
LightboxOptions = function() {
this.fileLoadingImage = 'images/loading.gif';
this.fileCloseImage = 'images/close.png';
this.resizeDuration = 700;
this.fadeDuration = 500;
this.labelImage = "Image";
this.labelOf = "of";
};
It would of course be possible to define those functions in a constructor using this but then they wouldn't be shared between instances hence every object instance would define the same function hence consuming more resources. So this is not the same although from the execution point it does look the same:
CustomClass = function() {
this.prop = true;
};
CustomClass.prototype.method = function() { alert("I'm shared."); };
is slightly different than:
CustomClass = function() {
this.prop = true;
this.method = function() { alert("I'm duplicated in every instance."); };
};
The later consumes more resources while function is defined for every object instance.
...and a bit more to completely clear this thing
Suppose we have this class definition:
var C = function() {
this.prop = true;
this.method = function() { console.log("Per instance method"); };
}
C.prototype.method = function() { console.log("Shared instance method"); };
What happens here if we call these lines of code
var a = new C();
var b = new C();
a.method();
b.method();
delete a.method;
a.method();
b.method();
What do you think the output would be? You should get at least a little confused what happens after delete? Which method will get deleted? Per instance? Shared? Both? Well as it should be per instance method gets deleted on object instance a, that's why afterwards it reports that the shared method has been called. But only on a. b still has its own per instance method.
So without any further ado, output looks like this:
Per instance method // a.method
Per instance method // b.method
Shared instance method // a.method
Per instance method // b.method
What about prototype properties
These are different. When you create an object instance all those properties get copied to every object and are not shared. So whatever you do on them within the realm of a particular object will not get reflected to others.
If you'd then delete such property on a particular object it would still be available with its initial value as it was when object got instantiated.
var C = new function() {};
C.prototype.prop = 1;
var a = new C();
var b = new C();
a.prop = 10; // does not change the value of "b.prop"
delete a.prop; // "a.prop" is now back to 1
If they are specific to each instance of lightbox wouldn't it be
easier to use this.init?
They shouldn't be that's why they are putting everything in prototype object. When you use prototype, all methods still become available to you only that they do not become instance members.
JavaScript works with prototype chain, when it sees a method, it searches through the prototype chain till it finds the specified method. This process goes till the final Object object if not found in the middle.
You should only create instance members (via this) that you think are reasonable or needed because it adds an overhead (computational waste) if you put unnecessary methods using this keyword eg instance members.
I am wrapping common javascript functions that will work on elements on a page.
My page has 2 of these elements (textareas), so I will need to create 2 instances and then I want to do this:
var textArea1 = new SomeClass();
var textArea2 = new SomeClass();
textArea1.init("ta1");
textArea2.init("ta2");
I tried doing this the module pattern way, but I'm confused how I can create 2 seperate instances of it?
var MYMODULE = function() {
var _init = function(ta) {
// ..
}
return {
init: function(ta) {
_init(ta);
}
};
}();
Use a constructor function:
function SomeClass(id) {
this.id = id;
// ...
}
Usage:
var textArea1 = new SomeClass("ta1");
var textArea2 = new SomeClass("ta2");
You can put methods for the class in the prototype for the function. Example:
SomeClass.prototype = {
getValue: function() { return document.getElementById(this.id).value; }
};
Usage:
var text = testArea1.getValue();
Using your specific example, you could just MYModule twice, but it's a weird pattern that doesn't seem to do a whole lot.
Simple example how instantiation works:
function SomeClass() {
// constructor
}
SomeClass.prototype.init = function(ta) {
// ..
}
var textArea1 = new SomeClass();
var textArea2 = new SomeClass();
textArea1.init('ta1');
textArea2.init('ta2');
But regardless, you may like Backbone.js
Your MYMODULE idea will work fine. As above and then
MYMODULE.init("ta1");
MYMODULE.init("ta2");
This line here will not care it is called with two different parameters
var _init = function(ta) {
// ..
}
It is just a place to hold a function. The real question is what is inside that function.
For example if it works with ta in some standard way (attaches event handlers, does some styling.. ) then it will not be a problem. The issue will be if you use MYMODULE local variables and expect to have more than one of them. You only have one MYMODULE so local variables will be shared with this design. This might be what you want. I'm not sure.
This pattern can work fine for a control passed in having special data all itself. The best way to do this -- since you are using jQuery is with the data function... thus the code could look like:
var _init = function(ta) {
jQuery.data(ta,"foo", 10);
// etc
}
I'm not entirely sure how to implement OOP concepts in JS.
I have a class which is entirely declared in its constructor:
function AjaxList(settings)
{
// all these vars are of dubious necessity... could probably just use `settings` directly
var _jq_choice_selector = settings['choice_selector'];
var _jq_chosen_list = settings['chosen_list'];
var _cb_onRefresh = settings['on_refresh'];
var _url_all_choices = settings['url_choices'];
var _url_chosen = settings['url_chosen'];
var _url_delete_format = settings['url_delete_format'];
var jq_choice_selector_form = _jq_choice_selector.closest("form");
if (DEBUG && jq_choice_selector_form.length != 1)
{
throw("There was an error selecting the form for the choice selector.");
}
function refresh()
{
_updateChoicesSelector();
_updateChosenList();
_cb_onRefresh();
};
AjaxList.prototype.refresh = refresh; // will this be called on all AjaxLists, or just the instance used to call it?
// AjaxList.refresh = refresh; // will this be called on all AjaxLists, or just the instance used to call it?
// ...
}
There are multiple instances of AjaxList. When I call refresh() on one of them, I want only that one list to refresh itself. In the following instance:
term_list = AjaxList(settings);
term_list.refresh();
The refresh() call seems to make all the AjaxLists refresh themselves. What is the correct way to do this?
I'm using jQuery, if it makes any difference.
You should not redefine the prototype function in the constructor.
If you want to create a privileged function use this.methodname = ... from the constructor.
function AjaxList() {
var privateVar = 0;
function privateFunction() {
//...
}
//create a refresh function just for this instance of the AjaxList
this.refresh = function() {
//privileged function, it can access the 'privateVar & privateFunction'
privateVar++;
}
}
//public functions that don't need access to the private variables/functions
AjaxList.prototype.publicFunction=function() {
};
Also if you want to create a proper object, you need to change
term_list = AjaxList(settings);
to
term_list = new AjaxList(settings);
AjaxList = function(settings) {
this._jq_choice_selector = settings["choice_selector"];
this._jq_chosen_list = settings["chosen_list"];
this._cb_onRefresh = settings["on_refresh"];
this._url_all_choices = settings["url_choices"];
this._url_chosen = settings["url_chosen"];
this._url_delete_format = settings["url_delete_format"];
this.jq_choice_selector_form = _jq_choice_selector.closest("form");
if (DEBUG && jq_choice_selector_form.length != 1) {
throw "There was an error selecting the form for the choice selector.";
}
};
AjaxList.prototype = {
_updateChoicesSelector: function() { },
_updateChosenList: function() { },
_cb_onRefresh: function() { },
refresh: function() {
this._updateChoicesSelector();
this._updateChosenList();
this._cb_onRefresh();
}
};
Given that structure, you should be able to call:
var ajaxList = new AjaxList(settings);
ajaxList.refresh(); // etc.
I'm using jQuery, if it makes any
difference.
No it doesn't. See my answer here: What's the difference between Javascript, Jquery and Ajax?
I have a class which is entirely
declared in its constructor
There are no classes in Javascript. Forget them. You really need to learn some of the basics of this language in order to use them. It's not Java, even though it looks similar.
If you have a Constructor Function it will create an instance. The shared methods will be in the prototype chain, and only instance specific data goes right into the function with the this keyword.
So the basic concept of an object would look like this:
// constructor of an instance
function MyObject( param1, param2 ) {
this.param1 = param1;
this.param2 = param2;
this.param3 = 32;
return this; // [optional]
}
// Public methods can be called by any instance.
// Instances share their prototype object.
// The this keyword always points to the current
// instance that calls the method.
MyObject.prototype.sum = function() {
return this.param1 + this.param2 + this.param3;
}
// refresh should be a shared method, since it
// does the same thing on every instance
MyObject.prototype.refresh = function() {
// do the refresh
// ...
}
The power of this concept is that there is only one refresh function in memory. And it can deal with any instance. In addition, if another object inherits from MyObject the refresh function will be inherited. But in the memory there will be still one shared refresh function. And it can deal with any of the parent or child instances.