I've been using JavaScript for years and this one has me stumped. As I understood things, when defining a var, one of two things will happen:
If the expression is a primitive, var is defined as a new instance of that primitive with no reference to the passed expression.
If the expression is an object literal, a new object is created.
If the expression is an existing object, var will reference the object and any future changes to the object will be reflected.
However, I've run into a situation where case 3 doesn't apply:
var obj = {body: {'a': 1, 'b': 2, 'c': 3}};
var ref = obj.body;
ref = JSON.stringify(ref);
console.log(typeof ref); // string
console.log(typeof obj.body); // object
Since ref is defined as the body property of obj, I thought redefining ref as a string also would affect obj.body. So what am I missing?
JSON.stringify is a method which takes an object and returns its string representation, it doesn't change anything. By doing ref = x you make ref point to another thing, it doesn't affect what was there before assignment.
That simply means, you are no more referencing obj.body.body and referencing to something else.
var ref = obj.body;
//ref holding obj.body now any changes to ref will effect obj.body.
ref = JSON.stringify(ref);
//ref holding a String returned by `stringify()` now any changes to ref will effect obj.body.
You see ?? You just changing the ref with different values. Not really changing anything on obj
Primitives are immutable. If there’s a difference in how they would behave compared to objects, you can’t observe that, so forget all that stuff about copying. Let’s talk instead in terms of “things”! Objects and primitives are both things. When you assign a thing to a variable, you are not copying the thing.
var x = literally any value;
var y = x;
x and y are both variables that contain the same thing. If you change the thing, it doesn’t matter where you access it from in the future; the thing changed. If you change the thing the variable contains, the thing it contained before is not affected.
var z = some other value;
y = z; // y now contains the same thing as z instead of the same thing as x
// only variables changed, and the things did not
There are a lot of answers that talk about this in other terms but I enjoy technical language.
tl;dr: For all intents and purposes, the distinction between objects and primitives in JavaScript is not a useful one.
ts;iwrse: This article about Python applies to JavaScript just as much.
Related
I know that ES6 is not standardized yet, but a lot of browsers currently support const keyword in JS.
In spec, it is written that:
The value of a constant cannot change through re-assignment, and a
constant cannot be re-declared. Because of this, although it is
possible to declare a constant without initializing it, it would be
useless to do so.
and when I do something like this:
const xxx = 6;
xxx = 999;
xxx++;
const yyy = [];
yyy = 'string';
yyy = [15, 'a'];
I see that everything is ok: xxx is still 6 and yyy is [].
But if I do yyy.push(6); yyy.push(1); , my constant array has been changed. Right now it is [6, 1] and by the way I still can not change it with yyy = 1;.
Is this a bug, or am I missing something? I tried it in the latest chrome and FF29
The documentation states:
...constant cannot change through re-assignment
...constant cannot be re-declared
When you're adding to an array or object you're not re-assigning or re-declaring the constant, it's already declared and assigned, you're just adding to the "list" that the constant points to.
So this works fine:
const x = {};
x.foo = 'bar';
console.log(x); // {foo : 'bar'}
x.foo = 'bar2';
console.log(x); // {foo : 'bar2'}
and this:
const y = [];
y.push('foo');
console.log(y); // ['foo']
y.unshift("foo2");
console.log(y); // ['foo2', 'foo']
y.pop();
console.log(y); // ['foo2']
but neither of these:
const x = {};
x = {foo: 'bar'}; // error - re-assigning
const y = ['foo'];
const y = ['bar']; // error - re-declaring
const foo = 'bar';
foo = 'bar2'; // error - can not re-assign
var foo = 'bar3'; // error - already declared
function foo() {}; // error - already declared
This happens because your constant is actually storing a reference to the array. When you join something into your array you are not modifying your constant value, but the array it points to. The same would happen if you assigned an object to a constant and tried to modify any property of it.
If you want to freeze an array or object so it can't be modified, you can use the Object.freeze method, which is already part of ECMAScript 5.
const x = Object.freeze(['a'])
x.push('b')
console.log(x) // ["a"]
Came through this article while searching on why I was able to update an Object even after defining it as const. So the point here is that it is not the Object directly but the attributes it contains which can be updated.
For example, my Object looks like:
const number = {
id:5,
name:'Bob'
};
The above answers correctly pointed out that it's the Object which is const and not its attribute. Hence, I will be able to update the id or name by doing:
number.name = 'John';
But, I will not be able to update the Object itself like:
number = {
id:5,
name:'John'
};
TypeError: Assignment to constant variable.
This is consistent behavior with every programming language I can think of.
Consider C - arrays are just glorified pointers. A constant array only means that the value of the pointer will not change - but in fact the data contained at that address is free to.
In javascript, you are allowed to call methods of constant objects (of course - otherwise constant objects would not serve much purpose!) These methods might have the side effect of modifying the object. Since arrays in javascript are objects, this behavior applies to them as well.
All you are assured of is that the constant will always point to the same object. The properties of the object itself are free to change.
The keyword const is a little misleading.
It does not define a constant value. It defines a constant reference to a value.
Because of this you can NOT:
Reassign a constant value
Reassign a constant array
Reassign a constant object
But you CAN:
Change a constant array
Change a constant object
I think this would give you more clarity on the issue : https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0 .
Basically it boils down to the const always pointing to the same address in memory. You can change the value stored in that address but cannot change the address the const is pointing too.
The definition of const you mentioned will hold true when the const is pointing to an address that holds a primitive value . This is because you cannot assign a value to this const without changing its address (because this is how assigning primitive values work) and changing the address of a const is not allowed.
Where as if the const is pointing to non-primitive value , it is possible to edit the value of the address.
The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned. For instance, in the case where the content is an object, this means the object's contents (e.g., its parameters) can be altered.
In addition, an also important note:
Global constants do not become properties of the window object ...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
The value of a const can't be changed through reassignment, and it can't be redeclared.
const testData = { name:"Sandeep",lastName:"Mukherjee",company:"XYZ"}
First case
testData = {name:"hello"}
console.log(testData);//throws an Error:Assignment to constant variable
Here we are reassigning testData again
Second case
const testData = {name:"Sandeep",lastName:"Mukherjee",company:"ABC"}
console.log(testData); //throws an Error: Identifier 'testData' has already been declared
Here we are redeclaring testData again
When a variable is declared using const it means it points to some memory location the
behaviour of const is we can manipulate the value stored in that memory location but not
the memory location,when we reassign/redeclare the const variable it
does not allow to change the memory location
We can change the value of a specific key
testData.company = "Google"
console.log(testData);
//{ name: 'Sandeep', lastName: 'Mukherjee', company: 'Google' }
We can add any new key value pair to it
testData.homeTown = "NewYork"
console.log(testData)
//{name: 'Sandeep',lastName:'Mukherjee',company:'Google',homeTown: 'NewYork'}
Because in const you can change the values of an object, so the object does not actually store the assignment data but instead, it points to it.
so there is a difference between primitives and objects in Javascript.
const variable stores the address (memory address such as 0xFF2DFC) that is constant.
The constant is NOT the content at the memory.
constant is memory address ONLY
Thank you for reading.
const MY_OBJECT = {'key': 'value'};
// Attempting to overwrite the object throws an error
// Uncaught TypeError: Assignment to constant variable.
MY_OBJECT = {'OTHER_KEY': 'value'};
// However, object keys are not protected,
// so the following statement is executed without problem
MY_OBJECT.key = 'otherValue';
// Use Object.freeze() to make object immutable
// The same applies to arrays
const MY_ARRAY = [];
// It's possible to push items into the array
MY_ARRAY.push('A'); // ["A"]
// However, assigning a new array to the variable throws an error
// Uncaught TypeError: Assignment to constant variable.
MY_ARRAY = ['B'];
In your constant is saved not the object, but link to the object.
You can't change this link, because it is constant.
But object you can change.
If I declare the following in my Chrome console:
var object = {0:0, 1:1}
I can call object[0] and object[1] and get their values. I can also call object["0"] and object["1"]. Next, if I declare:
var object = {"0":0, "1":1}
I can also make all four of the above calls. But if I declare:
var object = {a:0, 1:1}
I get a ReferenceError of "a is not defined" when I call object[a], but object["a"] returns 0, even though the property name in the declaration is not a string. I guess JavaScript thinks I'm calling a variable that doesn't exist in the first example. But why do calling object[0] and object["0"] both work? It seems that JavaScript is doing some kind of automatic conversion for numbers (presumably since they can't be variable names), but what are the rules for this? And is this behavior universal to other places it might come up or just to the bracket notation for objects?
When you use brackets, the expression inside the brackets is evaluated. What's the value of the expression
a
?? Well, if "a" isn't a declared variable, it's nonsense. When you use . notation, the identifier (and it must be an identifier) following the operator is treated as a string. It's just the way the language works.
The reason you're getting a ReferenceError for object[a] is because a literal a is a variable in javascript. "a" is a string containing the letter a.
You can use the dot notation object.a or the bracket notation with object["a"]
object.a; //=> 0
object["a"]; //=> 0
object[1]; //=> 1
object["1"]; //=> 1
Or you can use a variable for access
var x = "a";
object[x]; //=> 0
var y = 1;
object[y]; //=> 1
You are correct.
a there is a token which the engine assumes is a variable.
If you type "a" JS knows it's a string-primitive.
If you type 0, JS knows it's a number-primitive.
So on top of obj.a, obj["a"], obj[0], obj["0"], you can also say:
var a = 0;
obj[a]; // 0
Your app is exploding, because a hasn't been defined yet, and now you want to use it.
And yes, this is the expected behaviour.
What's inside of the brackets isn't seen as a "part" of the object -- it's a way of saying "give me the value of the object which is referenced by this key", where the key might be a number or string (or something that can be coerced into a string or number).
In the future, with maps and weakmaps, you would actually be able to use other objects/functions as keys as well.
var obj = new Map(),
func = function () { },
el = document.getElementById("myId");
obj[func] = 1;
obj[el] = 2;
Right now, these things technically work... ...but only because they're converted to their string values... ...so if you had two functions which were written the same (but technically two different objects), you would overwrite values, currently.
Inside of a map, they'd be treated as separate objects.
Using DOM elements is even worse, right now, as it might be useful to store hundreds of those and bind references to them, so that you don't have to keep looking for them... ...but for now, you need to make a unique ID number/key for each one, and store that, and remember the keys, and then create a child object to hold the data you want...
Whereas in the future, with maps, you could say:
var my_els = document.querySelector(".lots-of-els");
for (let el of my_els /* also the future */) {
console.log( my_map_of_data[el].info );
}
Recently I started learning about object-oriented programming in JavaScript. What I understood, is that when referencing to variables, we in fact reference not to their actual values, but locations in the memory. That's why all those "return this" methods that are supposed to copy instances don't work.
So, example code:
//An example object with a simple property and
//failing "copy" function.
function MyObject()
{
this.myProperty = 123;
this.copy = function() { return this; };
}
var iOne = new MyObject();
var iTwo = iOne.copy();
iTwo.myProperty = 321;
Now "myProperty" property of both iOne and iTwo equals 321, because "copy" method returned a reference, instead of a value. This behavior is expected, and everything is okay.
Now, I tried doing the same with a native object type, Number. Let's create an instance of it, in a more object-oriented programmer-friendly way:
var iOne = new Number(123);
var iTwo = iOne; //Equals "iTwo = iOne.copy()", except there isn't a copy method
iOne = 321;
And now, something terrible happened. iOne equals 321, but iTwo kept its value and is still equal to 123.
I have no idea what is this behavior caused by. Maybe Number is some kind of "special"? Maybe the decimal number associated with it is something more than a property? Or maybe it's just supposed to make life of inexperienced programmers easier? The last option is related to operators. If anyone know something about it, please don't let my way of understanding JavaScript fall apart.
Objects, Arrays and Strings are assigned by reference (not by copy). All other types are effectively copies when assigned (e.g. they make a new variable that has nothing to do with the old one).
Strings are a special case because they are immutable so when you change a string, it always creates a new string so it behaves more like it makes a copy even though the previous assignment was a reference.
Assigning:
iOne = 321;
Is replacing the value of iOne with a simple primitive numeric type so it will have no effect on any other variable.
var iOne = new Number(123);
var iTwo = iOne; //Equals "iTwo = iOne.copy()", except there isn't a copy method
iOne = 321;
You're overwriting the object reference held by the iOne variable, with a distinct primitive number.
The objects are held as references, but they are not pointers that can be directly dereferenced, so you can't replace the data held in that memory location. You can only mutate it (if the object is mutable).
Specifically, the Number object wrapper is not mutable, or at least the primitive value it holds can't be replaced. You can only replace the entire object.
iOne = 321;
This code did what is expected, you assigned 321 to the variable iOne, overwriting what it was referencing to originally.
There's no real difference in behaviour between "native types" and objects in Javascript (except that native types are immutable).
In your second example you're simply changing what variable iOne is pointing to, why should it change what another independent iTwo variable is pointing to?
In the first case instead you have two variables pointing to the same object and if you use one variable to mutate the object and you can observe the change also using the other variable (obvious... it's pointing to the same object).
In Javascript you can imagine that everything is always by reference and never by value (copy). If you want to copy something you need to do it explicitly... for arrays you can use x.slice() to make a shallow copy of x; for objects there's no primitive function for doing the same so you must call the constructor.
A common OOP pattern is to have a member function .clone() that returns a copy so who needs the copy doesn't need to know how to make a copy of every class.
function P2d(x, y) {
this.x = x;
this.y = y;
}
P2d.prototype.clone = function() {
return new P2d(this.x, this.y);
}
Another possibility specific to the protoype model of Javascript and that can be useful in some cases is to create a separate object that will appear like a shallow copy that can be mutated without affecting the original but that is instead referencing the original object when reading:
function fakeCopy(x) {
function f() { }
f.prototype = x;
return new f;
}
p = new P2d(10, 20);
q = fakeCopy(p);
console.log(q.x); // Displays 10
q.y = 30;
console.log(q.y); // Displays 30
console.log(p.y); // Displays 20 -- original not changed
p.x = 99;
console.log(q.x); // Displays 99 (!)
This happens because Javascript objects have a "prototype chain" that is searched when accessing a member for reading. q is created as an empty object that with p as its prototype so when looking for an attribute (for reading) it will go searching inside p if something is not found inside q. When writing however an attribute will be set inside q, not affecting p and from that point on the value present in q will be returned instead of having to go up in the prototype chain.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Copying array by value in javascript
i have a funny problem with javascript. i copy an array variable to make modifications on the copy only, then splice the copy to delete an element. however the original array variable is affected by the splice - as if the copy was a 'copy by reference':
window.onload = function() {
var initial_variable = ['first', 'second', 'third'];
var copy_initial_variable = initial_variable;
copy_initial_variable.splice(0, 1);
alert('initial variable - ' + initial_variable);
};
//output: initial variable - second,third
firstly, is this intentional behaviour for javascript or is it a bug?
and secondly, how can i make a copy of an array and delete an element in the copy but not in the original?
one thing which makes me think that the above may be a javascript bug is that this behaviour only happens with arrays and not with integers. for example:
window.onload = function() {
var initial_variable = 1;
var copy_initial_variable = initial_variable;
copy_initial_variable = 2;
alert('initial variable - ' + initial_variable);
};
//output: initial variable - 1
if the behaviour were consistent then this ought to output 2 since the assignment would presumably be by reference?
This is in no way a bug, but a very common misunderstanding. Let's see what happens when I say
var a = b;
Integers and other javascript primitives, like floats and booleans, are "assigned by value".
Which means that whatever value b has is going to be copied to a. To the computer, it means having the part of memory that b references copied to the memory that a references. That's the behavior you were expecting.
When arrays and other objects (and "descendants" of a new Object() call) are used like that, there is a copy by reference. Meaning that the value of a now references the value of b, the memory that b references isn't copied or modified. Thus, when writing
a = [1,2,3];
b = a;
b and a become interchangeable. They're referencing the same memory address. To achieve what you're trying to do, use
var copy_initial_variable = initial_variable.slice(0);
Read Does JavaScript pass by reference? for more information.
In first case you are working with arrays, which are passed by reference. And in second case you are working with prime types which are passed by value. In first case you should copy initial array (e.g. with initial_variable.slice(0)). Try something like
window.onload = function() {
var initial_variable = ['first', 'second', 'third'];
var copy_initial_variable = initial_variable.slice(0); //returns new array!!!!
copy_initial_variable.splice(0, 1);
alert('initial variable - ' + initial_variable);
};
The problem isn't in how splice behaves, but the fact that initial_variable and copy_initial_variable reference the same array.
alert (copy_initial_variable === initial_variable);
In JavaScript there are two types of values: primitive values, like numbers and booleans, and objects, including arrays. Variables hold primitive values, but they hold references to objects. "Copying" primitive values works as you expect, a new primitive value is created so that changing the copy variable will not change the original variable. But "copying" an object actually copies the reference pointing to that object, it doesn't create a new object.
It is not a JavaScript bug, this is the intended behavior.
Today I came across this problem in javascript and do not know why it happens.
var a = {
prop: {
bool: true
}
};
console.log(a.prop.bool); // logs true
var b = a;
b.prop.bool = false;
console.log(a.prop.bool); // logs false ¿?
The expression { prop: ... } expression is evaluated once to create one object.
a and b both are references to that single object.
See What's the difference between passing by reference vs. passing by value? and http://en.wikipedia.org/wiki/Reference_(computer_science)
References are widely used in programming, especially to efficiently pass large or mutable data as arguments to procedures, or to share such data among various uses.
EDIT
clone from underscore does a shallow copy.
Create a shallow-copied clone of the object. Any nested objects or arrays will be copied by reference, not duplicated.
To create a deep copy, the easiest way is probably to serialize and deserialize. This will do weird things if a has reference cycles though.
var b = JSON.parse(JSON.stringify(a));
You've created a reference to the same object. When you do this, any changes of variable b will affect the object stored in variable a.
You will need to do a 'clone' of the object to change it so you have two objects instead of one with two references.
when you assign b to a the assignment is by reference meaning b references the same location in memory as a so when b is updated and you observe a it looks like a is also updated.