Implicitly global "item" variable - difference between Internet Explorer and FireFox - javascript

Just out of curiosity..
I have this JS code:
var someExternalArray = [{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 3, name: 'c'}];
var newArray = []
//var item;
for (var i = 0; i < someExternalArray.length; i++){
item = new Object();
item.id = someExternalArray[i].id;
item.name = someExternalArray[i].name;
newArray.push(item);
}
alert('0:' + newArray[0].name + ',1:' + newArray[1].name + ',2:' + newArray[2].name);
Notice the commented var item which leaves the loop with implicitly declared item variable.
If I run this code on FireFox, the result of alert is: 0:a,1:b,2:c
If I run the same code in Internet Explorer, the result is:
0:c,1:c,2:c
Here is jsfiddle: https://jsfiddle.net/fvu9gb26/
Of course, when I uncomment the var item it works the same way in every browser.
Does anyone know why this difference occurs? Thank you.

Basically, that's because Internet Explorer's window object exposes an item() method that your script cannot overwrite.
In the line:
item = new Object();
item is not declared in the local scope, so it is interpreted as a property of the global object (window.item). On Firefox, window does not expose a member named item, so a new member is introduced and the result of new Object() is assigned to it.
On the other hand, Internet Explorer exposes a native method named window.item(). That member is not writeable, so the assignment cannot take place -- it is silently ignored. In other words, item = new Object() has no effect at all (well, it does create an object but cannot assign it afterwards).
The subsequent assignments to id and name end up creating new members of the item() method. These are always the same members of the same method, so their values are overwritten on every loop iteration. In addition, the same object (the item() method) is pushed to the array on every iteration.
Therefore, the array ends up containing three times the same object, and the values of its id and name members are the last values assigned to them (in the last iteration), respectively 3 and 'c'.

This is tricky. For some obscure reason, Internet Explorer has a native method called item in the global context window (if someone knows why, you're welcome to share: I can't find it in the documentation). So, when you use the identifier item without declaring a variable, it refers to this method.
The first instruction in the loop (item = new Object();) does nothing because window.item is a readonly property. So, after this instruction, window.item is still the native function.
The next instructions (those which sets id and name) works, because while the property window.item is readonly, the Function object it's pointing to can be modified.
After the loops, you added the same Function object three times, and only the last iteration counts because you override the id and name properties everytime.

Related

Javascript assigning a new object to a function prototype, removes the link between the instance and the function prototype [duplicate]

This question already has answers here:
JavaScript: instanceof operator
(1 answer)
Javascript : Modifying Prototype doesn't affect existing Instances [duplicate]
(4 answers)
why instanceof keeps saying true after prototype changed?
(3 answers)
Closed 2 years ago.
function User() {
this.name = "name";
}
User.prototype.age = 10;
const user1 = new User(); // user1 -> { name: "name" }
console.log(user1.age) // prints 10
console.log(user1.__proto__ === user.prototype) // prints true
// if i change the age value on prototype, then user1.age will return the new value
User.prototype.age = 20;
console.log(user1.age) // prints 20
The above code works as expected, because when i call "User" function with keyword new it will return an object that only has name property and that object will be linked to User.prototype.
But What i don't get is when I've assigned the prototype with an object, there's no link anymore.
User.prototype = {age: 30};
console.log(user1.age) // prints 20! , Why?, shouldn't it print 30?
console.log(user1.__proto__ === User.prototype) // prints false
P.S: even if the link is lost because the reference of User.porotype has been changed, but why user1.age is still returning 20 not undefined
Edit: As the answers have mentioned, this has nothing to do with prototype, I've got confused because i was thinking that the object reference is like a pointer which is not. Which was already answered here Javascript pointer/reference craziness. Can someone explain this?
when you declare const user1 = new User();, behind the scene you will get user1.__proto__ = User.prototype.
Reassigning User.prototype later to another object will not affect user1.__proto__, the value will remain the same as before, it will keep a reference to the original prototype object.
In fact, it has nothing to do with the prototype thing, it's just related to object affectation like in the following example :
let obj1 = {a: 2};
const obj2 = obj1;
obj1 = {b: 2}
console.log(obj2); // print {a: 2}
When you reassign the .prototype property, old instances which used to have their internal prototype point to the old prototype do not get changed. See the docs on setPrototypeOf:
Warning: Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, currently a very slow operation in every browser and JavaScript engine. In addition, the effects of altering inheritance are subtle and far-flung, and are not limited to simply the time spent in the Object.setPrototypeOf(...) statement, but may extend to any code that has access to any object whose [[Prototype]] has been altered.
If old instances had their prototypes automatically updated, it would be a very expensive operation, and could cause odd breakages. Assigning a new object to the .prototype will only affect new instances.
If you want to update the prototypes of existing instances, you'd have to do it manually (for informational purposes only - it's a very strange thing to want to do in the first place)
function User() {
}
const user1 = new User();
User.prototype = { age: 30 };
Object.setPrototypeOf(user1, User.prototype);
console.log(user1.age)
console.log(user1.__proto__ === User.prototype)

What is falsy here ? Why does it dedupe? and why does console.log() contain the answer when infront of the foreach?

Q1-Why does this message print with the object even though it is before the forEach?
Q2-Why does this dedupe the new object
Q3-What triggers the falsy here that allow the {} to be added?
var test = [{id:1,name:'s1'},{id:2,name:'s2'},{id:2,name:'s2'},{id:1,name:'s1'},{id:4,name:'s4'}];
var test1 = {};
//Q1
console.log(test1);
test.forEach(function(item){
//Q2 and Q3
var o = test1[item.name] = test1[item.name] || {};
o.id = item.id;
});
<!--Output
{}
​
s1: Object { id: 1 }
​
s2: Object { id: 2 }
​
s4: Object { id: 4 }
​
__proto__: Object { … }
--!>
The console is tricky. It does NOT lie, it just stores a reference to that object. It's not "taking a picture" of the object as it is when you log it, it's actually reflecting the object itself. If you were to update it on click, it would update that reference in the console, too. If you alert it, you would get [object Object] instead of the text. You can use console.log(JSON.stringify(test1)) to print the contents as they were at the moment the object was read in the code.
As for deduping, it's pretty straight-forward, but a bit of a logic puzzle. During one iteration, for example, it will see test1['s1'] and assign it to 's1'. The next time it runs across another s1, it's actually referencing the same property as before, test1['s1'] and reassigning it to 's1' again.
I'll come back to take a closer look at question 3, but it's a good idea to ask one clear question at a time here on StackOverflow. ;)
--
Edit: Well, I'm not exactly sure about how you're getting those values in your log to be honest, because I'm not getting the same results even though I'm running the same script. Check my code on codepen to see my script working as expected!
Q1. As discussed in comments, major browsers now log an inspectable tree of an object, not the conversion of the object to a string using its toString method. The tree is based on a reference to the object and may show property values current when expanding the tree rather than when the object was logged.
Q2. The forEach loop sets properties of test1 using item.name values for items in the test array. If name values are repeated in test entries, only the one property name is updated in test1. Hence the de-duplication - an object can't store separate properties of the same name.
Q3. Initialisation of a test1 property to an empty object only occurs the first time a property is created in test1 for a property name held in item.name. Subsequently, if the same value of item.name is encountered again, it retrieves the pre-existing property from test1 because
test1[item.name] || {};
now evaluates to the existing test1 object property. (If the left-hand operand of an || operator is non falsey, it returns the left-hand operator value as the result of the operation.)
That perhaps leaves the o variable - it is a copy of the reference to an object stored in test1. Updating its id property updates the same object as held in test1. If test had multiple entries of the same name but different id values, the test1 property for the name would hold the id of the last duplicate entry in test.

Looking for a good clear explanation for why this doesn't work

So I am learning Javascript and testing this in the console.
Can anybody come up with a good explanation why something like this does not work?
var students = ['foo', 'bar', 'baz'];
function forEach2(arr1) {
for (i = 0; i < arr1.length; i++) {
console.log(arr1[i]);
}
}
students.forEach2(students)
I get : Uncaught TypeError: students.forEach2 is not a function
For me it would seem logical to work but it doesn't
why?
forEach2 is not a function of students. students is an array containing 3 string values. just use forEach2 without students. before it.
var students = ['foo', 'bar', 'baz'];
function forEach2(arr1) {
for (i = 0; i < arr1.length; i++) {
console.log(`arr1[${i}]:`, arr1[i]);
}
}
console.log("students:", students);
console.log("students has .forEach2 function ?", typeof students.forEach2 == "function");
console.log("forEach2 is a function?", typeof forEach2 == "function");
console.log("forEach2(arr1)...");
forEach2(students);
console.log("students.forEach(student)...");
//forEach already has a native implementation
students.forEach((student)=> {
return console.log("student:", student);
});
JavaScript is an object-oriented language with prototypal inheritance.
Object-oriented means you have objects with members (called properties in JavaScript) which hold values. In JavaScript, functions are “first-class citizens”, meaning you can assign them to variables just like you would other values.
When you write write a function such as ‘function x(y) { return y +1; }’, what’s really happening is 1) you are declaring a variable named “x”, and 2) you are creating a function “value” which is then assigned to that variable. If you have access to that variable (it’s within scope), you can invoke the function like ‘x(5)’. This evaluates to a new value which you could assign to another variable, and so on.
Ok, so now we have a problem. If functions are values, and values take up space (memory), then what happens when you need a bunch of objects with the same function? That’s where prototypal inheritance comes in. When we try to access a value on an object, via either the member access operator ‘.’, like ‘myObj.someValue’, or via an indexing operator ‘[]’ like ‘myObj[“someValue”] (both of which are equivalent in JavaScript, for the most part), the following occurs:
The runtime checks to see if a ‘myObj’ variable exists in the current scope. If it doesn’t? Exception!
The runtime looks at the object referenced by the variable and checks to see if it has a property with the key “someValue”.
If the object has that property, the member access expression (‘myObj.someValue’) evaluates to that property’s value, and we’re done.
If the object does not have that property, we start doing prototypal inheritance stuff. In JavaScript, all this means is that when we try to access a property that doesn’t exist, the runtime says “hey, what if this objects *prototype has a property with that key?” If it does, we use the prototype’s property’s value. If it doesn’t, we return ‘undefined’.
Notice that because prototypes are just objects, and therefore can themselves have a prototype, step 4 is recursive until we run out of prototypes on which to attempt member access.
Ok, so at this point you may be thinking “what’s a prototype and what does it have to do with my question?” It’s just an object. If any object has a property with the key “prototype”, then that property’s value IS a prototype. Specifically, it’s that object’s prototype. And so this is where your problem arises.
There is no property on your object with the key “forEach2”. Why? Because you didn’t put it there, and you didn’t put it on the object’s prototype (or any of the prototypes “up stream”.
The ‘forEach’ function of an Array exists as a property on the Array’s prototype: ‘Array.prototype.forEach = function (...) {...}’. Your function does not, and therefore you cannot use member access on an array to get that value (the function), and that’s why your code is borked.
Fortunately for you, a variable ‘forEach2’ exists in your current scope, and you can just use it without needing to do any member access! You just write ‘forEach2(students);’ and that’s all.
But what if you want to access that function anywhere you have an array? You have two options: put it on every instance of your arrays, or put it on Array’s prototype. ‘Array.prototype.forEach2 = forEach2;’ however, if you do this you will need to change your function a bit. Right now it expects the array as its first argument (‘arr1’), but its redundant to write ‘students.forEach2(students)’ because when a function is invoked immediately following member access, the function will be provided with a special variable ‘this’ which will have the value of the object for which you are accessing its member. So, in this case, you would omit the ‘arr1’ argument and instead just use the the special ‘this’ variable which is magically in scope within your function.
Array.prototype.forEach2 = function ()
{
for (var i = 0; i < this.length; i++)
{
console.log(this[i]);
}
}
I hope this clarifies some things for you, and I hope it raises a bunch more questions.
P.S: Adding things to prototypes is both powerful and considered harmful unless you know what you’re doing and have a good reason to do it (like writing polyfills)... so do it at your own peril and use responsibly.
For your example to work, simply add the following statement after definition of forEach2:
// Here you're redefining the built-in forEach function with your
// custom function
students.forEach = forEach2;
// Now this must work
students.forEach(students);
This is an explanation why is that:
An array is a particular implementation of the Object. Object is an aggregate of the property:value pairs, such as
{'some_property': '1',
'some_function': function(),
'forEach': function() {<function logic>},
...
}
Your error 'Uncaught TypeError: students.forEach2 is not a function' tells, that there is no property forEach2 (which supposed to be a function) in between the Object's properties. So, there are two methods to correct this situation: - add a property (method function) to the object, or alter the existing property with similar functionality (not necessarily though).
[1] The first method assumes you add a new property function using Array.prototype.forEach2 = function() {...}.
[2] The second - you may redefine or alter any property in the Object by just assigning a new value to that property: Object.forEach = forEach2
Because the function forEach2 is not available either within the Array.prototype or is not a direct property of that array, so, you have two alternatives to use that custom forEach:
Use a decorator to add that function to a specific array
var students = ['foo', 'bar', 'baz'];
function forEach2() {
for (i = 0; i < this.length; i++) {
console.log(this[i]);
}
}
function decorate(arr) {
arr['forEach2'] = forEach2;
return arr;
}
decorate(students).forEach2()
Add that function to the Array.prototype
var students = ['foo', 'bar', 'baz'];
Array.prototype.forEach2 = function() {
for (i = 0; i < this.length; i++) {
console.log(this[i]);
}
}
students.forEach2();
Both alternatives use the context this to get the current array.

Using JSON to create instances of an object

I'm trying to learn to program. I've gone through a list of tutorials sites online and I'm stuck on a thing that I think is extremely important for me to understand.
My questions:
(This is what I'd like to understand most) In my for...in loop, why is creating new "obj" objects using the "Contacts" constructor working? It seems like I would need a different name each time I loop so that I don't overwrite the object that I've created the pass before. If this is correct, how do I do this if I don't know anything about the number or value of contacts ahead of time? Additionally, why does the title of the any of the objects in the console logs not say obj? Am I confused about what it means to create an instance of an object? Are the names of these instances unimportant?
Why are all of the properties undefined? Should referencing properties from the temporary "i" variable work?
Creating objects from an unknown total of data entries seems really important. Unfortunately, places like Codecademy fall short here. You always manually create new instances of objects with hardcoded names they give you. But what would happen if there were two of the same name?
Thanks so much for any help I may get on this. Don't hold back from telling me anything else silly that I may be doing.
Here is a link to a console screenshot - http://i.imgur.com/TK4dtfV.png
var TestApp = {};
// my data... taken from wherever
TestApp.jsonContacts = {
contact1: {
name: "Ethan",
age: 24
},
contact2: {
name: "Evan",
age: 30
},
contact3: {
name: "Paul",
age: 9000
}
};
// I know this is silly, just let me pretend...
TestApp.jsonStrung = JSON.stringify(TestApp.jsonContacts);
TestApp.globalContactList = [];
// my constructor function to create instances of Contact
TestApp.Contact = function(name, age){
this.name = name;
this.age = age;
TestApp.globalContactList.push(this);
};
// where I'm taking data and creating new Contact objects
TestApp.instantiateObjects = function(){
// I know this is silly, just let me pretend...
var jsonUnstrung = JSON.parse(TestApp.jsonStrung);
// I think I'm looping through the first set of objects sitting in jsonContacts
for (var i in jsonUnstrung) {
var obj = new TestApp.Contact(i.name, i.age);
console.log(obj);
}
console.log(TestApp.globalContactList);
};
TestApp.instantiateObjects();
In my for...in loop, why is creating new "obj" objects using the "Contacts" constructor working?
A variable is just a holding place for a value. It is not the value itself. If you overwrite a variable with a new value, the previous value it held will continue to exist as long as something is holding a reference to it; it's simply that the variable won't be referring to it anymore.
It seems like I would need a different name each time I loop so that I don't overwrite the object that I've created the pass before.
No, you don't. One variable is fine, as long as you do something with the value before you assign the variable to the next value.
If this is correct, how do I do this if I don't know anything about the number or value of contacts ahead of time?
It doesn't matter how many you have.
Additionally, why does the title of the any of the objects in the console logs not say obj?
console.log() prints out the value that is passed to it. It doesn't care (doesn't know) anything about the variable that you pass to it.
Am I confused about what it means to create an instance of an object?
It seems so. Creating an instance of an object allocates some memory to store that object's values. A variable allows you to gain access to that allocated object.
Are the names of these instances unimportant?
Yes, object instances don't have names.
Why are all of the properties undefined?
i holds the property names of the object you are iterating through, so in this case, the strings "contact1", "contact2", "contact3". These strings don't have a name or age property, so your constructor is receiving two undefined values.
Should referencing properties from the temporary "i" variable work?
You need to use i as a property name to access the property values:
var obj = new TestApp.Contact(jsonUnstrung[i].name, jsonUnstrung[i].age);
In general, it's a good idea not to have side-effects like TestApp.globalContactList.push(this); in a constructor. There may be cases where doing so makes sense, but most of the time, removing that line and doing this would be preferable:
for (var i in jsonUnstrung) {
var contact = jsonUnstrung[i];
var obj = new TestApp.Contact(contact.name, contact.age);
console.log(obj);
TestApp.globalContactList.push(obj);
}

javascript iterate object - first element undefined

I am simply iterating through an object with the javascript for (key in obj) syntax:
var myObj = { id:'1', number:'2', name: 'my' };
var i, item;
for (i in myObj) {
item = myObj[i];
}
I recognized in firebug debugger (firefox) that the first loop iteration sets i to undefined and yet the second iteration sets i to 'id'. I have some problems in my code, because of this behaviour. Is it normal that the first iteration gives undefined? Has it something to do with the JSON notation, missing prototype etc?
It seems to work perfectly fine. However, you might want to use .hasOwnProperty() to ensure that the property belongs to the object and is not inherited:
var myObj = { id: "1", number: "2", name: "my" };
var i,item;
for (i in myObj) {
if (myObj.hasOwnProperty(i)) item = myObj[i];
}
And generally, native prototype methods aren't enumerated, simply because they are not enumerable. If you are using a framework such as Prototype or MooTools, however, the methods they add will be enumerated and seen in your loop. .hasOwnProperty() ensures that they are ignored.

Categories

Resources