I have the following code:
var foo = function() {
this.init = function() {
alert('hiya');
};
};
$(document).ready(function() {
var bar = new foo();
bar.init();
});
which results in:
Uncaught TypeError: bar.init is not a function
However, if I pull the new object instantiation and the .init() call out of the document.ready function, everything works as expected.
Why does jQuery's document ready not allow me to instantiate a new instance of a globally-defined function?
Edit: KevinB was right, foo existed elsewhere in our codebase. I doubted that was the case as "foo" in the real-world instance is actually the unlikely-named "L8.contact_form_handler()", but apparently someone else was using that same unlikely naming scheme.
Related
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!"
I'm confused as to what the problem is with context in a JS constructor. Within the ctor I have a function declared. Before the call to that function this is set to the context of the ctor. Inside the function the value of this is set to window. I don't understand why. In the HTML the ctor is called with 'new'.
function MyCtor() {
var myFunc = function() {
debugger; // #2
// code for myFunc
}
debugger; // #1
myFunc();
debugger; // #3
}
At debugger #1, this is set to MyCtor. At #2 this is window. And at #3 it is back to MyCtor.
I'm sure I'm missing something basic here, but I've read a lot on scope and context; obviously not enough.
The this object is one of the most annoyingly hard-to-understand concepts in Javascript. And that's quite a contest to win... First off, you have to understand that it will be specific to each function you call - the context in which you call myFunc won't set it how you want it. Here's one way you can do it:
function MyCtor() {
this.myFunc = function() {
debugger; // #2
// code for myFunc
}
debugger; // #1
this.myFunc();
debugger; // #3
}
Generally, there are only a few situations in which you can rely upon a function's this to be a particular value. All of them to my knowledge:
objectToBeThis.aFunction = function() { ... } // declare this function as
// an object property at any time -
objectToBeThis.aFunction();
Or, not used as often is:
aFunction.call(objectToBeThis, extraArgument1, extraArgument2);
When a named, but not "owned" function (ie, var functionName = function(), or function functionName()) is called, then it will have window as its this argument. This part I'm less sure of as a certainty, but I just wouldn't use this inside such a method.
As in the case of your code, there's also "new MyCtor" - in which a new object is created to be returned, and that object is set to this inside of the constructor method.
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();
I am using a jQuery Class plugin as so :
jQuery(document).ready(function($) {
window.SSK.calendar = new(Class.extend({
filter_by_filtered_names: function() {
console.log('foobar!');
},
init: function() {
if ( window.location.href.match(/name_filters/) ) {
SSK.calendar.filter_by_filtered_names();
};
}
}))
});
For some reason this returns on load :
SSK.calendar is undefined
Which tells me that the plugin class is not loading before its own call. Very strange indeed. Curious if anyone knew a remedy?
The behaviour seems to make perfect sense to me, even if I don't know how Class works:
Class.extend(...) creates a new constructor function (I assume). new executes the constructor which in turn calls init. The result is assigned to window.SSK.calendar. You see, init is called upon instantiation and this happens before the instance is assigned to window.SSK.calendar.
Here is a simplified example:
function MyClass() {
this.bar = 'baz';
console.log(foo.bar);
}
var foo = new MyClass();
This will fail since foo is still undefined at the moment the constructor is called. The instance is the return value of the function call, hence foo cannot contain a reference to the instance before the call.
You might be able to solve your problem by simply using this to reference the instance:
init: function() {
if ( window.location.href.match(/name_filters/) ) {
// better in this case: if(/name_filters/.test(window.location.href))
this.filter_by_filtered_names();
};
}
The documentation of the plugin should mention how you can reference the instance from inside a method.
Seems you use two onReady listeners: jQuery(document).ready(fn) and $(fn) are exactly equivalent. Effectively, you will append the inner function to the end of the function queue when the outer function executes. When trying to access SSK.calendar in any onDOMready function that is not registered after that, it won't be available.
Example:
$(function(){
console.log("A");
$(function(){
console.log("B");
});
console.log("C");
});
$(function(){
console.log("D");
});
will log:
A
C
D
B
How does JQuery handle the "Cannot set property xxx of undefined" issue?" If I have:
function Foo() {
Foo.fn = Foo.prototype;
}
Then I have an extension JavaScript file with this code:
(function(foo) {
foo.fn.myPlugin = function() {
//return something here
};
})(Foo);
I get an error that says "Cannot set property myPlugin of undefined." Funny enough if I just copy the plugin code into FireBug or Chrome and execute it in the console, it works fine and attaches and works as expected like:
foo.myPlugin();
I'm guessing my problem either comes from the timing of the browser loading the document, or the fact that my existing object isn't inside of it's own namespace. I'm wondering if this only works when you define a class using the var syntax??
I don't know...from what I understand...either should work.
function Baz() {
return this;
}
Baz.prototype.fn = Baz.prototype;
var Foo = new Baz();
Baz = null;
(function(Bar) {
Bar.fn.myPlugin = function() {
return "test";
};
})(Foo);
Foo.myPlugin(); //returns test
This works, and leaves Baz assignable. Not sure if that is how jQuery does it or not.
From what I can tell, you never execute the line:
Foo.fn = Foo.prototype;
in your Foo() function.
Functions only execute when you call them.
Try using the singleton pattern:
var Foo = new function Foo() {
this.fn = this.prototype;
};
In this case, you're creating an instance of the Foo object by calling the new operator on the named anonymous Foo() function.
This isn't an issue for jQuery. There's at least one problem in your code, as pointed out in another answer.
If you are serious about understanding this, John Resig (the original author of jQuery) has put together an excellent interactive tutorial called Learning Advanced Javascript.