I have object:
var devark = {
init: function() {
var obj = this;
obj.assignHandlers();
},
assignHandlers: function() {
var obj = this;
document.getElementById("menu-toggler").onclick = obj.documentFunctions[0];
},
documentFunctions: [
function() {
toggleClass(this, "opened");
}
]
};
on window.load, I am calling the init method. That works fine but when it calls another object method assignHandlers, it throws an error:
[17:54:33.192] TypeError: obj.assignHandlers is not a function
Why is it?
Like #Bergi said, it's a this value issue that can be solved by doing:
window.onload = function () {
devark.init();
};
The difference between both ways is the value of this within the init function. To determine the natural value of this, look at the left-side of the . in a method call, such as obj.someFn();. Here the value of this within someFn will be obj. However, when you do window.onload = devark.init;, you can imagine the handler will later be invoke like window.onload(). Which means the value of this within the onload function, which is really the init function will be window, not devark.
You can also use Function.prototype.bind to bind a specific this value to a function.
window.onload = devark.init.bind(devark);
Related
I want to call posts() in refresh function.
Here is my code.
var TIMELINE = TIMELINE || (function (){
/*** private ***/
var _args = {};
var self = this;
return {
init : function(Args){
_args = Args;
}, // init
posts : function(data) {
alert('posts called');
}, // posts
unsetMarkers : function() {
alert('unsetMarkers called');
}, // unsetMarkers
refresh : function(){
self.posts;
}
};
}());
the problem is at this line self.posts;
I also tried self.posts({'data':'success','another':'thing'});
How I can use posts in refresh?
There are two problems in your code:
self doesn't refer to the object with the property posts, i.e. not to the object you are returning from the function. You have var self = this; and this refers to window (assuming non-strict mode).
You are not even trying to call the function.
Instead of returning the object immediately, assign it to self:
// instead of `var self = this;`
var self = {
// function definitions
};
return self;
and then you can call the method with
self.posts(); // note the parenthesis after the function name
If you are certain that the refresh function is always called as TIMELINE.refresh() (i.e. as a method of the TIMELINE object) , then you can also call the posts method with
this.posts();
and forget about self.
Further reading material:
MDN - this: Learn which value this has in different contexts.
Eloquent JavaScript - Functions: Learn how functions work.
refresh : function(){
this.posts();
}
JSFIDDLE
At the moment I came this far.
function Class() {
var privateMethod = function () {
return 'private'
}
this.publicMethod = function () {
return 'public'
}
var _constructor = function () {
$(document).on('click', _onClick)
}
var _onClick = function () {
// My error is `this`, focus now on the click event, but I need the object itself
console.log(privateMethod())
console.log(this.publicMethod())
}
_constructor()
}
$(document).ready(init)
function init() {
new Class()
}
The problem is that, in the click event, I am unable to call publicMethod.
I am able to call the private method.
How can I achieve this?
The problem is that, in your handler you've lost your context (this no longer means your instance of Class, it instead means the object that triggered your event. You need to create a closure scoped version of this to hold onto that context.
var self = this;
var _onClick = function () {
// My error is `this`, focus now on the click event, but I need the object itself
console.log(privateMethod())
console.log(self.publicMethod())
}
You have a scope issue, this in the onclick is pointing to a different object than what you expect. In your case it is the document
var that = this;
var _onClick = function () {
// My error is `this`, focus now on the click event, but I need the object itself
console.log(privateMethod())
console.log(that.publicMethod())
}
Running Example
My code is very simple. Ans to me it should work.
var preview = WinJS.Class.define(
function (el, options) {
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
this.preview.addEventListener('click', this.click, false);
//WinJS.Utilities.query("button", this.form)
//this.preview.addEventListener('', this.save, false);
},
{
click: function (e) {
this.form.style('display', 'block');
}
}
);
WinJS.Namespace.define('RegCtrl', { preview: preview });
But when click occurs this.form seems to be undefined of null. Why? I do not want to initialize objects in every method of the class.
New tests
I made additional test very small
var preview = WinJS.Class.define(
function (el, options) {
var test = 1;
this.test = 1;
this.test1();
},
{
test1: function () {
console.log(this.form, test);
}
}
);
WinJS.Namespace.define('RegCtrl', { preview: preview });
This test fails on line this.test1();. What I think now that this class is called RegCtrl.preview() rather than new RegCtrl.preview().
How do I shek inside the function that this called as new but not a simple function?
The other answers aren't explaining what's going on, and as such are giving incorrect advice.
JavaScript has first-class function objects - you can pass them around as values. That's exactly what you're doing when you set up this callback:
this.preview.addEventListener('click', this.click, false);
You're taking the contents of the this.click property, which happens to be a function, and handing it to the addEventListener function to do whatever it wants with it.
I was very specific about terminology there - note I specifically said function, not method. JavaScript doesn't really have a method construct, it just has methods as properties on an object.
So where does the "this" member come from? It's determined at the caller - the object you use on the left side of the '.' is the one that becomes the value of this. For example,
function exampleFunc() { console.log("this.myName = " + this.myName); }
var a = { myName: "Chris", doSomething: exampleFunc };
var b = { myName: "Bob", doSomething: exampleFunc };
Note I've assigned the exact same function to the doSomething properties. What what happens:
a.doSomething(); // Outputs "this.myName = Chris"
b.doSomething(); // Outputs "this.myName = Bob"
The exact same function object, called through two different objects, has a different this pointer.
exampleFunc is a global function, let's call it:
exampleFunc() // Outputs "this.myName = undefined"
So where'd the undefined come from? In a global function, "this" is set to window (the global scope), which didn't have the myName property defined. Which also means that you could do this instead:
myName = "Global Name"; // note, no var - we want this global
exampleFunc(); // Outputs "this.myName = Global Name"
Ok, so what's going on with the original question? Basically, you've passed the function this.click to be the callback, but you haven't passed the "this" pointer that you want it called through. Actually, addEventListener doesn't have a way to pass the this pointer. As a result, when the function is invoked this is not pointing at your object. I don't remember off the top of my head what it's pointing at - it's either window or the element that was clicked on, check the DOM documentation to verify.
To get it to call the right function with the right context (context = the correct "this"), the traditional approach is to use a closure. Capture "this" in a variable, then pass in an anonymous function that calls your actual callback with the right this pointer. The code looks like this:
var preview = WinJS.Class.define(
function (el, options) {
// Capture your current this pointer in a global variable
// Using "that" as the name comes from JavaScript: The Good Parts book
var that = this;
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
// Note what gets passed instead of this.click:
this.preview.addEventListener('click',
function (e) {
// NOTE: Calling through "that": "this" isn't pointing to the right object anymore
// Calling through "that" resets "this" inside the call to click
that.click(e);
}, false);
},
{
click: function (e) {
this.form.style('display', 'block');
}
}
);
This is a common enough pattern that ECMAScript 5 has a utility function to build these wrappers for you - function.bind. Do this:
this.preview.addEventListener('click',
this.click.bind(this),
false);
The construct this.click.bind(this) will construct a new function that, when called, will set the "this" reference to whatever you passed (in this case "this"), and then invoke the function you called it on.
Yes, there are a lot of different values for "this" floating around. Keeping track of what "this" is pointing at is an important part of mastering JavaScript programming.
I think you may want to define a global JavaScript variable as :
var myForm = document.getElementById('perview-form');
or jest define var myForm; and initialize inside function (el, options) as:
myForm = d.getElementById('perview-form');
Now you can use this variable in your function as :
myForm.style('display', 'block');
EDIT: I believe you may define this variable as first line in your WinJS.Class.define to make it instance level variable as below:
var preview = WinJS.Class.define(
var myForm;
function (el, options) {
....
....
myForm = d.getElementById('perview-form');
...
},
{
click: function (e) {
myForm.style('display', 'block');
}
});
This is a really hard thing to research if you don't know what to look for. I added one line and changed another line. That should fix your issue.
In short, the keyword this gets reset every time you enter a new function, this the value of this inside your click function is not the same this of the outer scope. Preserve this this you want. The name of that seems fairly common.
Edited based on the link provided by the OP.
This code is UNTESTED. If using this doesn't work now, then I'd try this2
Sorry I can't test this, but I don't have the framework anywhere so I'm doing
educated guesswork.
var preview = WinJS.Class.define(
function (el, options) {
that = this; // No var should be needed since it is declared already
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
this.preview.addEventListener('click', this.click, false);
//WinJS.Utilities.query("button", this.form)
//this.preview.addEventListener('', this.save, false);
},
// This is the section for instance vars
{
click: function (e) {
that.form.style('display', 'block'); // AND THIS ONE
},
that: null // Added instance variable
},
// And these are static variables
{
that2: null
}
);
Ok, so I thought I understood this (no pun intended), but apparently not.
var Constructor = function () {
var internalFunction = function () {
return this === window;
};
this.myMethod = function () {
alert(internalFunction());
};
};
var myObj = new Constructor();
myObj.myMethod();
This alerts true. Why can't the internal function see this as the object? Instead I have to use alert(internalFunction.call(this)); in myMethod.
Edit: I was looking for an explanation as to why this is assigned in that way, not workarounds such as var self = this;, etc. Sorry if I didn't make that clear.
this is not bound until the function is called and is dependent on how the function is called. You could think of it as an extra parameter implicitly passed to the function.
In this case, the problem is that you're calling internalFunction using internalFunction(). The this value is set either by calling a function as a method (as in foo.bar() or foo["bar"]()) or by setting this explictly via call() or apply(). Your call is doing neither so this reverts to the global object.
The simplest way to achieve what you want in this case while keeping internalFunction private is to store a reference to this inside the constructor function:
var Constructor = function() {
var thisObj = this;
var internalFunction = function () {
return thisObj === window;
};
thisObj.myMethod = function () {
alert(internalFunction());
};
}
Because of functional scoping rules, this is reassigned inside each function... I would store a copy of your object as self and use it accordingly...
var Constructor = function () {
var self = this;
var internalFunction = function () {
return self === window;
};
this.myMethod = function () {
alert(internalFunction());
};
};
var myObj = new Constructor();
myObj.myMethod();
Should give you the output you expect.
SIDENOTE
This is a fairly precarious practice that javascript has created, mainly because if you forget the new keyword when using Constructor, you will get this referring to the window (god) object so you'll be attaching myMethod to the window without warning.
There are five ways to call a function in JavaScript. The value of this depends on which you choose:
Global function call (e.g. myFunction()). No explicit value for this is given. The value of this will be the default object (window in a browser).
Method call (e.g. obj.myFunction()). The value of this is the object on which the method was invoked (obj in this case).
Using the call method (e.g. myFunction.call(obj)). The value of this is provided explicitly (in this case obj).
Using the apply method (e.g. myFunction.apply(obj)). The value of this is provided explicitly (in this case obj).
Constructor function (e.g. new MyFunction()). The value of this is a newly-created object provided by the runtime.
Each of the five is explained in more detail here:
http://devlicio.us/blogs/sergio_pereira/archive/2009/02/09/javascript-5-ways-to-call-a-function.aspx
Its a scope issue try something like:
var Constructor = function () {
var $this = this;
var internalFunction = function () {
return $this === window;
};
this.myMethod = function () {
alert(internalFunction());
};
};
var myObj = new Constructor();
myObj.myMethod();
In the following code:
function xyz(x) {
//something
this.x = x;
}
xyz.prototype = {
a: function () {
//do something
},
b: function () {
//pre
this.a();
//post
}
}
the call of this.a() gives the warning of method not supported. So I tried using xyz.prototype.a.call(this) instead. But it does not maintain the value of x. What do I do to call one method of a class from other?
Given your code, if you write:
var myXyz = new xyz("hello");
then calling
myXyz.b();
should correctly get to the "a()" function on the prototype. However, if you do something like this:
var otherB = myXyz.b;
otherB();
then it will not work, because there's no context object (that is, the this value inside "b()" won't be set correctly to an instance of "xyz"). That often happens when a function is being used as an event handler:
something.onclick = myXyz.b;
The event handler, when called, won't have an "xyz" instance to work with. Instead of that, therefore, you could write:
something.onclick = function() { myXyz.b(); };
which clearly ensures that there's an "xyz" object.