Here I have a simple code to understand how Object.create() works. Here I have a common object to be used as prototype which is prototypeObj.newObj is an object which has its prototype set to prototypeObj. Here I have another object called session which has a property called anotherObj, and anotherObj has the same prototype as newObj. But adding new value to a property called foo which resides in the prototype of anotherObj , affect newObj too. Why am I experiencing this behaviour?
Code:
var prototypeObj = {
foo: [],
addItemToFoo: function(add) {
this.foo.push(add);
}
}
function create(fooVal) {
var myProto = Object.create(prototypeObj);
myProto.foo = fooVal;
return myProto;
}
var newObj = create([1, 2]); // initialized with [1,2]
session = {
anotherObj: create(newObj.foo) // initialized with [1,2]
}
session.anotherObj.addItemToFoo(6); // pushed 6 to session.anotherObj.foo
console.log("newObj.foo is " + newObj.foo); // newObj also get 6 pushed to its foo property
console.log("anotherObj.foo is " + session.anotherObj.foo);
foo is an array, it works by reference.
anotherObj: create(newObj.foo)
You are copying the reference here, so both your old and new object will have the same array reference to insert elements in. If you want to have to different array references, you should first copy it like this create(newObj.foo.slice())
https://jsfiddle.net/x8ftnh82/
Related
Object.freeze() seems like a transitional convenience method to move towards using const in ES6.
Are there cases where both take their place in the code or is there a preferred way to work with immutable data?
Should I use Object.freeze() until the moment all browsers I work with support const then switch to using const instead?
const and Object.freeze are two completely different things.
const applies to bindings ("variables"). It creates an immutable binding, i.e. you cannot assign a new value to the binding.
Object.freeze works on values, and more specifically, object values. It makes an object immutable, i.e. you cannot change its properties.
In ES5 Object.freeze doesn't work on primitives, which would probably be more commonly declared using const than objects. You can freeze primitives in ES6, but then you also have support for const.
On the other hand const used to declare objects doesn't "freeze" them, you just can't redeclare the whole object, but you can modify its keys freely. On the other hand you can redeclare frozen objects.
Object.freeze is also shallow, so you'd need to recursively apply it on nested objects to protect them.
var ob1 = {
foo : 1,
bar : {
value : 2
}
};
Object.freeze( ob1 );
const ob2 = {
foo : 1,
bar : {
value : 2
}
}
ob1.foo = 4; // (frozen) ob1.foo not modified
ob2.foo = 4; // (const) ob2.foo modified
ob1.bar.value = 4; // (frozen) modified, because ob1.bar is nested
ob2.bar.value = 4; // (const) modified
ob1.bar = 4; // (frozen) not modified, bar is a key of obj1
ob2.bar = 4; // (const) modified
ob1 = {}; // (frozen) ob1 redeclared
ob2 = {}; // (const) ob2 not redeclared
Summary:
const and Object.freeze() serve totally different purposes.
const is there for declaring a variable which has to assinged right away and can't be reassigned. variables declared by const are block scoped and not function scoped like variables declared with var
Object.freeze() is a method which accepts an object and returns the same object. Now the object cannot have any of its properties removed or any new properties added.
Examples const:
Example 1: Can't reassign const
const foo = 5;
foo = 6;
The following code throws an error because we are trying to reassign the variable foo who was declared with the const keyword, we can't reassign it.
Example 2: Data structures which are assigned to const can be mutated
const object = {
prop1: 1,
prop2: 2
}
object.prop1 = 5; // object is still mutable!
object.prop3 = 3; // object is still mutable!
console.log(object); // object is mutated
In this example we declare a variable using the const keyword and assign an object to it. Although we can't reassign to this variable called object, we can mutate the object itself. If we change existing properties or add new properties this will this have effect. To disable any changes to the object we need Object.freeze().
Examples Object.freeze():
Example 1: Can't mutate a frozen object
object1 = {
prop1: 1,
prop2: 2
}
object2 = Object.freeze(object1);
console.log(object1 === object2); // both objects are refer to the same instance
object2.prop3 = 3; // no new property can be added, won't work
delete object2.prop1; // no property can be deleted, won't work
console.log(object2); // object unchanged
In this example when we call Object.freeze() and give object1 as an argument the function returns the object which is now 'frozen'. If we compare the reference of the new object to the old object using the === operator we can observe that they refer to the same object. Also when we try to add or remove any properties we can see that this does not have any effect (will throw error in strict mode).
Example 2: Objects with references aren't fully frozen
const object = {
prop1: 1,
nestedObj: {
nestedProp1: 1,
nestedProp2: 2,
}
}
const frozen = Object.freeze(object);
frozen.prop1 = 5; // won't have any effect
frozen.nestedObj.nestedProp1 = 5; //will update because the nestedObject isn't frozen
console.log(frozen);
This example shows that the properties of nested objects (and other by reference data structures) are still mutable. So Object.freeze() doesn't fully 'freeze' the object when it has properties which are references (to e.g. Arrays, Objects).
Let be simple.
They are different. Check the comments on the code, that will explain each case.
Const - It is block scope variable like let, which value can not reassignment, re-declared .
That means
{
const val = 10; // you can not access it outside this block, block scope variable
}
console.log(val); // undefined because it is block scope
const constvalue = 1;
constvalue = 2; // will give error as we are re-assigning the value;
const obj = { a:1 , b:2};
obj.a = 3;
obj.c = 4;
console.log(obj); // obj = {a:3,b:2,c:4} we are not assigning the value of identifier we can
// change the object properties, const applied only on value, not with properties
obj = {x:1}; // error you are re-assigning the value of constant obj
obj.a = 2 ; // you can add, delete element of object
The whole understanding is that const is block scope and its value is not re-assigned.
Object.freeze:
The object root properties are unchangeable, also we can not add and delete more properties but we can reassign the whole object again.
var x = Object.freeze({data:1,
name:{
firstname:"hero", lastname:"nolast"
}
});
x.data = 12; // the object properties can not be change but in const you can do
x.firstname ="adasd"; // you can not add new properties to object but in const you can do
x.name.firstname = "dashdjkha"; // The nested value are changeable
//The thing you can do in Object.freeze but not in const
x = { a: 1}; // you can reassign the object when it is Object.freeze but const its not allowed
// One thing that is similar in both is, nested object are changeable
const obj1 = {nested :{a:10}};
var obj2 = Object.freeze({nested :{a:10}});
obj1.nested.a = 20; // both statement works
obj2.nested.a = 20;
Thanks.
var obj = {
a: 1,
b: 2
};
Object.freeze(obj);
obj.newField = 3; // You can't assign new field , or change current fields
The above example it completely makes your object immutable.
Lets look following example.
const obj = {
a: 1,
b: 2
};
obj.a = 13; // You can change a field
obj.newField = 3; // You can assign new field.
It won't give any error.
But If you try like that
const obj = {
a: 1,
b: 2
};
obj = {
t:4
};
It will throw an error like that "obj is read-only".
Another use case
const obj = {a:1};
var obj = 3;
It will throw Duplicate declaration "obj"
Also according to mozilla docs const explanation
The const declaration creates a read-only reference to a value. It
does not mean the value it holds is immutable, solely that the
variable identifier can not be reassigned.
This examples created according to babeljs ES6 features.
in following code I declare two objects of class "person".
problem is that when for one of the variables ("Courses") I use push method to update its values so they are copied in proto and as a consequence both objects share same "Courses" array inside there proto. i want them to have there own unique arrays.
var person = {
Name: "John",
Grade: 0,
Courses:[],
setGrade : function(y){
this.Grade=y;
},
setCourse:function(t){
this.Courses.push(t);
},
}
var grade9 = Object.create(person);
grade9.setCourse("eng");
grade9.setCourse("math");
grade9.setGrade(9);
var grade10 = Object.create(person);
grade10.setCourse("phy");
grade10.setCourse("chem");
grade10.setCourse("bio");
grade10.setGrade(10);
debug output
thanx.
Create a factory method, and inside overshadow the property with a specific property for the current instance:
function createNewPerson() {
return Object.create(person, { // object.create with propertiesObject to overshadow original Courses property
Courses: { writable: true, configurable: true, value: [] }
});
}
var grade9 = createNewPerson();
grade9.setCourse("eng");
grade9.setCourse("math");
grade9.setGrade(9);
var grade10 = createNewPerson();
grade10.setCourse("phy");
grade10.setCourse("chem");
grade10.setCourse("bio");
grade10.setGrade(10);
Object.create returns an object with the prototype property set to the passed-in object. The 'instance' objects delegate to the prototype. The same prototype. In C terms they all have pointers to the same struct (the prototype) and modifying it changes the value for all the 'instance' objects (they're only pointing to it). While that isn't totally accurate its enough so for our purposes here. If you want them to all to have independent copies you'll have to add them:
personFactory = function() {
newPerson = Object.create(person);
newPerson.array = [];
}
myPerson = personFactory();
Ok I am killing my brain here I have an array
var myArray = ['Bob', 'Sue', 'Jim'];
myArray.__proto__ = new Entity();
//Entity looks something like this
Entity = function(){
this.isChanged = false;
this.add = function(newPerson){
alert(this.length); //alerts with 3
alert(JSON.stringify(this)); //alerts a {}
this.push(newPerson);
this.isChanged = true;
}
}
push does not exist on an object but its obviously an array as per the alert returning a 3.
very curious how to access my array that seems to be wrapped by an object thanks to my proto
how to access my array that seems to be wrapped by an object thanks to my __proto__
It is not wrapped - it just lost it's identity due to your modification of __proto__. The array now inherits from your new Entity instance instead of from Array.prototype.
If you want to call Array methods on it, you will have to do it using .call:
Array.prototype.push.call(this, newPerson);
However, your implementation of inheritance is questionable anyway. Even if you use an array object and mutate its [[prototype]], you rather should be doing
var myArray = new Entitiy(['Bob', 'Sue', 'Jim']);
// Entity looks like this
function Entity(arr) {
if (!Array.isArray(arr)) {
arr = [];
// maybe:
// arr.push.apply(arr, arguments);
}
arr.__proto__ = Entity.prototype;
arr.isChanged = false;
return arr;
}
Entity.prototype = Object.create(Array.prototype);
Entity.prototype.constructor = Entity;
Entity.prototype.add = function(newPerson) {
alert(this.length); //alerts with 3
alert(JSON.stringify(this)); //alerts a ["Bob","Sue","Jim"]
this.push(newPerson);
this.isChanged = true;
};
The array isn't wrapped, it's no longer an array! __proto__ is a depricated getter/setter pair to access an objects internal [[Prototype]]. Since you assign a value the setter is used and you simply overwrite its complete prototype with an instance of Entity. That's why push() (and all others: pop(), splice(), ... ) doesn't exist any longer.
Why alert(this.length);works ? length is not a property of Array.prototype but an own property of each Array instance. So it's not overwritten/removed by changing the prototype, your "thing" still has a length. You can check that with following:
console.log(Object.getOwnPropertyNames(myArray)); // --> ['0', '1', '2', 'length']
Of course you can access the properties of your "thing", e.g. console.log(myArray[1]) // --> 'Sue'or assign new properties, but you have to use object-methods for it. So if inside Entity() instead of this.push(newPerson) you use this[this.length] = newPerson it will work.
Reference for __proto__ here.
If I have this object:
var myclass = {
foo: {
bar: function(var) {}
},
some: {
bar: function(var) {}
}
}
and I want to call the bar function depending on a variable that defines the parent level of the object like this:
var part = "some";
myclass.part.bar(var);
How can I do?
You can do it using array access notation:
myclass[part].bar(var);
JavaScript objects are like associative arrays, and you can use a property name to either set or get the property's value, you can even create new properties with this syntax.
For example:
var obj = { a : 1 };
console.log(obj["a"]); // 1
obj["b"] = 2; // this creates a property called b and assigns 2 as the value
console.log(obj["b"]); // 2
You can keep a reference to a function as a variable, which is a little cleaner than a string.
var func = myclass.foo.bar;//or myclass.some.bar
...
func.call(myclass, var);
Or keep a reference to the part:
var part = myclass.foo;//or myclass.some
part.bar.call(myclass, var);
I have a function that looks like the function below. It takes an object called
link, calls getAdminParams and then uses the return values of that call to
change properties in the link object:
function checkParams(link: Link) {
var rtn : IAdminParams = null,
table = null;
if (link.Action === "Create") {
if (link.Params == null) {
rtn = getAdminParams(link.Entity);
if (rtn.Success) {
link.Url = link.Href + rtn.Param;
table = rtn.Table;
} else {
link.$Link.prop('disabled', false);
return;
}
} else {
link.Url = link.Href + link.Params;
table = link.Entity;
}
} else {
link.Url = link.Href;
}
}
I am calling the function as below.
function adminDialog($link: JQuery) {
var link = new Link($link);
checkParams(link);
doDialogAjax(link);
}
When I pass the value of link to the checkParams(link) will it be passed by
reference? In other words will the changes I make in the checkParams(link: Link)
function be available to the link object so they can be used in the doDialogAjax
function?
Yes, and no.
"When I pass the value of link to the checkParams(link) will it be
passed by reference?"
No.
"Will the changes I make in the checkParams(link: Link)
function be available to the link object so they can be used in the
doDialogAjax function?"
Yes.
The variable is not passed by reference. It's passed by value, but the value is a reference.
If the variable was passed by reference, the function could change the variable. That doesn't happen:
function change(obj) {
// change the object
obj.value = 42;
// replace it with a new object
obj = { value: 1 };
}
// create an object
var a = { value: 0 };
// create another variable, to use in the call to the function
var b = a;
change(b);
alert(a.value); // shows 42, as the function changes the object passed in
alert(b.value); // shows 42, not 1, as the variable b is not changed by the function
Demo: http://jsfiddle.net/sCJHu/
If the variable was passed by reference, b would be replaced by the new object create in the function, and b.value would be 1.
Yes, the changes you make in the function receiving the object are visible in other uses of the objects.
Simple test :
​function increment(obj) {
obj.a++;
}
var myobj = {a:3};
increment(myobj);
document.write(myobj.a); // prints 4
Demonstration
Answering a comment : arrays are objects and it works the same.
You might see, in this precise case, the passing mode as a "pass by reference" but that's just using some vocabulary of another language : you pass the value of the variable and variable values for objects are their references. The problem with this vocabulary is exemplified by Archer's mistake : you can't replace the variable's value :
​function increment(obj) {
obj = {a:4}; // doesn't change the passed object, just the local variable obj
}
var myobj = {a:3};
increment(myobj);
document.write(myobj.a); // prints 3 because myobj isn't changed
To say it otherwise : a function can modify an object that is the value of an external variable passed to it, it can't replace it. Because you don't pass a reference to original variable as you would do in C.
When I pass an object to a javascript function does that object get passed by reference?
A reference to the object is passed to the function (this is not "pass by reference", that's a different thing entirely). A simpler example helps make this clear:
function foo(o1) {
console.log("foo1: o1.a = " + o1.a);
o1.a = "updated"; // <=== MODIFYING the object
console.log("foo2: o1.a = " + o1.a);
}
function bar(o2) {
console.log("bar1: o2.a = " + o2.a);
o2 = {a: "updated"}; // <=== Pointing to a DIFFERENT object (no effect outside function)
console.log("bar2: o2.a = " + o2.a);
}
var x = {a: "original x"};
foo(x); // Logs "foo1: o1.a = original x",
// then "foo2: o1.a = updated"
console.log(x.a); // Logs "updated"
var y = {a: "original y"};
bar(y); // Logs "bar1: o2.a = original y",
// then "bar2: o2.a = updated"
console.log(y.a); // Logs "original y", *NOT* "updated"
Note that this is pass by value; the value in question with objects is an object reference. You may see people calling it "pass by reference," but that's fundamentally wrong. If a variable is "pass by reference," that means you can change the variable's value in the calling code. E.g., it would mean our functions could change what x and y refer to. But they can't, as we can see from bar above. They can only change properties on the objects that x and y refer to, not x and y themselves.