I've a crazy problem. I'm instantiating an object from a class. Then I want to pass a function from this object to the setInterval function. The whole code block is executed with a keyDown event.
Here's the code:
function doKeyDown(e){
var instance = new Class(e.keyCode);
setInterval(instance.functionToBeExecuded(), 200);
}
The strange thing is that it's getting executed once and then starts to loop an error which states (firebug):
27
SyntaxError: missing ] after element list
[object HTMLAudioElement]
For the sake of completeness:
Class.prototype.functionToBeExecuded = function(){
var node = document.createElement("audio");
//the key is stored in the object (from the Class's constructor)
node.setAttribute("key", this.variable);
}
Does anyone see some fails?
Thanks in advance! =)
P.S.: This is not a duplicate cause I'm not facing the function, function() error. If I'm executing it with (instance.functionToBeExecuded, 200) it happens just nothing.
What I've found out so far is that the problem must be in the scope somehow. If I do it like this:
function doKeyDown(e){
var instance = new Class(e.keyCode);
setInterval(instance.functionToBeExecuded, 200);
}
Class.prototype.functionToBeExecuded = function(){
console.log(this);
var node = document.createElement("audio");
//the key is stored in the object (from the Class's constructor)
node.setAttribute("key", this.variable);
}
I'm getting a looping console output with "window". So the function is executed in the window's scope. But why? And how to work around?
Console output:
Window index.html
The workaround would be: wrap it using another function instead of calling method directly
function doKeyDown(e){
var instance = new Class(e.keyCode);
setInterval(function(){
instance.functionToBeExecuded()
}, 200);
}
This would give output many of these:
Class {functionToBeExecuded: function}
Related
I'm trying to wrap my head around using Javascript prototype objects and have run into a block perhaps due to my understanding of a traditional classes.
My code looks like the following:
$(document).ready(function () {
var instance = new cards();
$dom = instance.buildItem(5);
}
var cards = function () {
//constructor
this.chromaObj = chroma.scale(["lightblue", "navy"]).domain([2, 6]);
}
cards.prototype.buildItem = function (val) {
var scale = this.chromaObj;
var $item = $("<span>" + val + "</span>").addClass("label-default").addClass("label").css({
"background-color": scale(val)
});
return $item;
}
Every time scale is called in the buildItem function, I receive an error in the console that scale is not a function, however, if I create the scale instance inside of the buildItem function, it works as expected.
Can someone please point me in the right direction as to why I am unable to access the function reference when it is defined in the constructor?
The original code does in fact work correctly.
My problem turned out to be that I was calling buildItem from another method (not shown here) using the generic cards.prototype.buildItem(), which caused a subsequent call to chromaObj to be undefined.
The solution was to change all calls to cards.prototype.buildItem() to this.buildItem().
While this issue occurred to me specifically with KnockoutJS, my question is more like a general javascript question.
It is good to understand however that ko.observable() and ko.observableArray() return a method so when assigning a value to them, you need to call the target as method instead of simply assigning a value to them. The code that I'm working with should also support plain objects and arrays, which I why I need to resolve to a method to call to assign a value to the target.
Think of these 2 examples:
Non-working one (this context changed in called method):
// Assigning value to the target object
var target;
// target can be of any of thr following types
target = ko.observableArray(); // knockout observable array (function)
// target = ko.observable(); // knockout observable (function)
// target = {}; // generic object
// target = []; // generic array
//#region resolve method to call
var method;
if (isObservable(target)) {
// if it is a knockout observable array, we need to call the target's push method
// if it is a konckout observable, we need to call the target as a method
method = target.push || target;
} else {
// if target is a generic array, we need to use the array's push prototype
// if target is a generic object, we need to wrap a function to assign the value
method = target.push || function(item){ target = item; };
}
//#endregion
// call resolved method
method(entity);
Working one (this context is fine):
if (isObservable(target)) {
if (target.push) {
target.push(entity);
} else {
target(entity);
};
} else {
if (target.push) {
target.push(entity);
} else {
target = entity;
};
}
Now, to the actual question:
In the first approach, later in the execution chain when using a knockout observable knockout refers to this context within itself, trying to access the observable itself (namely this.t() in case someone is wondering). In this particular case due to the way of callin, this has changed to window object instead of pointing to the original observable.
In the latter case, knockout's this context is just normal.
Can any of you javascript gurus tell me how on earth my way of calling can change the 'this' context of the function being called?
Ok, I know someone wants a fiddle so here goes :)
Method 1 (Uncaught TypeError: Object [object global] has no method 'peek')
Method 2 (Works fine)
P.S. I'm not trying to fix the code, I'm trying to understand why my code changes the this context.
UPDATE:
Thanks for the quick answers! I must say I hate it when I don't know why (and especially how) something is happening. From your answers I fiddled up this quick fiddle to repro the situation and I think I got it now :)
// So having an object like Foo
function Foo() {
this.dirThis = function () {
console.dir(this);
};
};
// Instantiating a new Foo
var foo = new Foo();
// Foo.dirThis() has it's original context
foo.dirThis(); // First log in console (Foo)
// The calling code is in Window context
console.dir(this); // Second log in console (Window)
// Passing a reference to the target function from another context
// changes the target function's context
var anotherFoo = foo.dirThis;
// So, when being called through anotherFoo,
// Window object gets logged
// instead of Foo's original context
anotherFoo(); // 3rd log
// So, to elaborate, if I create a type AnotherFoo
function AnotherFoo(dirThis){
this.dirThis = dirThis;
}
// And and instantiate it
var newFoo = new AnotherFoo(foo.dirThis);
newFoo.dirThis(); // Should dir AnotherFoo (4th in log)
If you're after a way to choose the 'this' that will get used at the time of call,
you should use bind, that's exactly done for that.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
So if SomeObject has a push method, then storing it like this won't work :
var thePushMethod = someObject.push;
since you loose the context of the function when writing this.
Now if you do :
var thePushMethod = someObject.push.bind(someObject);
the context is now stored inside thePushMethod, that you just call with
thePushMethod();
Notice that you can bind also the arguments, so for instance you might write :
var pushOneLater = someObject.push.bind(someObject, 1 );
// then, later :
pushOneLater(); // will push one into someObject
Consider this example,
function Person () {
this.fname = "Welcome";
this.myFunc = function() {
return this.fname;
}
};
var a = new Person();
console.log(a.myFunc());
var b = a.myFunc;
console.log(b());
Output
Welcome
undefined
When you make a call to a.myFunc(), the current object (this) is set as a. So, the first example works fine.
But in the second case, var b = a.myFunc; you are getting only the reference to the function and when you are calling it, you are not invoking on any specific object, so the window object is assigned. Thats why it prints undefined.
To fix this problem, you can explicitly pass the this argument with call function, like this
console.log(b.call(a));
So, for your case, you might have to do this
method.call(target, entity);
Check the fixed fiddle
How can I pass an object from a function to a protoyped function of it's own?
function Main()
{
this.my_object = {"key":123};
}
Main.prototype.Sub = new Sub(this.my_object);
function Sub(obj)
{
alert(obj);
}
Main.Sub; //this should alert the object created in Main()
fiddle: http://jsfiddle.net/GkHc4/
EDIT 1:
I'm trying to make a chain of functions, and each link must get the previous object and add something. At this point it is an experiment. For example:
Main.Link1.Link2.link3();
//link3 it's a prototype for link2
//link2 it's a prototype for link1
//and so on...
Where each link adds a key to the initial object
There are three different issues:
1) You don't create an object with new Main(), but try to access the Sub property directly from the constructor. This doesn't work. You have to create an instance:
var main = new Main();
main.Sub; //<-- now you can access it
2) You try to access the property my_object with this, but outside of any function. That doesn't work either. this will probably point to the window object, which doesn't have any property called my_object. The solution could be to write main.my_object but that would kind of defeat the purpose of the prototype. Usally you would put there function or properties that are the same for every instance. But you are trying to put a property in there that should be different for every instance. So it looks like you don't need to access the prototype at all but can just define it as a regular property:
function Main()
{
this.my_object = {"key":123};
this.Sub = new Sub(this.my_object);
}
3) The line main.Sub doesn't execute anything. You are just requesting the property Sub. Instead the function Sub will be executed when you write new Sub(...). So if you want to alert something by calling a function, you have to define a function. You could for instance define an alert method in Sub or in Sub.prototype and then call this method:
function Sub(obj)
{
this.alert() {
alert(obj);
}
}
main.Sub.alert();
updated Fiddle
I think that maybe you are looking for something like the following:
function Main()
{
this.my_object = {"key":123};
}
Main.prototype.Sub = function () {
Sub(this.my_object);
};
function Sub(obj)
{
alert(obj);
}
var main = new Main(); // main object is created with main.my_object property
main.Sub(); // this will do alert(main.my_object)
I think you're going at it in the wrong way.. you see:
The alert is not coming from the last line, it's actually coming from the prototype line, when you do the "new Sub".
Maybe a better approach would be something like:
function Main()
{
this.my_object = {"key":123};
}
Main.prototype.Sub = Sub; //You set the prototype, but don't actually execute the function
function Sub(obj)
{
alert(obj);
}
var m = new Main(); //You need to create a new object of type Main in order for it to have access to the method Sub
m.Sub(m.my_object); //this should alert the object created in new Main()
Does this help?
Edit
Additionally, you could even do something like this for the Sub function:
function Sub() {
alert(this.my_object);
}
Although that way, you wouldn't be able to use the function by itself.
I defined a global Javascript function:
function resizeDashBoardGridTable(gridID){
var table = document.getElementById('treegrid_'+gridID);
.....
}
After this function was used a few times, I want to remove(or undefined) this function because the Procedure code should be called again. if somebody try to call this method we need do nothing.
I don't way change this function right now.
so re-defined this function may be one way:
function resizeDashBoardGridTable(gridID){
empty,do nothing
}
Thanks. any better way?
Because you're declaring it globally, it's attached to the window object, so you just need to redefine the window function with that name.
window.resizeDashBoardGridTable = function() {
return false;
}
Alternately you could redefine it to any other value or even to null if you wanted, but at least by keeping it a function, it can still be "called" with no detriment.
Here's a live example of redefining the function. (thanks TJ)
An additional reason for pointing out that I'm redefining it on the window object is, for instance, if you have another object that has that function as one if its members, you could define it on the member in the same way:
var myObject = {};
myObject.myFunction = function(passed){ doSomething(passed); }
///
/// many lines of code later after using myObject.myFunction(values)
///
/// or defined in some other function _on_ myObject
///
myObject.myFunction = function(passed){}
It works the same either way, whether it's on the window object or some other object.
how about using a var?
// define it
var myFunction = function(a,b,c){
console.log('Version one: ' + [a,b,c].join(','));
}
myFunction('foo','bar','foobar'); // output: Version one: foo,bar,foobar
// remove it
myFunction = null;
try { myFunction(); console.log('myFunction exists'); }
catch (e) { console.log('myFunction does not exist'); }
// re-define it
myFunction = function(d,e,f){
console.log('Version two: ' + [d,e,f].join(','));
}
myFunction('foo','bar','foobar'); // output: Version two: foo,bar,foobar
OUTPUT:
[10:43:24.437] Version one: foo,bar,foobar
[10:43:24.439] myFunction does not exist
[10:43:24.440] Version two: foo,bar,foobar
The simplest approach is to set the function (treat it as a variable) to null. This works even if you don't declare it as a var. Verified this on IE.
resizeDashBoardGridTable = null
If the functions needs to be called 1 time you use an anonymous self invoking function like this:
(function test(){
console.log('yay i'm anonymous');
})();
If you have to call the function multiple times you store it into a var and set it to null when you're done.
Note: You don't have to name an anonymous function like I named it test. You can also use it like this:
(function(){
console.log('test');
})();
The reason I do name my anonymous functions is for extra readability.
At the very beginning of the javascript file, I have:
var lbp = {};
lbp.defaults = {
minLength: 40
};
I can successfully alert it afterwards, with:
alert(lbp.defaults.minLength);
But as soon as I put it inside a function, when I alert, I get "Undefined". What gives, and how do I avoid this? Is it absolutely necessary to pass this variable into each function, for example, by doing:
function(lbp) { alert(lbp.defaults.minLength); }
I would have thought that defining it first, it would attain global scope and not be required to be passed in?
Thanks in advance for enlightening me :)
====================================
EDIT:
The problem seems like it might be my initialize function is itself defined within lbp. Is there any way to use this function var, and still use lbp vars inside it?
lbp.initialize = function() {
alert(lbp.defaults.minLength);
};
The full bit of code looks like this:
<script type="text/javascript">
var lbp = {
defaults: {
minLength: 40
}
};
lbp.initialize = function() {
alert(lbp.defaults.minLength);
};
window.onload = lbp.initialize;
</script>
Are you actually passing lbp as the argument? Otherwise the parameter with the same name will hide the global variable.
Use this.
var lbp = {
defaults: {
minLength: 40
}
};
lbp.initialize = function() {
alert(this.defaults.minLength);
};
window.onload = function() { lbp.initialize(); };
If you call initialize as a method of lbp, this will point to lbp. When you assign a function to an event handler, such as window.onload, you are essentially copying the body of that function to the object on which the event handler is defined. So,
window.onload = lbp.initialize
is the same as
window.onload = function() {
alert(this.defaults.minLength);
};
Now, this is pointing to window, which is obviously not what we want. By wrapping the call to lbp.initialize() in a function, we preserve the context of this within that function and we can make sure that it always points to lbp. Check out this for a more complete explanation.
This works for me from javascript console in Firefox:
javascript:var lbp={}; lbp.defaults={minLength: 40};function xx() { alert(lbp);alert(lbp.defaults);alert(lbp.defaults.minLength); }; xx();
Gives output [object Object], [object Object], 40.
So, it seems there might be some problem with some associated code, which is not shown?
In the original code where you are trying to use lbp in a function. You are passing lbp in as an argument. This would hide the lbp from the global scope with a local (to the function) variable of the same name (unless when calling the function you passed lbp in again).
//this is what you have and will not alert a thing other
//and will probably throw an error
function(lbp) { alert(lbp.defaults.minLength; }
//should just be this with no argument. this will allow
//the function to see lbp in the global scope.
function() { alert(lbp.defaults.minLength; }
by passing lbp as a parameter in the first function it is not seen inside the function as the global object, but the local argument.