Changing <this> in object literal - javascript

I'm creating an object literal and I want to use the reserved word "this". The problem I'm having is that the "this" points to the window object in an object literal. I know the this points to the current object when used in a constructor function. Is there a way to override it so that "this" points to my object literal?
main = {
run: function()
{
var elements = [];
var allElements = document.querySelectorAll("*");
for(var i = 0; i < allElements.length; i++)
{
if(allElements[i].nodeType != 3)
{
elements.push(allElements[i]);
}
}
for(var i = 0; i < elements.length; i++)
{
// Doesn't work
// this.parseElement(elements[i]);
// Works
main.parseElement(elements[i]);
}
},
parseElement: function(e)
{
// Unimportant code
}
}
(function()
{
main.run();
})();

The thing you claim works in your question doesn't work:
var main = {
run: (function()
{
var elements = [];
var allElements = document.querySelectorAll("*");
for(var i = 0; i < allElements.length; i++)
{
if(allElements[i].nodeType != 3)
{
elements.push(allElements[i]);
}
}
for(var i = 0; i < elements.length; i++)
{
// Doesn't work
// this.parseElement(elements[i]);
// Works
main.parseElement(elements[i]);
}
})(),
parseElement: function(e)
{
// Unimportant code
}
};
<div></div>
Fundamentally, you cannot refer to the object being constructed from within the object initializer. You have to create the object first, because during the processing of the initializer, while the object does exist no reference to it is available to your code yet.
From the name run, it seems like you want run to be a method, which it isn't in your code (you've edited the question now to make it one). Just remove the ()() around the function:
var main = {
run: function() {
var elements = [];
var allElements = document.querySelectorAll("*");
for (var i = 0; i < allElements.length; i++) {
if (allElements[i].nodeType != 3) {
elements.push(allElements[i]);
}
}
for (var i = 0; i < elements.length; i++) {
this.parseElement(elements[i]);
}
},
parseElement: function(e) {
console.log("Parsing " + e.tagName);
}
};
main.run();
<div></div>
Since this is set by how the function is called for normal functions, if you want run to be bound to main so that it doesn't matter how it's called, using main instead of this is the simplest way to do that in that code.
But if you don't want to use main, you could create a bound function:
var main = {
run: function() {
var elements = [];
var allElements = document.querySelectorAll("*");
for (var i = 0; i < allElements.length; i++) {
if (allElements[i].nodeType != 3) {
elements.push(allElements[i]);
}
}
for (var i = 0; i < elements.length; i++) {
this.parseElement(elements[i]);
}
},
parseElement: function(e) {
console.log("Parsing " + e.tagName);
}
};
// Bind run
main.run = main.run.bind(main);
// Use it such that `this` would have been wrong
// if we hadn't bound it:
var f = main.run;
f();
<div></div>
Just as a side note, we can use Array.prototype.filter and Array.prototype.forEach to make that code a bit more concise:
var main = {
run: function() {
var allElements = document.querySelectorAll("*");
var elements = Array.prototype.filter.call(allElements, function(e) {
return e.nodeType != 3;
});
elements.forEach(this.parseElement, this);
},
parseElement: function(e) {
console.log("Parsing " + e.tagName);
}
};
// Use it
main.run();
<div></div>
That assumes that parseElement only ever looks at the first argument it's given (since forEach will call it with three: the entry we're visiting, its index, and the object we're looping through).

Related

Object method doesn't get run in for loop

In this code, I create an array of objects and attempt to loop through them calling a method on each one. The method is part of the objects' prototype.
Here's the setup:
function TestObj(name) {
this.name = name;
}
TestObj.prototype.speak = function() {
console.log(this.name);
};
var myArray = [
new TestObj('first'),
new TestObj('second')
];
I know I can access the method correctly because this gives the proper output:
myArray[0].speak(); // displays "first"
However, both of the loops I tried won't output anything:
for (var i = 0; i < myArray.length; i++) {
myArray[i].speak();
}
for (var key in myArray) {
key.speak();
}
I your second loop key.speak(); to myArray[key].speak();
for (var key in myArray) {
myArray[key].speak();
}
function TestObj(name) {
this.name = name;
}
TestObj.prototype.speak = function() {
console.log(this.name);
};
var myArray = [
new TestObj('first'),
new TestObj('second')
];
for (var i = 0; i < myArray.length; i++) {
myArray[i].speak();
}
for (var key in myArray) {
myArray[key].speak(); /*change here in your code*/
}

React native trouble calling function within another function?

in my React-native app I am trying to call another function within my listenForItems function, but keep getting the error this.populateArray is not a function. In 'this.populateArray(solutions)', this.populateArray is undefined. I do this in other classes and it's working, but for some reason it's not working here. Is there anything I'm missing?
populateArray: function(solutions) {
var completed = [];
var inProgress;
for (var i = 0; i < solutions.length; i++ ) {
if (solutions[i].completed == 0) {
inProgress = solutions[i].id;
}
else {
completed.push(solutions[i].id);
}
}
},
listenForItems: function(cluesRef) {
var solutions = [];
userSolutionsRef.orderByChild('user_id').startAt(0).endAt(0).once('value', function(snap){
var solution = snap.val();
for (var i = 0; i < solution.length; i++) {
if (solution[0].hunt_id == 0) {
solutions.push(solution[0]);
}
}
this.populateArray(solutions);
});
},
The classic this scope issue of javascript. Google will help with better understanding. In short, the word "this" inside a function refers to that function. In this example it refers the anonymous function (callback) that you use in userSolutionsRef.orderByChild. There are many ways to solve this. You can use ES6(ES2015) arrow functions in which case it becomes something like
userSolutionsRef.orderByChild('user_id').startAt(0).endAt(0).once('value', (snap) => {
var solution = snap.val();
for (var i = 0; i < solution.length; i++) {
if (solution[0].hunt_id == 0) {
solutions.push(solution[0]);
}
}
this.populateArray(solutions);
});
or es5 solution
var that = this;
userSolutionsRef.orderByChild('user_id').startAt(0).endAt(0).once('value', function(snap){
var solution = snap.val();
for (var i = 0; i < solution.length; i++) {
if (solution[0].hunt_id == 0) {
solutions.push(solution[0]);
}
}
that.populateArray(solutions);
});

JavaScript Json row undefined for TreeView

I have a question. When querying my Json object, although i have my Field as same name and all Json rows available give me an error that is undefined.
//e.g: row.DepParentId
Bellow is my code. Am I missing some tag?
function convert(rows) {
debugger;
function exists(rows, parent) {
for (var i = 0; i < rows.length; i++) {
if (rows[i].DepId === parent) return true;
}
return false;
}
var nodes = [];
// get the top level nodes
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
if (!exists(rows, row.DepParentId)) {
nodes.push({
id: row.DepId,
name: row.Title
});
}
}
var toDo = [];
for (var i = 0; i < nodes.length; i++) {
toDo.push(nodes[i]);
}
while (toDo.length) {
var node = toDo.shift();
// the parent node
// get the children nodes
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
if (row.DepParentId == node.Id) {
var child = {
Id: row.DepId,
Name: row.Title
};
if (node.options) {
node.options.push(child);
} else {
node.options = [child];
}
toDo.push(child);
}
}
}
return nodes;
}
My Json example for one row picked from Firefox
"{"DepId":7,"DepParentId":3,"Title":"OTT"}"
Thanks for helping
Joao
thanks all for helping, it's working now, the problem was the JSON.stringify() was returning only an [Object] so when you use JSON.parse() generates a sintax error.
function onLoaded() {
var itemsCount = items.get_count();
for (var i = 0; i < itemsCount; i++) {
var item = items.itemAt(i);
var dep = JSON.stringify(item.get_fieldValues());
deps.push(dep);
}
So, i had to change the format to be valid to parse like this
var dt = "[" + deps + "]";
var ret = JSON.parse(dt);
Thanks
Joao

returning a value after for loops

So, I have been trying for the past few hours to get an result out of a function after performing some for loops :
Cluster.prototype.initiate_api_data_fetching = function(username) {
var self = this,
object = [];
return self.initiate_available_market_search(username, function(data_object){
var json_obj = JSON.parse(data_object);
for(var obj_key in json_obj) {
for (var i = json_obj[obj_key].length - 1; i >= 0; i--) {
self.initiate_market_items_data_fetching(username, json_obj[obj_key][i].site, function(data_obj){
var json_object = JSON.parse(data_obj);
for(var data_key in json_object) {
for (var j = json_object[data_key].length - 1; j >= 0; j--) {
object.push(json_object[data_key][j]);
/*log(object);*/
};
};
log(object);
});
};
};
});
};
Making abstraction of all the variables and other things that make no sense to you readers, I would just like to know how can I return the object array with the data that I\m pushing in it. Everything is fine if I\m logging where the /*log(object);*/ is, but if I want to see what the object contains at the end of the function, I get an empty array.
I suggest you add a callback to your main function and call it when done..
Cluster.prototype.initiate_api_data_fetching = function (username, callback) {
var self = this,
object = [];
return self.initiate_available_market_search(username, function (data_object) {
var json_obj = JSON.parse(data_object)
, counter = 0;
function done() {
counter -= 1;
if (counter === 0) {
callback(object);
}
}
for (var obj_key in json_obj) {
if (!json_obj.hasOwnProperty(obj_key)) { continue; }
for (var i = json_obj[obj_key].length - 1; i >= 0; i--) {
counter += 1;
self.initiate_market_items_data_fetching(username, json_obj[obj_key][i].site, function (data_obj) {
var json_object = JSON.parse(data_obj);
for (var data_key in json_object) {
if (!json_object.hasOwnProperty(data_key)) { continue; }
for (var j = json_object[data_key].length - 1; j >= 0; j--) {
object.push(json_object[data_key][j]);
/*log(object);*/
}
}
done();
});
}
}
});
};
PS. 1 assumption is that initiate_api_data_fetching is async.
PS. 2 Follow the advice from the commenters above to improve your code. I answered your immediate question by showing you how to synchronise async calls, but don't stop there.

Accessing variables w/in complete function

for (var i = 0; i < 32; i++) {
var thisId = dropId+i;
$("#p"+thisId).animate({ left:"+=32px" }, function(){
if ($("#p"+thisId).position().left == 1024) {
$("#p"+thisId).remove();
window.console.log("removed");
}
});
}
In the above code example, by the time I get around to executing animate's complete function, thisId represents the last assigned value from the for loop NOT the value that I wanted to pass in for each iteration of the loop. Is there a way to get it to access the correct thisId?
JavaScript does not have block scope. You can create a new scope by calling a function. E.g.
for (var i = 0; i < 32; i++) {
(function(thisId) {
$("#p"+thisId).animate({ left:"+=32px" }, function(){
if ($("#p"+thisId).position().left == 1024) {
$("#p"+thisId).remove();
window.console.log("removed");
}
});
}(dropId+i)); // <-- calling the function expression and passing `dropId+i`
}
Variables declarations area always hoisted to the top of the function. So even if you have the declaration inside the loop, it is actually the same as:
var i, thisId;
for(...) {
thisId = dropId + i;
//...
}
Every closure you create inside the loop references the same thisId. It's like in Highlander: "There can be only one."
You need to use a closure around the current thisId.
for (var i = 0; i < 32; i++) {
var thisId = dropId+i,
complete = (function(id) {
return function() {
if ($("#p"+id).position().left == 1024) {
$("#p"+id).remove();
window.console.log("removed");
}
}
}(thisId));
$("#p"+thisId).animate({ left:"+=32px" }, complete);
}
Just wrapping what you had in an anonymous function should work:
for (var i = 0; i < 32; i++) {
(function() {
var thisId = dropId+i;
$("#p"+thisId).animate({ left:"+=32px" }, function(){
if ($("#p"+thisId).position().left == 1024) {
$("#p"+thisId).remove();
window.console.log("removed");
}
});
})();
}

Categories

Resources