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.
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 am trying to make a parent data access layer class that is inherited by multiple classes.
parent class:
var DataAccess = function() {
this.Save = function(){
alert(this.ListName); //works
SaveLogic(this.Id); //doesnt work
}
}
Child Class:
var Job = function(){
Job.prototype.ListName = 'MyList'; //works
this.Save = function(){
Job.prototype.Save().call(this);
//specific Job Save logic
}
}
Job.prototype = new DataAccess();
Now in my main class:
var aJob = new Job();
aJob.Id = 1;
aJob.Save(); //Does not work. Prototype can not see aJob.Id..
As you can see, I need to create a parent function with shared variables such as ID, so when I inherit the parent class, I can assign values to these variables so the shared logic of hte parents class work, then my extended class's can have specific logic
You can start with construction like this:
var DataAccess = function() {
this.Save = function(){
console.log('DataAccess Save call', this.ListName, this.Id);
}
}
var Job = function(){
this.ListName = 'MyList';
}
Job.prototype = new DataAccess();
/**
* Delete me to use parent's Save method.
*/
Job.prototype.Save = function(){
console.log('Job Save call', this.ListName, this.Id);
}
var aJob = new Job();
aJob.Id = 1;
aJob.Save();
#stivlo described how it works in his answer here: https://stackoverflow.com/a/4778408/1127848
The problem I had was I wanted to reuse the same code. I think I have worked it out this way, im still not 100% its the right way to go with prototype programming :
function DataAccess() {
//setup common variables
}
DataAccess._Save_(listname, id){
commonSaveLogic(id);
doStuff(listname);
}
function Job() {
this.ListName = 'Jobs';
DataAccess.call(this); //call DataAccess Constructor
}
Job.prototype = DataAccess;
Job.prototype.constructor = Job;
Job.ProtoType.Save = function(){
this._Save_(this.ListName, this.Id);
}
function AotherList() {
this.ListName = 'AnotherList';
DataAccess.call(this);
}
//same as above. Job and Another list both inherit off DataAccess.
Dont use .prototype inside the constructor. We define .prototype for sharing same copy to all objects.
You are missing here many things. I'm explaining one by one:
First : SaveLogic(this.Id); //doesnt work
Because You don't use this with the function so it's a global function not a constructor function. And you don't have defined it any where so there will be an error like function SaveLogic not defined
To prevent this error, define the function somewhere.
Second : You have passed this.Id as a parameter. Id using the line aJob.Id = 1; will not be accessible within the SaveLogic(this.Id); because Id is a property of aJob not of ajob.prototype. this.ListName will be available here because it's a property of prototype.
So it you want to get Id inside SaveLogic() function, define it as prototype property.
Third : when this line aJob.Save(); will be invoke it will call
this.Save = function(){
Job.prototype.Save().call(this);
//specific Job Save logic
}
Job.prototype.Save() will search for a function named as Save(). Which is not defined in Job's prototype so function not defined error will occur.
Fourth : call() can not be called anyhow excepts either DataAccess.call() or Job.call();
call() is just like the constructor call excepts it's first parameter get assigned to the constructor's this object.
Here i have improved your code. Just copy and paste it in your editor and see what is going here.
Try this :
function SaveLogic(Id)
{
alert(Id);
}
var DataAccess = function() {
this.Save = function(){
alert(this.ListName); //works
SaveLogic(this.Id);
return this; //doesnt work
}
this.call = function() {
alert('call is called here');
}
}
var Job = function(){
Job.prototype.ListName = 'MyList'; //works
this.Save = function(){
//console.log(Job.prototype.Save());
Job.prototype.Save().call(this);
//specific Job Save logic
}
}
Job.prototype = new DataAccess();
var aJob = new Job();
Job.prototype.Id = 1;
aJob.Save(); //Does not work. Prototype can not see aJob.Id..
I am trying create a hashmap in View function having instances of subviews , which I do in init method of View. But it is giving an error that init() of view doesnt exist. Am I doing anything wrong here? Thanks in advance.
http://jsfiddle.net/3fR4R/1/
view = function() {
var subview;
init = function() {
subview['search'] = new searchSubView();
}
}
check = function() {
console.log("clicked");
var view1= new view();
view1.init();
}
searchSubView = function() {
}
You've created a function and assigned it to an implicit global, which has nothing to do with the view function or instances created by it.
You can assign the function either by assigning to this.init within the constructor, or by putting it on view.prototype.
So either:
view = function() {
var subview;
// Note: You need code here to initialize `subview`
this.init = function() {
subview['search'] = new searchSubView();
};
};
or (note that I've made subview a property):
view = function() {
this.subview = /* ...initialize subview... */;
};
view.prototype.init = function() {
this.subview['search'] = new searchSubView();
};
Side notes:
You're falling prey to The Horror of Implicit Globals a lot in that code. You need to declare variables.
The overwhelming convention in JavaScript code is to use initial capitals for constructor functions, e.g., View rather than view.
You're also relying on automatic semicolon insertion, which I wouldn't recommend. Learn the rules and apply them, not least so you can minify/compress/compact your code safely.
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(); }
}
Below is the entire contents of a JS/JQuery file. I didn't write it, but I'm trying to add on to it. I am having trouble understanding what this is referring to. I haven't seen functions set up in this style before (SmartPhone = function() {})
SmartPhone = function()
{
this.miniMap = new GameModeMap();
this.init = function()
{
var self=this;
var $PhoneContainer = $("#PhoneContainer");
$PhoneContainer.append("<div id='PhoneScreen'></div>");
$PhoneContainer.append("<div class='PhoneButton'></div>");
$('.PhoneButton').click(function(){self.toggleClicked()});
this.miniMap.init("#PhoneScreen");
//append the appMenu
$("#PhoneScreen").append("<div id='AppMenu'></div>");
$("#AppMenu").hide();
initMenu();
//toggleClicked();
}
this.toggleClicked = function()
{
console.log(this);
$('#PhoneContainer').toggleClass ('clicked');
$('#PhoneScreen').toggleClass ('vertical');
this.miniMap.toggle();
$('#AppMenu').toggle();
}
this.init();
}
They're using the Object Functionality of JavaScript.
SmartPhone is essentially a class structure in this example, with init() being the construct (called by the last this.init() line within SmartPhone.
this is refering to the scope, and in this case the object being created.
How this works
Live Example
var Construct = function() {
this.magic = 42;
}
var c = new Construct();
alert(c.magic === 42);
The "this" variable in JavaScript can point to many different things depending on your context. There is a great blog post on this called: Understanding JavaScript’s this keyword
In the context you are showing, this will be bound to the object created from the SmartPhone constructor.
this refers to the SmartPhone object. For instance, this.init is defining the init method for the SmartPhone. Later, you could access it by using SmartPhone.init().