My JavaScript object is:
var MyObject = {
DOM: function(tagName){
if(!this.isElement){
var found = document.getElementsByTagName(tagName);
this.isElement = true;
for(i in found)
this[i] = found[i];
} else{
var found = this[0].getElementsByTagName(tagName);
for(i in found)
this[i] = found[i];
}
return this;
}
}
It works perfectly:
MyObject.DOM("div");
The problem is that when I log the object again:
MyObject.DOM("div");
console.log(MyObject);
The problem is that it logs:
> Object {0: div#lower.cl, 1: div.higher, find: function, isElement: true}
But actually I want it to log:
> Object {find: function}
So I don't want it to keep the found elements when I run the MyObject again.
So really I just want to reload the object every time I use it.
Here's one way you can implement this. It's best if you stick to keeping your objects as immutable as possible. You're trying to use one instance of one object to do everything, and that won't work:
function MyObject() {
this.length = 0;
}
MyObject.prototype.DOM = function (tagName) {
var found = new MyObject(),
batch,
toSearch,
i,
j,
z = 0;
if (this === MyObject) {
toSearch = [document];
} else {
toSearch = Array.prototype.slice.call(this);
}
for (i = 0; i < toSearch.length; i += 1) {
batch = toSearch[i].getElementsByTagName(tagName);
for (j = 0; j < batch.length; j += 1) {
found[found.length] = batch[j];
found.length += 1;
}
}
return found;
}
MyObject.DOM = MyObject.prototype.DOM;
http://jsfiddle.net/Sygdm/
You can add sort of 'private' fields to your classes like so:
var MyObject = (function() {
var instance = {};
return {
DOM: function(tagName){
if(!instance.isElement){
var found = document.getElementsByTagName(tagName);
instance.isElement = true;
for(i in found)
instance[i] = found[i];
} else{
var found = instance[0].getElementsByTagName(tagName);
for(i in found)
instance[i] = found[i];
}
return this;
}
}
})();
Not sure if that is what you want, though.
Related
I have the following code:
function checkIfUnitCostItemsAreZero(finaliseIDList)
{
var selectedItems = finaliseIDList;
selectedItems = $.makeArray(selectedItems); //array is ["-2,-3"]
var getItem = $("#builderItemsList .listItem");
for (i = 0; i < getItem.length; i++)
{
var currentItem = ($(getItem[i]).attr("key"));
if (currentItem.indexOf(selectedItems) > -1)
{//currentItem = "-2" then "-3"
var unitCost = $(getItem[i]).attr("unitcost");
console.log(unitCost);
unitCost = parseFloat(unitCost);
if(unitCost==0.00)
{
return true;
break;
}
}
}
return false;
}
selected item currently equates to the following:
selectedItems = ["-2,-3"]
And further down, currentItem is evaluated at:
"-2", and the next loop "-3".
In both instances, neither go into the if statement. Any ideas why?
Courtesy of Hossein and Aer0, Fixed using the following:
String being passed in as a single value. Use split to seperate it for array.
Modify the if clause.
function checkIfUnitCostItemsAreZero(finaliseIDList)
{
var selectedItems = $.makeArray(finaliseIDList.split(','));
var getItem = $("#builderItemsList .listItem");
for (i = 0; i < getItem.length; i++) {
var currentItem = ($(getItem[i]).attr("key"));
if (selectedItems.indexOf(currentItem) > -1)
{
var unitCost = $(getItem[i]).attr("unitcost");
unitCost = parseFloat(unitCost);
if(unitCost==0.00)
{
return true;
break;
}
}
}
return false;
}
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).
Im learning JS OOP from Stoyan Stefanov's book. I got problem with exercise 4 in chapter 4:
Imagine the String()constructor didn't exist. Create a constructor
function MyString()that acts like String()as closely as possible.
You're not allowed to use any built-in string methods or properties,
and remember that String()doesn't exist. You can use this code to test
your constructor:
Below is my attempt for creating String split() like method. Could you guide me how to make it work ?
function MineString(string){
this.lengthS = string.length;
//this[1] = "test";
for(var i = 0; i < string.length;i++){
this[i] = string.charAt(i);
}
this.toString = function(){
return string;
}
this.split = function(char){
var splitedArray = [];
var foundedChar = [];
var string2 = [];
for (var i = 0; i < this.lengthS ; i++){
foundedChar.push(string[i].indexOf(char));
}
for (var j = 0; j < foundedChar.length; j++){
if(foundedChar[j] === -1){
//splitedArray[0] += string.charAt(j);
//splitedArray[j] = string.charAt(j);
string2 += string.charAt(j);
//splitedArray.push(string2);
splitedArray[foundedChar.length] = string2;
}else if (foundedChar[j] === 0){
//splitedArray.push(string.charAt(j));
}
}
return splitedArray;
}
}
var text = new MineString("hello");
text.split("e");
So text.split("e"); should display something like this:
var text = new MineString("hello");
text.split("e");
["h","llo"]
Your split method looks somehow overly complicated. I simplified it and rewrote the other parts of your class so that they adhere to the task of not using string methods. See jsfiddle or the code below.
New JS-Code:
function MineString(str){
this.str = str;
this.addChar = function(c) {
this.str += c;
}
this.length = function() {
return this.str.length;
}
this.toString = function(){
return this.str;
}
this.split = function(char){
var out = [],
current = new MineString(""),
addCurrent = function() {
if (current.length() > 0) {
out.push(current.toString());
current = new MineString("");
}
};
for (i = 0; i < this.str.length; i++) {
if (this.str[i] == char) {
addCurrent();
} else {
current.addChar(this.str[i]);
}
}
addCurrent();
return out;
}
}
var text = new MineString("hello");
console.log(text.split("e"));
Outputs:
["h", "llo"]
I suppose to copy a object source, while copy changes, sourceshould not change.The source code goes as follow:
layoutTreemap: function(source) {
var copy = jQuery.extend(true,{},source);
var select_polygon = this.get_selected_polygon();
var vt = d3.layout.voronoitreemap()
var layoutNodes = vt(copy);
return layoutNodes;
}
d3.layout.voronoitreemap = function() {
var hierarchy = d3.layout.hierarchy().sort(null),
root_polygon = [[0,0],[500,0],[500,500],[0,500]],
iterations = 100,
somenewvariable = 0;
function voronoitreemap(d, depth) {
var nodes = hierarchy(d),
root = nodes[0];
root.polygon = root_polygon;
root.site = null;
if (depth != null){
max_depth = depth;
}
else{
max_depth = "Infinity";
}
computeDiagramRecursively(root, 0);
return nodes;
}
function computeDiagramRecursively(node, level) {
var children = node.children;
if(node.parent) node.parent = null;
if (children && children.length && level < max_depth) {
node.sites = VoronoiTreemap.init(node.polygon, node); // can't say dataset, how about node?
VoronoiTreemap.normalizeSites(node.sites);
VoronoiTreemap.sites = node.sites;
VoronoiTreemap.setClipPolygon(node.polygon);
VoronoiTreemap.useNegativeWeights = false;
VoronoiTreemap.cancelOnAreaErrorThreshold = true;
var polygons = VoronoiTreemap.doIterate(iterations);
// set children polygons and sites
for (var i = 0; i < children.length; i++) {
children[i].polygon = polygons[i];
children[i].site = VoronoiTreemap.sites[i];
computeDiagramRecursively(children[i], (level + 1));
}
}
}
....
return d3_layout_hierarchyRebind(voronoitreemap, hierarchy);
}
But after execute vt(copy), the source has been changed.
This gives issues
root.polygon = root_polygon;
children[i].polygon = polygons[i];
You are copying arrays you think. but actually you are copying the reference to the array. now you have two pointers to the same object(imagine two people using the same fork at dinner)
You need to change this into
root.polygon = root_polygon.slice();
children[i].polygon = polygons[i].slice();
this way the array gets copied instead of referenced. Now each dinner guest has its own fork.
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.