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.
Related
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.
I'm coming to javascript from C background. In javascript, when I use the assignment operator to assign one object to another, does it copy the values from one to the another, or do they both now point to the same data?. Or does the assignment operator do anything in this case?
function point_type()
{
this.x = 0;
this.y = 0;
}
var pnt1 = new point_type();
var pnt2 = new point_type();
pnt1.x = 4;
pnt1.y = 5;
pnt2 = pnt1;
pnt1.x = 8;
pnt2.y = 9;
In the example above, does pnt2.x now equal 8, or does it still equal 4, or does it still equal 0?
Yes, I realize I can test this myself, and I will be doing that while I wait for the community to come up with an answer. However, I'm hoping the answer to my question will go one step past just answering this one example and might shine some light on how javascript objects work and some best practices.
Follow up question:
The answer seems to be that the reference is copied. pnt2 and pnt1 now point to the same data. Is it possible to set up my object so that the values are copied? How is this usually accomplished in javascript? Clearly I don't want to set each attribute individually every time I need to copy this object.
Whenever I need to copy one object to another in JS, I just cast it to a primitive:
var newObject = JSON.stringify(oldObject);
Then when I need to use it:
var evenNewerObj = JSON.parse(newObject);
Hope this helps someone.
In JavaScript, primitive types are copied by value and reference types are copied by reference. More info here: http://docstore.mik.ua/orelly/web/jscript/ch09_03.html
It equals 8.
pnt2 = pnt1
That statement is pointing the pnt2 object to the pnt1 object so any modification you do to pnt1 will show up in pnt2.
Given the object you showed in your example, it is setting a reference to the object. If it were a primitive type (number, date) then it would copy the object.
Let's say there is a simple object literal which name will never change:
var car = {
wheels : 4,
construct : function() {
var that = this;
setTimeout(function() {
console.log(that.wheels);
console.log(car.wheels);
}, 500);
}
};
My question is: Which way is better? Referencing by the object's name or creating a new variable (which may take some time and memory and probbaly must be done in multiple functions)?
Within the object, you should always refer to the object via this (or a copy of it, e.g. that, if required) to prevent the following breakage:
var car = ...
// do stuff
car = undefined; // or anything else, perhaps by a code hacker in the JS console
// class is now broken
You should treat the variable name that happens to have been given to your object on the outside as unknown to you, and subject to change.
Someone else might call it something else, there might be multiple names, the name might suddenly point at some other object altogether. Such variables are for the benefit of the "owners" of references to the object, and not for the object itself.
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.
this is my very first post! I have a quick question in regarding inheritance in javascript.
Of course the 'extend2' method is used to inherit child objects from the parent object using a for-in loop.
var extend2 = function (child, parent) {
var c = child.prototype;
var p = parent.prototype;
for (var i in p) {
c[i] = p[i];
}
}
I'm currently reading "Object Oriented Javascript" by Stoyan Stefanov. It's an awesome book.
Can anyone give me a good detailed explanation of how the child's prototype object is not entirely overwritten or replaced but it's just augmented?
How is it that when the objects inherit, they copy (primitive data types) instead of being looked up as a reference by using the extend2 function?
This would really help thanks!
Primitive data types in javascript are passed via value, rather than reference. Thus when you copy a value it is actually copying it, not referring to it.
Traditionally this is because a primitive was literally encoded in the memory in such a way (so the primitive int 7 would be encoded in memory as 0x7. When dealing with objects, however, they are encoded as a pointer to the memory location where the actualy object is. Thus, when you copy the value it is merely a copy of the reference pointer, not the object that that pointer referrs to.
As for the fact that the child's prototype is not replaced, that is because a prototype in java is merely another object. So a prototype may look like:
{
someField: 5
}
Which would indicate that instances of the object would initialize with a field called someField whose value is 5. with the above code, each entry in the object is copied to the child prototype, but nothing is deleted. Thus if the child prototype looks like:
{
someField: 10
someOtherField: 3
}
Then performing the above extend2 command will overwrite someField, but not someOtherField, so the resulting prototype would be:
{
someField: 5
someOtherField: 3
}