Calling object as function - javascript

I have a class defined like:
function S(items) {
this.items = items;
this.item = function(pos) {
return this.items[pos];
}
}
To retrieve the items, I normally do,
var s = new S([10, 20, 30]);
console.log(s.item(0));
console.log(s.item(1));
I want to change the way I access the items to be like:
var s = new S([10, 20, 30]);
console.log(s(0));
console.log(s(1));

You can only call functions. The most close way to do what you are trying to accomplish is to make your function S return another function that receives the position as a parameter and returns the item in that position. And this way you'd need to stop using the new keyword.
function S(items) {
return function(pos) {
return items[pos]
}
}
var s = S([10, 20, 30])
console.log(s(0))
console.log(s(1))
Anyway, I can't see any good reason of why you would like to do that. If you are trying to freeze an array so it can't be modified, then I think using Object.freeze would be a better choice.
FIDDLE: http://jsfiddle.net/84wVU/

You can't.
But you can come close.
function S(items) {
var l = items.length, i;
for( i=0; i<l; i++) this[i] = items[i];
}
var s = new S([10,20,30]);
console.log(s[0]); // note square brackets
Only functions can be called with ().

Although you can indeed loop through the items and take each item in the array and attach it to the class as this[index] = items[index]. Another option is to extend the Array prototype to attach your current functions to it:
Array.prototype.item = function(pos){
return this[pos];
}
Now you can call the item function on arrays like so :
[1,2,3,4].item(1) // will return 2
[1,2,3,4].item(2) // will return 3
and so on..

Related

converting JS array with objects to HTML collection

I got to the point with my project where I decided to simplify some of the js functions where I am looking for a parent in a DOM tree, then drill down to the elements many many times in one function. instead I though I will make instances of a function which will keep some data so then I can refer and operate on objects in easy way. I got it working but as I was going along, I decided to extend functionality and add some extra functions like getElementsByClassNameThenTagName.
I loop through the arrays and if add matching elements to the array.
I have noticed (sadly only now) that I am creating an array with elements rather than HTML collection. As a results, I cannot refer to the objects in my findings by typing buttons['reset'].disabled = false;. I can access my reset button by buttons[3].disabled = false; but this would cause a lot of inconvenience.
I am therefore looking for a way to convert my array with object into a HTML collection.
Please see below my current function:
this.getElementsByClassNameThenTagName = function (elementClass, elementTag) {
if (parentNode == null) {
this.init();
}
var results = [];
var regexStr = elementClass;
var regex = new RegExp(regexStr);
var x = moduleNode.getElementsByClassName(elementClass);
// console.log(x);
var y;
for ( var i = 0; i < x.length; i++ ) {
// console.log(i);
y = x[i].getElementsByTagName(elementTag);
// console.log(y);
for (var k=0; k<y.length; k++){
// console.log(y[k]);
results.push(y[k]);
}
// console.log(results);
}
return results;
};
Any suggestions please?
Thanks.
this.getElementsByClassNameThenTagName = function (elementClass, elementTag) {
if (parentNode == null) {
this.init();
}
var results = {}; // thid should be an object (collection)
var x = moduleNode.querySelectorAll("." + elementClass + " " + elementTag);
x.forEach(function(y) {
var name = y.getAttribute("name"); // if you really sure that all the matched elements have names
results[name] = y;
});
return results;
};
Now you can use the results array like this:
var someElement = results['some name'];
NOTE: All the matched elements x should have a name attribute, and all the name attributes of the matched elements should be unique.

Object being called with wrong properties

I am currently building a website like PCPartPicker but for watercooling parts for a school project. I dove in and I am having some issues. The most important on being this:
Here is my object constructor to start
var cpuCollection = [];
var myComputer = [];
function CPU(frequency,cores,socket,name) {
this.name = name;
this.frequency = frequency;
this.cores = cores;
this.socket = socket;
cpuCollection.push(this);
}
var i75930k = new CPU(3.6, 6, 2011.3, "i7 5930k");
var i54690k = new CPU(3.6, 4, 1150, "i5 4960k");`
After I built the object constructor I made some test objects using real computer parts.
In my HTML I have drop down menus that are populated by the objects on load using this code:
$(cpuCollection).each(function() {
$('#cpusel').append($("<option> " + this.name + "</option>"))
});
From there I wanted to make it so that when an option was selected in the dropdown the proper object would be pushed into the myCPU var for compatibility testing in another function. The code I used to accomplish this is as follows:
$('#cpusel').change(function() {
myCPU = new CPU();
$(cpuCollection).each(function(){
if(this.name = $('#cpusel :selected').text()) {
myCPU = this;
}
});
});
Unfortunately this code currently isn't working and is telling me that myCPU.socket is 1150 when the i75930k is selected when it really should be 2011.3. I am getting to wits end here and want to make some progress.
Thanks for the help in advance.
Edit: I fixed the equals sign issue and now I am thinking that the problem may be stemming from the way I push the objects into the cpuCollection array. When I try and log cpuCollection I get [CPU, CPU] which is obviously not what I want. How can I push the CPU objects on creation into cpuCollection with all of their properties intact.
Try this in your if() statement:
$('#cpusel').change(function() {
myCPU = new CPU();
$(cpuCollection).each(function(){
if(this.name === $('#cpusel :selected').text()) {
myCPU = this;
}
});
So, there were a few issues with some of your logic/syntax, and I will try to address them individually so you can better understand how I got a working solution:
1) You are pushing the element to an array from inside your object definition. This is typically bad practice, as it is not reusable, and will throw an error if an array called cpuCollection is not defined prior to the instantiation of that instance of the CPU object. It is better to say cpuCollection.push(new CPU(...));
2) When you append the options to the dropdown list, you should add the value property as well, so you can more easily grab the value in #3
3) If you set the value propery on the <option> elements, there is no need to look for the text of the selected option, you can simplify your selector to $('#cpusel').val()
4) There is no need to wrap your arrays in a jQuery object by saying $(cpuCollection).each(...), you can (and should) use the built-in vanilla javascript array operations.
5) I changed your .each() in the change handler to be a .some() this is because when you return true from .some() it stops any further iteration, which is what you want. Previously, it would continue to loop to the end, even if it already found the matching CPU.
6) I added a myCPU variable and instantiated it to null, so if you are running in strict mode, your change handler wouldn't throw an error for the variable not having been previously defined.
7) Your logic in your if statement was doing an assignment, rather than a comparison because you used = instead of == or ===, simple mistake.
Hope this helps!
var cpuCollection = [];
var myComputer = [];
var myCPU = null;
function CPU(frequency,cores,socket,name) {
this.name = name;
this.frequency = frequency;
this.cores = cores;
this.socket = socket;
}
cpuCollection.push(new CPU(3.6, 6, 2011.3, "i7 5930k"));
cpuCollection.push(new CPU(3.6, 4, 1150, "i5 4960k"));
cpuCollection.forEach(function(cpu, index) {
$('#cpusel').append($('<option value="'+ cpu.name + '">' + cpu.name + '</option>'))
});
$('#cpusel').change(function() {
myCPU = new CPU();
cpuCollection.some(function(cpu, index){
if(cpu.name === $('#cpusel').val()) {
myCPU = cpu;
return true;
}
});
console.log(myCPU);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="cpusel"></select>
There is simple way(s) :
var cpuCollection = [];
var myComputer = [];
function CPU(frequency,cores,socket,name) {
cpuCollection.push({
name: name,
frequency: frequency,
cores: cores,
socket: socket
});
}
var i75930k = CPU(3.6, 6, 2011.3, "i7 5930k");
var i54690k = CPU(3.6, 4, 1150, "i5 4960k");
$(cpuCollection).each(function(i,cpu) {
var option = $('<option/>').html(cpu.name).data('cpu',cpu);
$('#cpusel').append(option);
});
$('#cpusel').change(function() {
var selected = $(this).find('option:selected');
var text = $(selected).text();
var myCPU;
// You can simply use : myCPU = $(selected).data("cpu")
console.log('via data:',$(selected).data("cpu"));
$(cpuCollection).each(function(i,cpu){
if($.trim(cpu.name) == $.trim(text)) {
myCPU = cpu;
// return false; ==> break the each();
}
});
console.log('via each:',myCPU);
});
See this demo.

Advice on the best way to collect values in an array

I need to save all the color values ​​of the elements in the pages of my site and put them in a database. I thought I'd do it this way:
First thing I'm going to pick up the rgb values ​​of each element so
$("*").each(function(e){
createColorArray($(this).css('backgroundColor'));
});
then in the function createColorArray store into an array all the values ​​that are passed
function createColorArray(rgbColor)
{
//Create rgb array
}
and finally remove duplicate items from my array
function removeDoupe(ar) {
var temp = {};
for (var i = 0; i < ar.length; i++)
temp[ar[i]] = true;
var r = [];
for (var k in temp)
r.push(k);
return r;
}
now my question is,
how recommended to create the array? directly inside the $ ("*") or in a dedicated function as I'm thinking? also i need than once removed duplicates in the new array "clean" as well as the rgb value I would have also given the number of times that value was in the original.
Some example code?
As I mentioned in the comments, why not check for duplicates earlier? A simple example:
var colors = [];
$('*').each(function(i, el){
var $element = $(el),
color = $element.css('background-color');
if(!~$.inArray(color, colors))
colors.push(color);
});
console.log(colors);
http://jsfiddle.net/sL9oeywk/
The best way to do this is to do it all while you are working on it. Heres a way you could potentially do it:
var colors = new Array();
var tempColors = {};
$(".colors").each(function(){
var c = $(this).val();
// check if the color exists without looping
if(typeof tempColors[c] == "undefined"){
// if it doesn't, add it to both variables.
tempColors[c] = true;
colors.push(c);
}
});
This will result in two variables: one is an object that you don't have to loop through to find out if you defined it before, one is a colors array that you push to using standard javascript.
You shouldn't make it a dedicated function if you are not reusing it, but you could make it an object like this:
var colors = function(){
var self = this;
self.array = new Array();
// this is a dedicated check function so we don't need separate variables.
// returns true if the color exists, false otherwise
self.check = function(color){
for(var i =0; i < self.array.length; i++){
if(self.array[i] === color) return true;
}
return false;
}
self.add = function(color){
// use the check function, if it returns false, the color does not exist yet.
if(!self.check(color)){
self.array.push(c);
}
}
}
You can then instantiate a colorlist using var colorlist = new colors(); and add colors using colorlist.add("dd0300"). Accessing the array can be done by requesting colorlist.array.

Make use of a global array

I want to use the values that I get from a request, but the response object is a local variable (an array). Therefore I create this global array:
<script type="text/javascript">
var response = [];
as you see, right under the script opening tag, so it is global. Then in the function where I have the response I added this:
jsonResponse.forEach(function(element){
response[element.size] = element.id;
});
And then added this, with the purpose to make use of the values that I've got in my global var from the response object:
getIdOfProductBySize: function() {
var selectedIndex = document.getElementById('dropdown_options').value;
for (var key in response) {
if (key != selectedIndex) {
continue;
} else {
return response[key];
}
}
}
Doesn't work, so I started going step by step (of the order I add the new things) and I noticed that the script breaks after the 2nd thing that I add (where the forEach is).
Maybe I am not declaring the global variable correctly, or maybe I cannot access it this way, or maybe I don't assign the values to it in the correct way, I don't know, so I am asking if someone can give me a hint how to make use of all this working together?
Try this:
var response = {key1: value1};
var i = 2;
jsonResponse.forEach(function(entry) {
console.log(entry);
response["key"+i] = entry.id;
i++;
});
var index;
for (index = 0; index < response.length; ++index)
{
console.log(response[index]);
if(response["key"+index] !== selectedIndex)
continue;
else
return response["key"+index];
}
Looks like you're going to need a two dimensional array.
Looks to me like your "key" value is undefined.
before:
for (var key in response) {
try:
var k=response.whatever;
If that makes sense?
response[element.id] = element.size;
Try this one, i believe element.size returns the actual size of an element and is not what you want to use as index in an array.

javascript removing items from array

I have "scene" with graphics "objects"...
Scene.prototype.objects=new Array();
Scene.prototype.add=function(obj){
var last=this.objects.length;
this.objects[last]=obj}
Scene.prototype.remove=function(obj){
this.objects.splice(obj.id,1)}
Scene.prototype.advance=function(){
for (var id in this.objects){
var obj=this.objects[id];
obj.id=id;
obj.advance();
}
}
Scene.prototype.paint=function(context){...}
each time creating and deleting many objects. Array.prototype.splice re-index array right? Does anyone know a better technique (adding and removing on javascript Array)?
In my opinion, is another possibility to do that something like
Scene.prototype.remove=function(obj){
delete this.objects[obj.id]; // don,t care about this.objects.length
delete obj; // not necessary...
}
I have not tried it yet...
I need a good book about JavaScript :)
Your delete method wouldn't work, because objects is an Array, and obj.id is the id of the object reference stored in an element in that Array. splice would be the method to use, but you'll have to know the index of the element in the Array. Maybe you should 'remeber' it this way:
Scene.prototype.add=function(obj){
var last=this.objects.length;
obj.objectsIndex = last;
this.objects[last]=obj
}
After which you can:
Scene.prototype.remove=function(obj){
this.objects.splice(obj.objectsIndex,1)};
//reindex the objects within the objects Array
for (var i=0; i<this.objects.length;i++){
this.objects[i].objectsIndex = i;
}
}
Note: Adding the objects Array to the prototype of your Scene constructor means it will be the same for all instances (static), is that what you want?
It seems you don't actually need an array for your objects. You can use another object has hash table:
(function() {
var i = 0;
Scene.prototype.objects = {};
Scene.prototype.add = function(obj) {
var id = i++;
this.objects[id] = obj;
obj.id = id;
};
Scene.prototype.remove = function(obj){
if(obj.id in this.objects) {
delete this.objects[obj.id];
}
};
Scene.prototype.advance=function(){
for (var id in this.objects){
var obj=this.objects[id];
obj.id=id;
obj.advance();
}
};
Scene.prototype.paint=function(context){...}
}());
I'd rather avoid using new Array() instead use [] (page 114 of book I mentioned in comment - 'JavaScript: The Good Parts' by Douglas Crockford ;)).
What's more, I don't think adding objects array to Scene prototype will work as you expect. You probably want to achieve some Object-Oriented structure, which requires some acrobatic skills in JavaScript, as it uses prototypal inheritance as it is class-free.
Try something like this:
var Scene = function(initial_objects) {
this.objects = initial_objects || [];
};
Scene.prototype.add = function(obj) {
this.objects.push(obj);
};
Scene.prototype.remove_by_index = function(index) {
this.objects.splice(index, 1);
};
Scene.prototype.remove = function(obj) {
var index = this.objects.indexOf(obj);
if (index > -1) {
this.objects.splice(index, 1);
}
};
Read also this: http://javascript.crockford.com/inheritance.html

Categories