I am just getting my feet wet where JavaScript Prototyping is involved, and I am having some trouble.
I need to create a _LEAVE object from a LEAVE prototype for a system I am working on based on a prototype object. The _LEAVE object has a function named Ready, which should fire when the document is ready. The system already has similar functionality in some of it's older code, and I am trying to keep it uniform.
Here is the code I am trying, but I keep getting an error:
var LEAVE = function () {
}
$(document).ready(function () {
_LEAVE.Ready();
});
var _LEAVE = function (params) {
this.Ready = function () {
alert ("Leave Ready");
};
}
_LEAVE.prototype = new LEAVE();
Error:
SCRIPT438: Object doesn't support property or method 'Ready'
leave.js, line 6 character 5
I'm not sure where I am going wrong, as this seems to be what is happening in other parts of the system. At least, something similar is happening, but I am struggling to wrap my mind around the old code...
Would appreciate any advice anyone could give me! :-)
I'm not sure if I've understood you correctly, but are you attempting to create an instance of a LEAVE object? If so, LEAVE needs to be a constructor function, and Ready should be a method on the prototype of that:
var LEAVE = function () {};
LEAVE.prototype.Ready = function () {
alert("Leave Ready");
};
Now, you can instantiate LEAVE by calling the constructor with the new operator:
var _LEAVE = new LEAVE(); // _LEAVE is an instance of LEAVE
$(document).ready(function () {
_LEAVE.Ready(); // Ready is a method of `LEAVE.prototype`
});
Methods declared as properties of the prototype object are shared by all instances. So all instances of LEAVE will have a .Ready method available to them, but they will share one copy of the function in memory (the copy that was assigned to the property of LEAVE.prototype).
What you have done here is just inherited the child _LEAVE function from parent LEAVE function. But if you want to call a method in the child class, you need to create an instance of it. So you need to create an instance of the _LEAVE class. just add this line :
var _LEAVE_OBJECT = new _LEAVE();
and use _LEAVE_OBJECT.Ready() instead of _LEAVE.Ready(); in $(document).ready.
Modified code :
var LEAVE = function () {
}
$(document).ready(function () {
_LEAVE_OBJECT.Ready();
});
var _LEAVE = function (params) {
this.Ready = function () {
alert ("Leave Ready");
};
}
_LEAVE.prototype = new LEAVE();
var _LEAVE_OBJECT = new _LEAVE();
Related
so I'm having a problem that seems to defy everything I know about how scope is handled in JavaScript with anonymous functions - but it could be something else I'm not thinking about.
I have a JavaScript object, called Element, with a constructor similar to this:
function Element(boxElement) {
var self = this;
// Set jquery instance variables
self.pageElement = null;
self.boxElement = boxElement;
... blah blah blah
// Implement triggers to empty functions
self.onElementClicked = function () {};
// Bind listeners
self._bind_listeners();
}
The bind_listeners method is defined as such
Element.prototype._bind_listeners = function() {
var self = this;
self.boxElement.on('click', function (e) {
// Don't handle if handled already
if (e.isDefaultPrevented()) return;
console.log("Got past the return");
self.onElementClicked();
});
};
And there's also a method to set the callback method onElementClicked:
Element.prototype.on_element_click = function(callback) {
var self = this;
self.onElementClicked = callback;
};
The problem I am encountering is that if I set my callback using the on_element_click method, my method doesn't see the current instance - it sees what the instance would look like just after construction.
More specifically to my situation, there's an instance variable called boxElement that refers to a JQuery element - and in Chrome's console I can see that the instance (self) still does refer to the correct element on the page, but the onElementClicked instance variable (and others) do not seem to be set from within the listener.
Feel free to revise my explanation or ask for clarification.
From the implementer perspective:
If I do this:
// Set default listener for element click
formElement.on_element_click(function () {
console.log("Hello");
});
The listener never says Hello because onElementClicked doesn't appear to be set.
However, if I instead do this:
formElement.boxElement.click(function () {
console.log("Hello");
});
It successfully says "Hello" and makes me confused.
I found the solution to my specific problem, which is a good example of how an error like this can occur. (offtopic: please feel free to add answers for other ways to produce this error - it is a very non-intuitive problem and will always be caused by an external factor)
It turns out the class I was testing with is a class that extends my Element class - BUT, it does so improperly / VERY VERY badly!
As embarrassing as it is to post this, here's the original constructor of my "subclass" (quotes for reasons soon apparent):
function StrikeoutFormElement (formElement) {
var self = this;
// Set reference to form element
self.fe = formElement;
$.extend(self, self.fe);
// Override methods
self.on_reposition(function () {
self._on_reposition();
});
}
I used JQuery's object extending function and a hacky workaround to override something. I have learned the hard way to NEVER use JQuery's extend for OOP, as it is only intended for data manipulation rather than as a language tool.
The new constructor looks like this:
function StrikeoutFormElement (elem) {
var self = this;
}
// Extend the FormElement prototype
StrikeoutFormElement.prototype = Object.create(Element.prototype);
StrikeoutFormElement.prototype.constructor = Element;
This is a method described in an MDN article somewhere. I'll post the source when I find it if someone doesn't beat me to it.
Shoutout to anyone who looked at this obscure problem and attempted to figure it out!
I've got 3 codes :
var control = new Control();
function Control() {
this.doSomethingElse = function() {...}
this.doSomething = function () {
control.doSomethingElse();
}
}
Or
var control = new Control();
function Control() {
var self = this;
this.doSomethingElse = function() {...}
this.doSomething = function () {
self.doSomethingElse();
}
}
Or
var control = Control();
function Control() {
var self = this;
this.doSomethingElse = function() {...}
this.doSomething = function () {
self.doSomethingElse();
}
return self;
}
Important : The function is a controller, and just declared once. Then I'm using "control" everywhere in my code...
I was wondering if the control.doSomethingElse() was slow ?
In the end, what is the right thing to do and/or the fastest code in those exemple ?
Thanks !
The first is wrong - an object should never internally use the variable name by which it is known outside. Other code could change that variable to point to something else, breaking this code.
The third is also wrong - when calling Control() without new the assignments to this.foo inside will end up getting attached to the global object (except in strict mode, where there's no implicit this on bare function calls, so the assignment to this.doSomethingElse tries to attach to undefined, causing a runtime error).
That only leaves the second as appropriate, but ultimately it's a question of correctness, not performance.
Do not define methods in constructor - that means defining them every time an instance is created. Use Control.prototype.foo = function() {} instead. Also you do not need to return this if you're using new operator - that's the whole point of new operator.
The recommended approach is this:
function MyClass(param1) {
// Here we're changing the specific instance of an object
this.property1 = param1;
}
// Prototype will be shared with all instances of the object
// any modifications to prototype WILL be shared by all instances
MyClass.prototype.printProperty1 = function() {
console.log(this.property1);
}
var instance = new MyClass("Hello world!");
instance.printProperty1(); // Prints hello world
To understand this code, you need to understand javascript's prototype-based inheritance model. When you create instance of MyClass, you get a new object that inherits any properties present in MyClass.prototype. Read more about it.
Also I wonder:
The function is a controller, and just declared once.
If you're not using this multiple times, you don't need to create something like class. You can do this instead:
var control = {doSomething:function() { ... }};
I assume you are used to Java, where everything must be a class, whether it makes sense or not. Javascript is different, you can also make single objects or functions as you need.
Is this possible? I'm trying to overwrite a javascript method after my page has been loaded. The code in question looks similar to this:
myObject = Backbone.ViewManager.BaseView.extend({
myMethod: function() {
alert("in old method definition");
},
initialize: function() {
var a = this;
Our.Events.on("alertEvent", function(){
a.myMethod();
}
}
);
(The Backbone.ViewManager bit is just a way to create an object in javascript using a framework and not important here.)
Note the event callback defined above in the initialize method. We initialize the objects as soon as they're created.
After my page has loaded I tried to redefine myMethod to alert a different message. But when the alertEvent fires the original message appears in the alert.
I assume this is because of a closure, that redefining the method on myObject after its been initialized won't affect the definition pointed to by a? If that's the case, is there something I can do to change the definition used by a, or am I out of luck?
You should be able to define myMethod on the object you create which will override the prototype myMethod. What you have written should be able to work because you arn't referencing myMethod directly, but through the a object.
The following works for me
function ObjCstr(){}
ObjCstr.prototype.myMethod = function(){ alert("Old Message"); };
ObjCstr.prototype.callMyMethod = function(){
var a = this;
return function(){ a.myMethod(); };
};
var test = new ObjCstr();
var fakeListener = test.callMyMethod();
fakeListener(); // "OldMessage"
test.myMethod = function(){ alert("Overridden!"); };
fakeListener(); // "Overridden!"
So, this is probably answered somewhere on this site, but I can't find it, if it is.
I'm having trouble figuring out why one of my this references inside functions seems to be resolved when I create the object, and one when I call the function that has the reference inside it. Here's some code:
function MyObj (name) {
this.locked = false;
this.name = name;
this.elem = null;
this.func1 = function () {
if (this.locked) return;
/* code that changes this.name here */
this.elem.innerHTML = this.name;
};
this.func2 = function () {
this.locked = !this.locked;
if (this.locked) this.elem.className = "locked";
else this.elem.className = "unlocked";
};
}
var myObjGlobal = new MyObj("foo");
function callFunc1 () {
myObjGlobal.func1();
}
Then I have a function that is called on document load:
function onLoad() {
var myElem = document.getElementById("myElem");
myObjGlobal.elem = myElem;
myElem.onclick = myObjGlobal.func2;
document.getElementById("myButton").onclick = callFunc1;
}
I've made sure all my html elements have the right ids. When I click myButton, I get no errors. However, when I click myElem, I get Uncaught TypeError: Cannot set property 'className' of undefined.
Why is the first this set when I call the function, and the second this set when I create the object? (Or so it seems?)
here's a working jsfiddle showing the problem (with the given example code).
Thanks in advance!
myElem.onclick = myObjGlobal.func2;
This doesn't do what you think inn JavaScript. It doesn't give you func2 with the object "attached" to it in any way; it just gives you func2. When it gets called later, it's called as a method of myElem, so that's what this is.
This is a gigantic and awful wart in JS. :)
You can either wrap it in another function:
myElem.onclick = function() {
myObjGlobal.func2();
};
Or use .bind, which does effectively the same thing, and which is supported almost universally nowadays:
myElem.onclick = myObjGlobal.func2.bind(myObjGlobal);
Note also that assigning to onclick is a little rude, since you'll clobber any existing click handler. You may want addEventListener instead.
myElem.onclick = myObjGlobal.func2;
This loses myObjGlobal entirely; myObjGlobal.func2 is just a function, with nothing tying its this to anything. In JavaScript, the this of a function is determined when it’s called, not when it’s defined. This is a fantastic and useful feature of JavaScript that’s much more intuitive than, say, Python. When myElem.onclick is called, it’ll be called with this bound to myElem.
Function.prototype.bind is a utility to do what you’re doing with callFunc1, by the way:
myElem.onclick = myObjGlobal.func2.bind(myObjGlobal);
How to attach a function dynamically to a javascript object.For ex: if the function for dynamic attachment is attach(),then i should be able to attach the function fn to onject obj as follows..
attach(
obj,fn,{
alert(1)
}
)
function attach(obj,fnName,code)
{
obj[fnName] = code;
}
If by "attach a function dynamically to a javascript object" you mean "add a function-object as an object property" then the syntax you've already shown is almost right. This is what it should be:
var fnName = "testFunc";
obj[fnName] = function() { alert("Test function"); };
// or
obj.testFunc = function() { ... };
// or
obj[fnName] = nameOfFunctionDefinedElsewhereInCurrentScope;
Which means you could call your attach() function like this:
// attach an anonymous function:
attach(obj, "newFunctionName", function() { alert(1); });
// attach a function defined elsewhere
attach(obj, "newFunctionName", someFunction);
Note: the attach() function really doesn't save any effort at all, in fact it just gives you more characters to type...
By the way (but don't do this), if the parameter you want to pass as code is a string of code do this:
var code = "alert(0);";
obj[fnName] = new Function(code);
More information: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function
EDIT : The other post's Function(code) solution appears better. I did not know about that constructor.
A possible solution may be:
Object.prototype.attach = function(name,code) {
this.name = function() {
eval(code);
};
}
You can attach them as function objects if they've already been defined, such as here: Javascript: better way to add dynamic methods?
You can also use the new Function constructor to dynamically define the functions, such as here: Creating functions dynamically in JS
Here is an explanation of the differences between eval and the new Function constructor: Are eval() and new Function() the same thing?
As a warning, use of eval() and the new Function constructor have created controversy and have been condemned (to some extent) by a number of individuals, such as here: Legitimate uses of the Function constructor
Here is more information about eval: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Here is more information about the new Function constructor: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
Assuming that definition of function attach, you should call it like this:
attach(obj, fnName, function(){ alert(1); });
The way you invoked it is invalid syntax.
Also, as you may have noticed, that's not a very useful function, since you can do the same thing using your one-line function definition:
obj[fnName] = function(){ alert(1); });