This question already has answers here:
Why isn't this object being passed by reference when assigning something else to it?
(4 answers)
Closed 9 years ago.
var a = [3, 4, 5];
var b = [6, 7, 8];
function why() {
b = a;
b[0] = 1;
alert(a[0] + ' ' + b[0]);
}
why();
The result is a[0]=1, b[0]=1;. It seem likes JavaScript is passing by reference?
But in this case:
var a = [3, 4, 5];
var b = [6, 7, 8];
function why() {
b = a;
b = [1, 2, 3];
alert(a + ' ' + b);
}
why();
The result is a=[3,4,5] and b = [1,2,3]. Why is it passing by value?
How to avoid passing by reference?
The value of a non primitive variable, in JavaScript like in most object languages, is a reference to the object.
In the second case, you're not changing the array, but the reference that is stored in b.
If you want to copy your array, use
var c = a.slice();
Then c and a will evolve independently.
Because that's how variables work.
In the first case, you're setting both variables to the same instance of one array, then modifying that array through b (b[0] = 1). You could also modify the same array through a, but the point is b and a point to the same array; b does not "point" to a.
In the second, you're setting both to the same instance of one array (b = a) but then setting b to an instance of an entirely new, different array (b = [1,2,3]). You're changing the array that b points to, you're not affecting a because, as I said, b didn't point to a, it pointed to the same value as a.
The variables b and a are pointing to an object. When you do b = // something, you're changing where the variable points to. You're not changing the underlaying object.
In the first code, b = a means that b and a are now pointing to the same object. Therefore, when you make a change to one, it's reflected in the other.
However, in the second example, you're first pointing b to the same object as a, but then pointing b to a new array ([1,2,3]). The fact it was pointing to a for a short amount of time is irrelevant.
Both cases are pass-by-value. JavaScript is always pass-by-value, there is no way to pass by reference. In particular, the value being passed is always a pointer to an object. (Actually, primitives are passed by value directly, but the difference can only be observed for mutable values and primitives are immutable, so it doesn't make a difference.)
In the first case you are mutating the object the pointer points to. But the reference doesn't get changed, only the object the reference points to. The reference still points to the same object, the object just looks different than it did before.
This specific case of pass-by-value-where-the-value-is-a-pointer is sometimes called call-by-object-sharing, call-by-object or call-by-sharing, and is the way argument passing works in pretty much all object-oriented languages: Smalltalk, Python, Ruby, Java, C# (by default, you can pass-by-reference by explicitly specifying the ref modifier) etc.
In first case are changing one value of array item. b now equals to a so a[0] and b[0] gives same value.
In second case even though you made b point to a, you assigned a new object to b. so again b and a are two different objects ...so different values.
After all these great Answers, there is not really more to say, so heres an explanation based on the ECMAScript 5 specification.
Also a deepclone function at the end of the Answer.
As defined in the ES5 specification,
11.13.1 Simple Assignment ( = )
The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:
Let lref be the result of evaluating LeftHandSideExpression.
Let rref be the result of evaluating AssignmentExpression.
Let rval be GetValue(rref).
Throw a SyntaxError exception if the following conditions are all true:
Type(lref) is Reference is true
IsStrictReference(lref) is true
Type(GetBase(lref)) is Environment Record
GetReferencedName(lref) is either "eval" or "arguments"
Call PutValue(lref, rval).
Return rval.
So whats happening when we reach point 3 and rref is an Object, is
§8.7.1 (section 4b of In GetValue(V) //V==rref is the interesting point)
4.
If IsPropertyReference(V), then
(b) Return the result of calling the get internal method using base as its this value, and passing GetReferencedName(V) for the argument.
Now at this point rval holds the reference to the Object, which then gets put in at 5.
Where §8.7.2 (again 4b of PutValue(V,W) //V==lref , W==rval is the interesting part) comes into play.
Note: W is atm the reference to the Object you want to assign and not the value
4.
Else if IsPropertyReference(V), then
(b) Call the put internal method using base as its this value, and passing GetReferencedName(V) for the property name, W for the value, and IsStrictReference(V) for the Throw flag.
As you can see in your case the value of b which is atm a reference to the Object [6, 7, 8] gets replaced with the result of, so to speak GetValue(rref), which is a reference to [1, 2, 3];
However
Apparently you are looking for a deep clone function
Here is one.
Object.defineProperty(Object.prototype, "clone", {
value: function (deep) {
var type = Object.prototype.toString.call(this).match(/^\[object (.+?)\]$/)[1];
if (type !== "Object") {
return this.valueOf;
}
var clone = {};
if (!deep) {
for (var prp in this) {
clone[prp] = this[prp];
}
} else {
for (var prop in this) {
if (typeof this[prop] !== "undefined" && this[prop] !== null)
clone[prop] = (typeof this[prop] !== "object" ? this[prop] : this[prop].clone((typeof deep == "boolean" ? deep : (deep - 1))));
else
clone[prop] = "";
}
}
return clone;
},
enumerable: false
});
Object.defineProperty(Array.prototype, "clone", {
value: function (deep) {
var clone = [];
if (!deep) clone = this.concat();
else this.forEach(function (e) {
if (typeof e !== "undefined" && e !== null)
clone.push((typeof e !== "object" ? e : e.clone((deep - 1))));
else
clone.push("");
});
return clone;
},
enumerable: false
});
var a = [1, [2, { a: 3 } ], 4];
var b = a.clone(Infinity);
a[1][1]["a"] = "cloned";
console.log(a[1][1]["a"], b[1][1]["a"]); //"cloned" , 3
And a Demo on JSBin
To use it just call .clone(levelOfDeepness) on an Object or Array
Note: I used the Objects prototype for simplicities sake as i can call directly .clone of Object and Array elements while cloning them (gives a performance boost over the type checking variant in a single function)
Related
As someone used to python and C++, having an = copy objects by reference rather than value is not intuitive at all. Not just that, but there seems to be no direct way of copying objects to begin with. JSON.parse(JSON.stringify) is the closest option (if I know correctly), and even that has problems.
a) In a language where all variables are anyway treated as objects, why does the = operator distinguish between primitive and non-primitive data types to decide whether to copy by value or reference?
b) Why is copy by value not possible for objects?
c) What techniques are helpful for a beginner used to copying objects by value to code without it?
a) In a language where all variables are anyway treated as objects,
why does the = operator distinguish [...] ?
The =(assign) operator does not distinguish between primitive and non primitive data types. It kinda does the same for both, considering that equality is preserved after assignment (excluding exceptions e.g. NaN, ...).
b) Why is copy by value not possible for objects?
Wrong assumption in a) leads to this. Assignment is no copy and a copy of an object does not preserve equality.
Or think about:
var obj = {a: {b: 1}}
.
What is the value of obj.a ? It is just the reference to {b:1}.
c) What techniques are helpful for a beginner used to copying objects
by value to code without it?
There are many approaches for this. And two trivial cases.
As the first case one knows the layout of the object. Thus creates a template or constructor and passes all values into the corresponding properties.
As the second case one assumes a cyclic object containing everything possible in javascript (functions, regexp, symbols, undefined, ...) of depth n and builds something (not json.stringify).
For starters: possible duplicate
Assumptions:
primitive and non primitive data types have default getter,setter, ...
I guess it's because of the specific nature of JS. When you create an object like this:
let obj = {a: 3, b: 5}
And try to pass this object to another variable like this:
let obj2 = obj
You will still have only 1 object, but with 2 references, so if you try to modify obj.a it will also affect obj2.a.
That's why I created a way around for myself, which may not be the best, but here is it:
//A little helper
Object.isObject = function (object) {
return object !== null && object instanceof Object && !Array.isArray(object)
}
/*
* Array and Object copy work with each other, so every nested array are
* copied properly
*/
/*
* ARRAY
*/
Object.defineProperty(Array.prototype, 'copy', {
value: function (array){
if(!(Array.isArray(array))) throw new TypeError('passed value should be an instance of array')
if(array.length <= 0) {
console.warn('WARNING: Found nothing to copy')
return this
}
this.splice(0, this.length)
for(let i = 0; i < array.length; i++) {
if (Array.isArray(array[i])) this[i] = Array().copy(array[i])
else if (Object.isObject(array[i])) this[i] = Object().copy(array[i])
else this[i] = array[i]
}
return this
},
enumerable: false
})
/*
* OBJECT
*/
Object.defineProperty(Object.prototype, 'copy', {
value: function (object){
if(!object || !(Object.isObject(object))) return false
if(Object.entries(object) <= 0) {
console.warn('WARNING: Found nothing to copy')
return this
}
const props = Object.getOwnPropertyNames(this)
for (let i = 0; i < props.length; i++) delete this[props[i]]
const keys = Object.keys(object)
const values = Object.values(object)
for (let i = 0; i < keys.length; i++) {
if(Array.isArray(values[i])) this[keys[i]] = Array().copy(values[i])
else if(Object.isObject([values[i]])) this[keys[i]] = Object().copy(values[i])
else this[keys[i]] = values[i]
}
return this
},
enumerable: false
})
//create 2 arrays
let a = [3, 5, {a: 5}, [3, 1]]
let b = []
//copy array of a
b.copy(a)
//modify values
b[0] = 6
b[2].a = 1
b[3][0] = 'test'
console.log(a) //source
console.log(b)
As you can see in the example these 2 arrays (a and b) are completely different and have no relation to each other.
P.S. Sorry if I wrote something wrong, my english is not that good :O
I was reading somewhere that when we pass an object into a function "...JavaScript always uses the Object by reference when it passes as argument..." What I think this implies is (correct me if I'm wrong) is that if the function was to modify the object in some way, it would change the original defined object. I tried illustrating this with some code and it does do what I think it does but when I try the example in the blog post with a Number obj, it doesn't change the original value in that object. Please see my jsbin: https://jsbin.com/wociro/edit?js,console,output
console.clear();
/**myobject Object**/
function myobject() {
this.value = 5;
}
var o = new myobject();
console.log("Original value of o: " + o.value); // o.value = 5
function objectchanger(fnc) {
fnc.value = 6;
}
objectchanger(o);
console.log("New value of o: " + o.value); // o.value is now equal to 6
/*Number Object*/
var num2 = new Number(2);
console.log("Original value of num2: " + num2);
function numberChanger(fnc) {
return fnc + 1;
}
console.log("num2 after running numberChanger: " + numberChanger(num2));
console.log("New value of num2: " + num2); //looks the same
Am I missing something?
Number objects are still objects. So their value is a reference, and if a function alters a property of an object passed as an argument, that object will be affected outside the function.
function changer(obj) {
obj.foo = 'bar';
}
var num = new Number(123);
console.log(num.foo); // undefined
changer(num);
console.log(num.foo); // 'bar'
However, the value wrapped inside the number object is not stored as a property. It's stored as a [[NumberData]] internal slot. ECMAScript provides no way to alter that slot, so you can't change the number.
Your attempt of fnc+1 unwraps the number object to get its [[NumberData]], and adds 1 to that. But the result is just discarded, it's not stored back in the [[NumberData]] slot of fnc.
If you want to be able to achieve something analogous to changing the [[NumberData]], you can
function MyNumber(num) {
this.__number__ = +num;
}
MyNumber.prototype = Object.create(Number.prototype);
Object.getOwnPropertyNames(Number.prototype).forEach(function(prop) {
var desc = Object.getOwnPropertyDescriptor(Number.prototype, prop);
if(desc && desc.value && typeof desc.value == 'function') {
var native = desc.value;
desc.value = function() {
return native.apply(this.__number__, arguments);
};
Object.defineProperty(MyNumber.prototype, prop, desc);
}
});
var num = new MyNumber(123);
console.log(+num, num+'', num.toFixed(2)); // 123, "123", "123.00"
num.__number__ = 456;
console.log(+num, num+'', num.toFixed(2)); // 456, "456", "456.00"
I actually had a lot issues when I started getting into the object side of JavaScript myself. Best way I can explain is by these examples.
Objects link.
var obj = {a: 5};
var b = obj.a;
b = 2;
// obj.a: 2
// b: 2
This will link to the object value I believe. So if you change b it will also change obj.a.
HTML DOM object link with odd behavior
var x = document.getElementById("some_div_id");
x.innerHTML = "example"; // this works
var x = document.getElementById("some_div_id").innerHTML;
x = "example"; // this doesn't, it thinks that it's document.getElementById("some_div_id");
Took me time to figure what was wrong when I first did the second DOM method.
Variables are not linked but copied.
var a = 5;
var b = a;
b = 2;
// a: 5
// b: 2
As you can see, this doesn't link the value but creates a new one based from it.
Deep copying from objects trick.
function deepCopy(objValue) {
return JSON.parse(JSON.stringify(objValue));
}
var obj = {a: 5};
var b = deepCopy(obj.a);
b = 2;
// obj.a: 5
// b: 2
This was a trick given to me some time back when I had issues wanting a object value being stored in a variable and edited but without it being linked to the object value. After a while I found I never needed it after improving my coding skills.
Also last note. I read somewhere in clean JavaScript coding that you shouldn't need to use the new object method unless it's a Date() object or or simulated class, or you may run into typeof and value check issues with ===.
Can't be certain if this is error free but hope this helps explains better.
In Javascript, objects refer to an array, indicated by [] or an object {}. You can verify the type of the variable by using typeof. These are passed by reference.
typeof [2, 5, 3] //object
typeof { a: 10} // object
If you pass the object literal to a function and modify the value of the property 'a', it would result in the value being modified.
When I use a for ... in loop like:
for(var i in object) {}
I want to know if the object in question is evaluated just once or each time the loop, well, loops.
I made a quick test in my browser (also in node) like:
for(var i in (console.log('INIT'), [1, 2, 3, 4])) { console.log('KEY', i); }
and I get:
INIT
KEY 0
KEY 1
KEY 2
KEY 3
So from this empirical evidence, I could assume that is indeed only evaluated once.
But, is this behavior standard?
From the Mozilla documentation, a for...in loop will iterate over all enumerable properties of the object itself and those the object inherits from its constructor's prototype. An (enumerable) property that is deleted before it has been visited will not be visited later. Properties added to the object over which iteration is occurring may either be visited or omitted from iteration.
In short, the outcome of the example posted by #Amit is not guaranteed, although Chrome and IE may use a different specification for the for ..in loop. However, at least deleting an element seems to prevent it from being visited in Chrome:
var obj = { a: 1, b: 2, c: 3 };
for(var k in obj) {
document.write(k);
delete obj['c'];
}
Bear in mind that (console.log('INIT'), [1, 2, 3, 4]) is an expression that evaluates to [1, 2, 3, 4] so it's not a valid evidence of the issue. A better empirical evidence can be obtained with:
var obj = { a: 1, b: 2, c: 3 };
for(var k in obj) {
document.write(k);
obj.d = 4;
}
And we don't see "d"...
The ECMAScript® Language Specification, section 12.6.4 does not say much about this, except that the expression on the right side is evaluated once and cast to an object:
Let exprRef be the result of evaluating the Expression.
Let experValue be GetValue(exprRef).
If experValue is null or undefined, return (normal, empty, empty).
Let obj be ToObject(experValue).
This says nothing about evaluating any of the keys or values of that object at that time.
In fact, the part that follows the quoted paragraph suggests the keys could be retrieved during the iterations:
Repeat
Let P be the name of the next property of obj whose [[Enumerable]] attribute is true. If there is no such property, return (normal, V, empty).
But there is no requirement in either direction. So, ... this could be implementation (browser) dependent.
When you say that the object is evaluated just once, that can be have different meanings:
The object reference is evaluated just once. This is important when that object is the outcome of an expression, such as a function call. But this is surely taken for granted and not what you mean;
The object's keys (properties) are enumerated once, but the values are retrieved on demand;
The object's keys and values are retrieved once. This could be done at 1 level or nested levels at a potential high memory cost.
This test case shows in my browser (FireFox) the second happens, not the third:
var obj = {a: 1, b: 2, c: 3};
for (key in obj) {
document.write('obj[' + key + '] = ' + obj[key] + '<br>');
if (key=='a') {
// modify a value that is still to be visited
obj["c"] = 4;
// add a key/value pair
obj["d"] = 9;
}
}
Output is (as you can probably also see in your browser):
obj[a] = 1
obj[b] = 2
obj[c] = 4
So the keys were only retrieved once, the values on demand (i.e. not at the start of the loop).
I have this:
var g = [{a:'a'},{a:'2'},{a:'3'}]
var c = [{a:'4'},{a:'2'},{a:'5'}]
The following statement:
g[1] == c[1]
Returns false, even though the objects look the same. Is there any way I can compare them literally so it will return me true instead of false?
You could encode them as JSON:
JSON.stringify(g[1]) == JSON.stringify(c[1])
You might also be interested in the answers to this related question on identifying duplicate Javascript objects.
For a more complex option, you might look at the annotated source code for Underscore's _.isEqual() function (or just use the library).
The == operator checks for reference equality. The only way to do what you want would be a memberwise equality test on the objects.
This ought to work:
function memberwiseEqual(a, b) {
if(a instanceof Object && b instanceof Object) {
for(key in a)
if(memberwiseEqual(a[key], b[key]))
return true;
return false;
}
else
return a == b;
}
Be wary of cases like these:
var a = {};
a.inner = a; //recursive structure!
here you compare the reference in the memory ,not its value try this and you will get true :
g[1]['a'] === c[1]['a']
In JavaScript variables which are objects (including arrays) are actually references to the collection. So when you write var x = { a: 2 } and var y = { a: 2 } then x and y become references to two different objects. So x will not equal y. But, if you did y = x then they would (because they would share the same reference). But then if you altered either object the other would be altered too.
So, when dealing with objects and arrays, by saying == you are checking if the two references are the same. In this case they are not.
For example:
var a = [];
function p(x) { a.push(x); }
[[p(1),p(2)],p(3),[p(4),[p(5)]],p(6)]
a == [1,2,3,4,5,6] // Always true?
Is 'a == [1,2,3,4,5,6]' defined behavior? Can it be relied upon?
Are elements of Javascript arrays processed in a defined order?
Yes they are.
Is 'a == [1,2,3,4,5,6]' defined behavior? Can it be relied upon?
No, the equals operator performs referential equality when comparing object.
Short answer: "Yes".
Longer answer: Your question is actually about JavaScript statements in general and not Arrays. The code you posted ([[p(1),p(2)],p(3),[p(4),[p(5)]],p(6)]) is not an Array, it is a statement that returns an Array whilst also populating an Array. JavaScript statements are executed according to the rules of operator precedence.
Your question is not very clear, what do you mean by "processed"?
var a = [];
That is an array literal, it assigns a reference to an empty array to a.
function p(x) { a.push(x); }
Each time push() is called, a new element is added to a at index a.length (i.e. it is always added after the highest index).
[[p(1),p(2)],p(3),[p(4),[p(5)]],p(6)]
That is an array literal that is equivalent to:
a = [];
a[0] = [p(1),p(2)];
a[1] = p(3);
a[2] = [p(4),[p(5)]];
a[3] = p(6);
The following expression:
a == [1,2,3,4,5,6]
is always false, since arrays are objects and objects are never equal to any other object. You can do:
var a = [0, 1];
var b = a;
a == b; // true since a and b reference the same array
But
var a = [0, 1];
var b = [0, 1];
a == b; // false since a and b reference different arrays