Consider the following two ways of creating the instance of an object:
function f() { return { k: 'v'} }
var inst1 = f();
function F() { this.k = 'v'; }
var inst2 = new F();
The behavior of inst1 and inst2 is the same, the only difference is that a different constructor is saved to the object:
inst1.constructor; // Object()
inst2.constructor; // F()
What's the constructor of an array? See:
var xs = [];
xs.constructor; // Array()
Until this Point I understand the logic. But I bumped into the following:
var tags = document.getElementsByTagName("*");
typeof tags; // object
tags.constructor; // HTMLCollection()
So far it's similar to the inst2 example. But when I console.log with Firebug, I receive something like an Array with a 'named' constructor:
console.log(tags); // HTMLCollection[tag1, tag2, ...]
The block brackets confuse me, I would have expected curly one here. There must be an explanation to this, anybody knows the answer?
It sounds like the crux of your question lies in determining how Firebug displays your object. Firebug examines objects you passed to it, and chooses how it will display objects as a string based on what properties they have.
HTMLCollection is "array-like" so it has a length property, and stores its contents in properties named 0, 1, 2, etc. This makes it array-like and Firebug recognizes this, outputting the string representation of your object like it would an array.
In the case of Firebug, if it sees a length and a splice property, it will treat the object as array-like:
var MyArrayLike = function(){
this[0] = 1;
this.length = 1;
this.splice = function(){};
}
Firebug output:
-> new MyArrayLike();
<- [1]
Related
I've created 2 objects (object1 and object2) using different ways.
I found no difference between them, except for the way how it is displayed in the Chrome Dev Console (see this in the below screenshot)
var F;
function create(parent, properties) {
F = function(p) {
for(var i in p){
this[i] = p[i].value;
}
};
F.prototype = parent;
return new F(properties);
}
var prop={ p: { value: 42 } };
var masterObject = {a: "masterObject value"}
var object1 = create(masterObject, prop);
var object2 = Object.create(masterObject, prop);
Following are my questions:
As I'm following different ways to create objects, will there be any difference between the objects - object1 and object2?
What is the difference that can be seen in the above screenshot (encircled in red)?
Both Objects have the same properties, and inherit from the same object. However, theres a small difference:
new F();
//vs.
Object.create(F.prototype);
The constructor (the function called to build the object) should be different:
object1.constructor!==object2.constructor
So these objects should not be equal, however it has no real effect as the constructor is rarely used.
Should because basically
F.prototype=parent;
breaks the whole thing, as F.prototype.constructor is initialized with F , so youre overridding this. More info at
Why is it necessary to set the prototype constructor?
When you assign a function to a variable, you created a named function.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
They are functionally the same.
I have an Array of objects A
And I have 2 different drawing functions which change A completely in their own ways.
I would like to keep A unchanged. Is there best practices how to do it? My current solution is somehow feels unnatural:
var A;//Should stay the same always
drawGraphX(A){
//Modifying A here to draw but I would like the original A to stay the same
B=JSON.parse(JSON.stringify(A));
//So I do it with B
}
drawGraphY(A){
//Modifying A here to draw
B=JSON.parse(JSON.stringify(A));
//So I do it with B
}
I suspect the real answer here is that you shouldn't be changing the data inside your function at all!
Lets imagine that A is an array of x/y points to be used for a chart
var A = [{x:1,y:1},{x:2,y:2},{x:3,y:3}];
And your function just wants the x - are you doing this:
function drawGraphX(A){
for(var i=0;i<A.length;i++)
A[i] = A[i].x;
// now draw the graph
}
In the above example, yes,, you will be changing the source of A as you've just passed a reference in to the function and updated the elements of that referenced array. This is bad practice, what you should be doing is this:
function drawGraphX(A){
var data = A.map(function(e){
return e.x;
});
// data is an array of just the x values
}
Try putting drawGraphX(A.slice()) slice returns copy of array if shallow copy is fine for you and it is just about array itself, however remember that you are in fact not cloning objects so
var A = [{test:'foo', test2:'bar'}, {test:'foo1', test2:'bar1'}];
var B = A.slice();
A[0].test = 'foobar';
console.log(B[0].test);
will return you "foobar"
so it is fine if you mutate array itself but not elements (note that the same comment goes to Array.from(A) solution.
What I would do is use the Array.from() method, to pass a new array into your function. So, when you call drawGraphX(A), instead call drawGraphX(Array.from(A)). This will create a new Array of the same data you had in 'A'. Easy Peasy.
var b = Array.from(A);
drawGraphX(b);
or
drawGraphX(Array.from(A));
Edit: As netRat and Jonasw pointed out. This will make a new array, but it keeps references to the individual objects. Meaning that while mutating the Array will not change the source array, changing any of the objects shared by the two arrays will change the source material. I.E.:
var a = [1,2];
var b = Array.from(a);
b[0] = b[0]++;
console.log(a); // will result [2,2];
while
b.push[3];
console.log(a); // will result [1,2]
console.log(b); // will result [1,2,3];
Proof of concept: https://jsfiddle.net/5hLjajc0/1/
The more elegant solution would be sth like a switch, that if an objs is undefined, take a parents one:
function switch(obj,prot){
return function(keys,val){
keys= keys.split(".");
var el=obj;
var par;
for(key of keys){
par=el
el=el[key];
}
if(el==undefined){
var el=prot;
var par;
for(key of keys){
par=el
el=el[key];
}
}
if(value){
par[keys[keys.length]]=value;
}
return el;
};}
Use like this:
prot=[0,1,3];//unchangeable
obj=[,5,];
callfunc(switch(obj,prot));
function callfunc(el){
//read
el("0");//0
el("1");//5
el("2");//3
//write
el("0", 12);//do not override prot
//also with multidim stuff:
el("0.a");
}
Its mainly an improved version of prototypes:
var obj={
0:function(){
alert("original prototype");
},
1:5
}
callfunc(Object.create(obj));//...
callfunc(Object.create(obj));//...
This will allow you to access the prototypes props, wich cant be overriden that easily:
function callfunc(arg){
arg[0]();//works as expected
arg[0]=function(){
alert("hi");
}
arg[0]();//hi
}
This doesnt override the prototype, it extends the arg...
To override you can still do
arg.prototype[0]=function(){
alert("hi prototype");
};
Please help me out to understand this example.
function fun(a){
this.length = 1;
this.splice = [].splice;
this[0] = a;
return this;
};
Now, when I execute this function the result is an array.
f = new fun('hi');// result will be : ["hi"]
Why is that?
If I remove this.length=1 OR this.splice = [].splice, the result will be different.
f = new fun('hi'); // result will be : fun {0: "a", splice: function}
Why is that?
I also see this technique used in jQuery. Please describe to me how this is working programmatically.
There is nothing programmatical about it. It's just the way a browser/js engine chooses to show you some variable in the console.
The console is generally for developers as they are the ones who open those things. So the browser does a little sniffing to show the developer what this object he's/she's dealing with is. If it looks like an array, it probably should be printed like an array.
Like the following list shows, there are some differences between the browsers. Namely, IE and node do nothing. My interpretation is that printing in node should yield the complete view and not just the sniffed view. The length seems to satisfy Opera 12 to show it as an array.
Why browsers use index, length and splice is an entirely different story. It's probably the smallest set of properties that denote an array with high probability. Look at it, what else would it be, if not an array?
Chrome 36, Firefox 30 and Opera 18 behave the same way:
fun as-is is ["hi"]
fun without length is fun { 0: 'hi', splice: function}
fun without splice is fun { 0: 'hi', length: 1}
Opera 12 shows:
fun as-is is Object ["hi"]
fun without length is Object
fun without splice is Object ["hi"]
IE 9 and node v0.8 did no sniffing at all (here IE output, node output very similar):
fun as-is is {0 : "hi", length : 1, splice : function splice() { [native code] }}
fun without length is {0 : "hi", splice : function splice() { [native code] }}
fun without splice is {0 : "hi", length : 1}
What you construct is not an array. I believe that your object may have a few features that let console recognize whether a given object is an array or not.
var arr = [];
var f = new fun('asd');
typeof arr; // "object"
typeof f; // "object"
arr instanceof Array; // "true"
f instanceof Array; // "false"
I'm not sure, how you can get that only "hi" result, because when I tried below code in a Fiddle, I get Object { 0: "hi", length: 1, splice: splice() }. Which is make sense because what it does is creating an object which has 3 attributes(length, splice, and index[0]).
function fun(a){
this.length = 1;
this.splice = [].splice;
this[0] = 'hi';
return this;
}
f = new fun('hi');
console.log(f); //get the object f
console.log(f[0]); //get the string inside index-0
console.log(f.length); //get the length value which is already assigned before
console.log(f.splice); //it returns a function, which I believe a splice function
I asked in IRC chat yesterday whether it was possible to have an element update with any changes to an object it references, rather than just keep the value it was given upon being declared. For example
Arr1 = [1,2,3]
i1 = Arr1.length-1
last1 = Arr1[i1]
Arr1.push(4)
Checking last1 at the end of this (arbitrary) example shows it has not updated to reflect the newly added value of 4, so objects in JS aren't "live" by default.
I'm a beginner but I'd worked this out from practice already, but was told this was actually the case... I guess my question wasn't understood.
NodeList objects are "live" however, and I'm wondering if there are other types of object in JS that do so, as it would obviously save lines of code spent updating them.
One important distinction here is that i1 does not "reference" anything here. It is simply storing the numeric result of the expression Arr1.length-1 when that line executed. Likewise, last1 may or may not reference the value that was the third element of Arr1 when line 3 executed, but it maintains no reference to Arr1 itself or anything about it.
As in some other programming languages, variables that are assigned to objects are references, so you can do this:
var obj1 = { prop1: "hello", prop2: "goodbye" };
var obj2 = obj1;
obj2.prop1 = "buongiorno";
console.log(obj1.prop1); // result is "buongiorno"
But this doesn't seem to be quite what you're describing.
It sounds like what you're describing is some sort of reactive programming. JavaScript doesn't really work the way you're imagining, but you could accomplish this using closures:
var Arr1 = [1,2,3];
var i1 = function() { return Arr1.length - 1; };
var last1 = function() { return Arr1[i1()]; };
console.log(i1()); // result is 2
console.log(last1()); // result is 3
Arr1.push(4);
console.log(i1()); // result is 3
console.log(last1()); // result is 4
Note that here, the () parentheses at the end are required to call these functions and get their current value.
One even trickier thing you could do is the following:
function capture(fcn) {
return { valueOf: fcn, toString: function() { return fcn().toString(); } };
}
var Arr1 = [1,2,3]
var i1 = capture(function() { return Arr1.length - 1; });
var last1 = capture(function() { return Arr1[i1]; });
console.log(last1 * 5); // result is 15
Arr1.push(4);
console.log(last1 * 5); // result is 20
Note however that this second technique has its limitations. You have to coerce the values into the type that you would expect or call their .valueOf() method in order for them to produce an actual value. If you just used:
console.log(last1);
You would not get any sort of friendly result.
I interpret your question as why are some variables which are copies are updated when you change the original value and others are not.
This is because some types of variables are Reference Types and others are Value Types. Numbers, dates and strings are value types and are copied whenever you assign them to a variable. Objects, Arrays (which are also an Object) are reference types and are not copied, just referenced.
This example is using value types and any change to the first variable will not be copied:
var foo = 1;
var bar = foo; // this is copying the value from the foo variable to bar
foo = 2;
console.log(bar); // 1
compared to the same thing but with a reference to an object:
var foo = {prop:1};
var bar = foo; // this is creating a reference to the foo object
foo.prop = 2;
console.log(bar.prop); // 2
Okay, so this is complicated so try to bear with me, i will try to keep it as simple as possible:
I have a "class" Struct, that creates a "struct like" skeleton to apply to ArrayBuffers within Javascript. The problem is when I am attempting to mimic the 'c' like behavior of allowing a struct to contain other structs.
The problem is that it clobbers the iterator of the calling method (obviously a closure problem), that i cant seem to figure out.
Here is an example of the code that gets clobbered (hopefully this is enough code to get the answer, if not I will add more as necessary, just trying to keep extraneous code out of here):
function StructObject(){
this.applyBuf = function(buf, start){
var struct = {};
for (obj in this){
//problem is here:
console.log(obj); //prints "c" on the way in
struct[obj] = this[obj].__createFromBuf();
console.log(obj); //prints "foo" (see the structs below)
}
return struct;
}
}
function struct(strctObj, name){
var structObject = new StructObject();
...
//create the skeleton
for (item in strctObj){
//the specific code that fails me
structObject[item].__createFromBuf = function(buf, pos){
return structs[this.name].applyBuf(buf, pos);
}
...
//store the skeleton for later application
structs[name] = structObject;
}
//Creating structs looks like this:
new struct({ foo: type.INT }, "bar");
new struct({
a: type.INT, //defines size of memory (like c's sizeof)
b: type.LONG,
c: {type: type.STRUCT, name: "bar"},
d: type.SHORT}, "myStruct");
structs.myStruct.applyBuf(new ArrayBuffer(35));
When I iterate through the first struct on the class method applyBuf, it calls __createFromBuf on each item within the struct skeleton, if the item is another struct __createFromBuf calls applyBuf on the other "struct skeleton" object and returns an instance of that struct back to the calling struct, which works as intended.
JSFIDDLE -- Here is the link to a working example :)
clean your for statements:
if not declared properly, the for loop index becomes a global member.
demonstration:
for (item in strctObj) // item becomes a global member
for (var item in strctObj) // item is scoped within the loop's owner function