I have a Problem accessing an Object function using "this".
In case of the below example (which is simplified, because I cannot supply actual code due to various reasons) the function call this._getEntry() is "undefined" when calling
createList().
I would hope for some opinions on wether this is due to a misunderstanding of
javascript closures or rather a syntax error.
In the latter case I will have to find the error in the actual code myself.
If it is a misunderstanding of javascript or dojo concepts I would really appreciate
some help on how to correctly scope and access the below mentioned function (_getEntry()).
var OBJECT = {
_getEntry : function(entry){
var li = document.createElement('LI');
li.appendChild(document.createTextNode(entry));
return li;
},
createList : function(entryArray){
var list = document.createElement('UL');
dojo.forEach(entryArray,function(entry){
list.appendChild(this._getEntry(entry));
});
dojo.body().appendChild(list);
}
};
OBJECT.createList(["entry1","entry2"]);
thanks!
Firstly, I think your pasted code is missing ); to complete the forEach.
Secondly, forEach takes an optional third parameter which determines the context in which the passed function runs. If not given, it defaults to the global scope, so yes this is your problem. Assuming this already refers to what you need it to immediately outside the forEach, you should be able to just pass this in as the third argument to forEach and it should work, e.g.:
dojo.forEach(entryArray, function(entry){
list.appendChild(this._getEntry(entry));
}, this);
For more information: http://dojotoolkit.org/api/dojo/forEach - which is based on the API in JS 1.6 https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
This is a common problem and you are right, its all about scoping and closure. What is happening here is once you get into the forEach the context changes to the global context (window). Handily, Dojo provides a way to set this context:
createList : function(entryArray){
var list = document.createElement('UL');
dojo.forEach(entryArray,function(entry){
list.appendChild(this._getEntry(entry));
}, this);
dojo.body().appendChild(list);
}
An alternate approach is to use closure to get at this
createList : function(entryArray){
var list = document.createElement('UL');
var _this = this; // closure allowing the forEach callback access to this
// some popular variable names used for this include:
// $this, _this, me and that
dojo.forEach(entryArray,function(entry){
list.appendChild(_this._getEntry(entry));
});
dojo.body().appendChild(list);
}
Related
I am a newbies to Javascript and recent need to support for it. May anyone help me to understand the following coding?
var proj = proj || {};
(function () {
var functA = function () {
var base = {}; // I don't understand from this line
base.getString = this.getString;
this.getString = function () {
var strings = base.getString.call($this);
return strings.concat(["ab","cd","ef"]);
};
};
})();
I am quite confused with the getString function. Can anyone kindly explain the purpose of it? Also, is there any name for such kind of coding style or can anyone lead me to some articles about this style of coding?
Many thanks.
In Javascript, curly braces define both scope (as in method definitions) and object types (what Ruby people call hashes and Python people call dictionaries). The line you're confused about is declaring a variable to be an empty object.
After that, the programmer is adding a function to their new object. Javascript throws references around like crazy, so the programmer has set up base.getStrings as an alias pointing to the getStrings function they define later.
As for the purpose of the function itself, my best guess is that it's generating test data, but without understanding the entire code base it's impossible to say,
The line var strings = base.getStrings.call($this) seems to be seeding the method with strings from another scope. I can't know what $this refers to, but intuition tells me that it is a reference to the this of an outer scope, which also has a getStrings method.
The call() method executes the function and forces resolution of the this keyword to the first parameter, in this case $this. Since base.getStrings is aliased to this.getStrings, the programmer can use this trick to force the call to $this.getStrings, avoiding an infinite recursion.
This looks like a super-call in a mixin pattern to me. Imagine that this and $this refer to an object that already has a getString method that returns an array of strings.
Now, when functA is called on that object, it will create a base object, and store the original this.getString function on it. Then, it will overwrite the this.getString method with an own definition.
This new version of the method will call the original function (which is referred to as base.getString) on the $this object, and return its result but slightly modified (extended by the "ab","cd","ef" array elements).
var base = {}; // I don't understand this line
It's not strictly necessary actually. One could equally have just directly stored the original function in a variable, there is no need to create this base object:
function functA() {
var original_getString = this.getString;
this.getString = function new_getString() {
var strings = original_getString.call(this);
return strings.concat(["ab","cd","ef"]);
};
}
How do i edit property variables from within jquery as shown in the example below:
function Timetable(){
this.timetables=[];
//get timetable data for currentUser
$.get('http://someurl.com/api',function(data){
this.timetables.concat($.parseJSON(data));
});
}
What seems to happen is that the this.timetables seems to be out of the variable scope.
You don't need to use this, you need to create a closure.
function timetable(){
// create a closure
var timetables = Array();
//get timetable data for currentUser
$.get('http://someurl.com/api',function(data){
// this references the Array created above
that.timetables = that.timetables.concat($.parseJSON(data));
});
}
It just occurred to me that you may be using timetable to create objects. As a side note, if that's the case, the convention is to call it Timetable for clarity. If that's the case, use something like this.
function Timetable () {
// store a reference to the scope
var that = this;
// create an instance property of the Timetable object
this.timetables = [];
$.get('http://someurl.com/api',function(data){
// use our scope reference captured above
// to update the timetables property
that.timetables = that.timetables.concat($.parseJSON(data));
});
}
var t = new Timetable();
// once the get call is complete
console.log(t.timetables);
It's because inside your callback function, the scope of this is not the same. It now refers to the object bound to your callback function ($ in this case).
Earlier answers to your question already advised you on a concrete way out of your trouble, so I'm not of much help there.
But if you want a (very) detailed and absolutely worth-reading answer about this topic in a broader sense, you should definitely read this post (see the accepted answer).
This other one about closures (what you seem to need here) could also be of great help.
Force the scope.
var scope = this;
Then you can use scope inside instead of "this" from jQuery
Here is what I want:
var Validator = function () {
this.do = function () {
alert(INTANCENAME); /// SHOULD BE 'FOO'
}
}
var FOO = new Validator().do();
Is it possibe to implement in Javascript?
The truth is there is no point of doing that, the only way I can hardly think is to loop all window or scope objects and check some kind of equality with the current object, something like
this.do = function () {
for(var key in window) {
if(this === window[key]) {
alert(key);
}
}
};
In order to work call it after you assign it.
var FOO = new Validator();
FOO.do();
Another issue that can come up is that an instance (a reference) can be stored in various variables so maybe will not get what you expect.
The literal answer to your question would be:
Use (new Error()).stack to get information on the line and the function where the do() method was called.
Use JS parser (e.g. Esprima) to find out what variable it was called on (if any, the method itself might be assigned to a variable).
I do not recommend doing this though.
There's no way to directly do what you're asking for here. Objects themselves are not in any defined by their matching variable name - in fact it's possible to have objects that exist that are not directly assigned to a variable, and multiple variables assigned to the same object.
The javascript interpreter uses our variable names as identifiers to help with the code execution, but once it's running the variable name makes no difference to the running javascript program, as it's probably been reduced to a memory reference by the time it's executing, completely separated from the original code that you wrote.
Edit: Answer by yannis does kind of simulate this, but it relies on working with variables available in a specific scope - what I ment was that there's no direct way to do this from within the object itself as per your example in the question.
I am a relatively experienced c# (and before that c++ Win32) developer, I am new to javascript and have a question regarding the this pointer.
I am using knockout.js, and one function called subscribe accepts a this variable, that will be set inside the callback function.
From my way of thinking from the Win32 days and C#, on any callback function i want a scope object which contains my state.
In this case I have use the this javascript thing to set my callback scope.
My questions are:
Now everything works (full fiddle here if you are
interested), but have I done something terrible?
Is there any reason this is used instead of passing in an explicit
scope variable as a parameter (that would make things easier to understand as
for me, this makes the workings kind of hidden).
What is the intended use for this?
From http://knockoutjs.com/documentation/observables.html it says:
The subscribe function accepts three parameters: callback is the function that is called whenever the notification happens, target (optional) defines the value of this in the callback function, and event (optional; default is "change") is the name of the event to receive notification for. Example below
myViewModel.personName.subscribe(function(oldValue) {
alert("The person's previous name is " + oldValue);
}, null, "beforeChange");
My code snippet below:
var computedOptions = createComputedDepdency(viewModel[option.requires.target],option.data);
viewModel[option.optionsName] = computedOptions;
console.log("making callback scope object for: " + option.optionsName );
var callbackScope = {
callbackName: option.optionsName,
options: computedOptions,
selectedValue: viewModel[option.selectedName]
};
// when the list of available options changes, set the selected property to the first option
computedOptions.subscribe(function () {
var scope = this;
console.log("my object: %o", scope);
scope.selectedValue(scope.options()[0].sku);
console.log("in subscribe function for..." + scope.callbackName);
},callbackScope);
First a semantic note:
The scope of a function is not related to this word. The context is related to this. The scope is related to the accessibility of variables and functions inside another function.
When you try to read a variable outside the function where it's declared, then you trying to access to a var outside its scope. So you cannot do it because the var is inside a scope not accessible from current position.
Now everything works (full fiddle here if you are interested), but have I done something terrible?
If it works, it's not so terrible :-)
Is there any reason this is used instead of passing in an explicit scope variable as a parameter (that would make things easier to understand as for me, this makes the workings kind of hidden).
a fast read: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
In javascript the value of this is determined by how a function is called.
In one way this approach could save annoying passage of context as argument: in a well documented library, the this use is very intituitive.
In other cases, I agree when you change continually context in your app without a rigorous logic, it could be confused.
What is the intended use for this?
We should always remember how and when the javascript is born. It was born for browser in order to interact with the DOM.
For this purpose, the context has sense that change based of which element call the function.
For example:
var divs = document.getElementsByTagName('DIV');
for(var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click',_clickHandler);
}
function _clickHandler() {
this.innerHTML = "clicked";
}
DEMO http://jsfiddle.net/AYBsL/1/
This is an example to how is useful the implicit change of context in javascript.
You could do this also for user-defined function: when you call a function you could change the context:
_clickHandler.call(divs[0]); // simulate click of first div
In javascript 'this' refers to the object that called your function. Only in a situation when you use 'new' keyword you can expect it to point to the current object (function).
var MyObject = function () {
this.hello = function () { console.log(this); }
}
var instance = new MyObject();
There is a way to make sure that this is always what you expect and that is creating a variable to store the correct reference for you and use that instead of this... in your example it would be similar to this...
computedOptions = function () {
var that = this;
}
computedOptions.subscribe(function () {
console.log("my object: %o", scope);
scope.selectedValue(that.options()[0].sku);
console.log("in subscribe function for..." + that.callbackName);
},callbackScope);
MDN JavaScript reference would inevitably explaing it more better then myself, have a look at it.
You shouldn't mix scope and this. this is supposed to mimic classical-oop languages like java or++, that is to keep the reference to an instance object. But it can be used just to execute arbitrary function on a given context using .apply() or .call.
What about scope, you don't have to do anything to pass the scope to a function, since the outer scope becomes automatically accessible inside function. You should read about closures - it's the best part of javascript.
I have this code:
PageList: function(url, index, classes){
this.url = url;
this.index = index;
...
};
PageList.prototype.toHTML = function(){
var div = $('<div class="container"></div>');
var p = $('<p></p>');
var link = $('<a></a>');
$.each(this.elements, function(index, array_value){
console.log(this.url);
...
}
}
And it worked as expected.
The problem was that console.log(this.url) was printing undefined, so I reworked the code to look like this:
PageList.prototype.toHTML = function(){
var div = $('<div class="container"></div>');
var p = $('<p></p>');
var link = $('<a></a>');
var instance = this;
$.each(this.elements, function(index, array_value){
console.log(instance.url);
}
}
I know that the problem was on the closure not taking this as the value of the instance, but as far as i know a reference to this inside a function that doesn't have an instance bound to it must refer to the window object, instead of undefined, at least that's the case on many of the browsers out there.
So what exactly is going on on my code.
Note: I'm using jQuery and this.elements is already defined.
Edit: Now im figuring out that $.each is a non-instance function, so my callback is being called from $.each but it must be window the reference to this, still thinking about it.
According to the jQuery docs for $.each:
The value [of the current element] can also be accessed through the this keyword...
In JavaScript, when you hand off a callback function to a higher-order function (in this case, $.each), the higher-order function can decide what the value of this will be when the callback runs. There is no way for you to control this behavior -- simply don't use this (e.g., by using a reference like instance in your example or via a closure).
Check out the context-setting functions Function.call and Function.apply to understand how a higher-order function like $.each sets the this context of a callback. Once you read those MDN pages, it might clear a few things up.
Here's a quick example:
Array.prototype.forEachWithContext(callback, this_in_callback) {
for(var i = 0; i < this.length; ++i) {
callback.call(this_in_callback, i, this[i]);
}
}
And to use it:
PageList.prototype.toHTML = function(){
//...
this.elements.forEachWithCallback(function(index, array_value){ ... }, this);
}
My example Array.forEachWithContext is similar to Array.forEach. However, it takes a callback and a second argument that is used as the value of this during the execution each of those callbacks.
Try wrapping your $.each function with a $.proxy like this...
$.each(this.elements, $.proxy(function(index, array_value){
console.log(this.url);
},this));
The $.proxy will ensure that this references your PageList...
I know that the problem was on the closure not taking this as the value of the instance, but as far as i know a reference to this inside a function that doesn't have an instance bound to it must refer to the window object, instead of undefined, at least that's the case on many of the browsers out there.
this is window. You're printing window.url, which is undefined. Try console.log(this), and it should yield window.