Javascript assigning prototype confusion - javascript

var object1 = {
name: "abc",
age: 21
}
var Object2 = function() {}
Object2.prototype = object1;
Object2.prototype.hello = function() {
console.log("Hello");
}
var obj = Object.create(object1);
for (var prop in obj) {
console.log(prop + ": " + obj[prop]);
}
The output of this code is:
name: abc
age: 21
hello: function () {
console.log("Hello");
}
Obj is created by setting its prototype as object1, which doesn't have the 'hello' function, so why is it listed in the output? If I comment out 'Object2.prototype = object1;', the 'hello' function no longer shows in the output. I don't see how obj and Object2 are connected. Can anyone please explain what's happening here?

After this line...
Object2.prototype = object1;
... both Object2.prototype and object1 refer to the same object. Hence this line...
Object2.prototype.hello = function() { // ...
... assigns a new property to the object referred by object1.
I don't see how obj and Object2 are connected.
With var obj = Object.create(object1), you create an object which __proto__ property points to object1. The latter, as we established, is actually the same object Object2.prototype points to.
I.e setting Object2.prototype to an object without them both referring to the same object?
If you only want Object2.prototype to copy properties from object1, just clone an object.

Related

JavaScript access unknown properties of object

Situation:
I have multiple JS-Objects with the same structure.
In all of those Objects there's a property called console.
I'd like to assign a value (the same value) to each of these properties.
Please find below the structure of my code:
NameOfMyObject = function() {
return {
actions: null,
console: null,
init: function() {},
//object specific functions
}
};
My Problem:
Currently I'm assigning the value manual, but I want do do it generic.
Code snippet
this.console = console;
this.actions.console = console;
this.fixtures.console = console;
How can I access the properties of my objects?
I hope I asked my question clear enough.
Here is how you would share a property across objects:
function MyClass() {};
MyClass.prototype.console = {}; // Define a "common" property on the prototype
var obj1 = new MyClass(); // Create two instances
var obj2 = new MyClass();
Object.getPrototypeOf(obj1).console.id = 13; // Assign a value once...
console.log(obj1.console.id); // ... and it exists on both instances
console.log(obj2.console.id);
The shared property is on the prototype object.
Instead of Object.getPrototypeOf(obj1) you can of course use MyClass.prototype, since you know that obj1 was created by MyClass. They give the same prototype object.
If your property always has an object as value and you don't need to replace that value, only mutate it by setting properties on that object, then you don't need to explicitly reference the prototype to set a new value.
So this works:
function MyClass() {};
MyClass.prototype.console = {}; // Define a "common" property on the prototype
var obj1 = new MyClass(); // Create two instances
var obj2 = new MyClass();
obj1.console.id = 13; // Assign a value once... (getting console from the prototype)
console.log(obj1.console.id); // ... and it exists on both instances
console.log(obj2.console.id);
But if you change console itself, you'll be setting it on the instance, not on the prototype:
function MyClass() {};
MyClass.prototype.console = {}; // Define a "common" property on the prototype
var obj1 = new MyClass(); // Create two instances
var obj2 = new MyClass();
obj1.console = { id: 13}; // Setting an instance property now
console.log(obj1.console.id); // ... and it does not exist on both instances
console.log(obj2.console.id); // == undefined
So if that kind of assignment should still work on the prototype, you need to use the first code block with Object.getPrototypeOf or MyClass.prototype.

Object returns own property and preserves methods

I have an object that wraps some data:
function Obj1() {
var _foo = 'bar'
this.obj2 = {
'b': 'c'
}
this.method = function() {
return _foo
}
}
var obj1 = new Obj1()
Now when I call console.log(obj1); I want it to show me object obj2 content. The trick is that I need to still be able to call obj1.method and get value of _foo. How do I do that if it's even possible?
My thought was that sth like getter will be suitable, but can't figure out where and how to assign one.
As far as I understood you're trying to hide method property. To achieve this, use Object.defineProperty. Function will not be logged because enumerable property is false by default which prevents property from showing in console.log for example.
function Obj1() {
var _foo = 'bar'
this.obj2 = {
'b': 'c'
}
Object.defineProperty(this.obj2, 'method', {
value: function() {
return _foo;
}
});
return this.obj2;
}
var obj1 = new Obj1()
console.log(obj1);
console.log(obj1.method());
if i understand correctly, you can use prototype
Example
function Obj1() {
this.obj2 = {
'b': 'c'
}
}
Obj1.prototype.method = function() {
return 'bar';
}
var obj1 = new Obj1();
//prints only properties
console.log(obj1);
//prints method result
console.log(obj1.method())
Since you calling new Obj1(). The result variable var obj1 is a class object and not a function, for you to get the value of obj2 you will have to call obj1.obj2 in your console log. If you want obj1 to hold the value of obj2. Then use the following code
function Obj1() {
var obj2 = {
'b': 'c'
}
return this.obj2;
}
var obj1 = Obj1();
console.log(obj1);
This will give you the required result in the console log, but the object will no longer be a class object and will have only the value of obj2.
Sticking to your original snippet a factory looks like a good option:
function factory() {
var foo = 'bar';
var props = { b: 'c'};
var proto = {
method: function() { return foo; }
};
var obj = Object.create(proto);
Object.assign(obj, props);
return obj;
}
var obj = factory();
console.log(obj); // {b: 'c'}
console.log(obj.method()) // 'foo'
You could even pass props as an argument to get a more flexible way of spawning objects with an "unenumerable" method accessing private members.

object generator functions, 'this' and closed upon variable act the same [duplicate]

Is it problematic to reference an object literal within a function which is part of that very literal? It seems to work just fine, but I want to make sure there aren't other implications.
Here's an example of what I'm talking about:
instead of:
var obj = {
key1: "it",
key2: function(){return this.key1 + " works!"}
};
alert(obj.key2());
using:
var obj = {
key1: "it",
key2: function(){return obj.key1 + " works!"}
};
alert(obj.key2());
Both can be problematic.
var obj = {
key1: "it",
key2: function(){ return this.key1 + " works!" }
};
var func = obj.key2;
alert(func()); // error
When func is not called as a method of obj, this can reference something else (in here: the global object "window").
var obj = {
key1: "it",
key2: function(){ return obj.key1 + " works!" }
};
var newref = obj;
obj = { key1: "something else"; };
alert(newref.key2()); // "something else works"
In here we access the object from another reference, though the obj in the function may now point to some other object.
So you will have to choose which case is more likely. If you really want to make it safe, prevent obj from being exchanged:
// ES6 - use `const`:
const obj = {
key1: "it",
key2: function(){ return obj.key1 + " works always!" }
};
// ES5: use a closure where the `obj` is stored in a local-scoped variable:
var obj = (function(){
var local = {
key1: "it",
key2: function(){ return local.key1 + " works always!" }
};
return local;
})();
or you bind() the function to the object:
var obj = {
key1: "it",
key2: function(){ return this.key1 + " works always!" }
}
obj.key2 = obj.key2.bind(obj);
There will be a difference in variable scope binding. If you modify obj later, you will modify the return value of key2:
var newobj = obj;
obj = { key1: "new" };
alert(newobj.key2());
Now it alerts "new works!", because even though you are calling key2() on the original object (which is now newobj), the reference to obj.key1 now binds to the value of the new obj instance. Using this prevents this from happening.
Demo: http://jsfiddle.net/m6CU3/
If you are not using prototype object, you czn go like that. as all instances of your object will return the value of the obj instance...
Either or both of those techniques may apply depending on the situation.
The value of this within a function depends on how the function was called. If you call a function as property of an object like this:
obj.key2();
//or
obj["key2"]();
Then this will be that object. Whether the object was created via an object literal or some other means is not relevant.
But you can use .call() or .apply() to call a function and explicitly set this to some other object.
Consider also:
var obj = {
key1: "it",
key2: function(){return this.key1 + " works!"}
};
alert(obj.key2()); // alerts "it works!"
var func = obj.key2;
alert(func())​; // alerts "undefined works!"
I'm setting up func to reference the same function as obj.key2, but calling it as func() does not set this to obj.
For more information have a look at what MDN has to say about this.
I don't think there are any implications off the top of my head. Just make sure you don't accidentally do this at any point:
var obj = {
key1: "it",
key2: key1 + " works!"
}
As that will cause an error. Other than that, you should be good to go!

object in prototype is inherited as reference

I want to inherit new object instance using prototype.
Test case:
var MyObj = function() {}
MyObj.prototype.objName = {}
// I want this to be a different object for each instance of MyObj
var o1 = new MyObj (),
o2 = new MyObj ();
o1.objName['a'] = 1;
o2.objName['a'] = 2;
alert(o1.objName['a']) // 2
alert(o1.objName === o2.objName) // true
This means that objects in prototype are not inherited as its copies but instead as its reference.
I know that normally you can do it like this.
var MyObj = function() {
this.objName = {}
}
var o1 = new MyObj(),
o2 = new MyObj();
alert(o1.objName === o2.objName) // false
This works fine, but in my case this is not an option. I really need to define objName outside the MyObj function.
I managed to "solve" the problem with this
MyObj.prototype.objName = function() {
if ( this._objName === undefined ) {
this._objName = {};
}
return this._objName;
}
var o1 = new MyObj(),
o2 = new MyObj();
o1.objName()['a'] = 1;
o2.objName()['a'] = 2;
alert(o1.objName()['a']) // 1
But this is not very pretty and the performance of this code is much worse.
Is there any way to solve this more elegantly ?
This means that objects in prototype are not inherited as its copies but instead as its reference.
Nothing on the prototype is copied - the whole concept of prototypical inheritance is that properties reference the shared properties of the prototype object. So if you want a property to be individual for each instance, you have to explicitly assign it to the object and shadow the prototype property; just as you're doing it with the _objName property in your code.
But this is not very pretty and the performance of this code is much worse.
If you want it pretty, move it to the constructor (or make the constructor look for something like an init method to call if exists, then you can create that init method on the prototype.
To make performance a little better, you can change the getter function to
MyObj.prototype.getObj = function() {
var obj = {};
this.getObj = function(){ return obj; }; // overwrite itself
return obj;
};
though it still has the function call overhead. For even more elegance, you can use a getter property (not supported in old browsers) that removes itself on the first access:
Object.defineProperty(MyObj.prototype, "objName", {
get: function() {
var obj = {};
Object.defineProperty(this, "objName", {
value: obj,
writable: true //?
});
return obj;
},
enumerable: true,
configurable: true
});
Now you can omit the function call parenthesis.
This means that objects in prototype are not inherited as its copies but instead as its reference.
Just to be clear. First of all in JavaScript all objects are passed by reference, not by value. Only primitives are passed by value.
Second, you're not actually "copying" or "passing" anything. When you set a prototype, you're creating a prototype's chain. It means that in your case:
var MyObj = function() {};
MyObj.prototype.objName = {} ;
var o1 = new MyObj ();
var o2 = new MyObj ();
Both o1 and o2 doesn't have any property called objName, and you can simply test it with:
console.log(Object.keys(o1)); // []
When JS see a code like o1.objName, as first thing checks if the object has this property, and if it has, use it. If not, start to looking in the prototype's chain, starting by the prototype of o1, that is MyObj.prototype: it found the properties objName, and returns it. If it didn't find it, then JS will continue to check the prototype of MyObj.prototype, and so on. So, here the point: MyObj.prototype it's an object: and you shared that object between o1 and o2. That's why the instance of objName is the same. It's exactly the same logic of having:
function objName(obj) {
return "objName" in obj ? obj.objName : O.objName;
}
var O = { objName: [] };
var foo = {};
var bar = {};
objName(foo).push(0);
objName(bar).push(1);
So, you can't put in prototype any object that is not meant to be shared across the objects creates using that prototype. I would say that shared states like that is also a bad practice that should be avoided, that's why in general prototype shouldn't have such property.
It's still not clear to me why you can't modify the constructor, but the point is: when you create the instance of your object, you have to "setup" it. Usually, calling the constructor, but any function is fine. This is made also when you want to support inheritance, and calling the "super" constructor to initialize your object.

Whats the difference declaring variables with and without prototypes

Whats the difference between (via prototypes)
var Todo = {};
Todo.prototype.name = "...";
Todo.prototype.hello = function() { ... }
Vs (variables & functions "outside" object)
var Todo = {}
Todo.name = "..."
Todo.hello = function() { ... }
Or even the below : variables & functions in object
var Todo = {
name: "...",
hello = function() { ... }
}
Think it like
A property or a function declared with prototype is an instance member of Todo.
A property or a function declared without prototype is a static member of Todo.
The first one doesn't make sense as you are dealing with an object instance (({}) instanceof Object === true), it won't have a prototype property (Object does).
You may be inquiring about the difference between these two patterns...
var ObjA = function() {
this.method = function() {};
};
var ObjB = function() {};
ObjB.prototype.method = function() {};
jsFiddle.
The former will use more memory when instantiated - each object has their own method. The latter won't each have their own method, the method lives on the prototype object, which is the next in command on the prototype chain when its attempted to be accessed on the parent.
Todo.prototype is also an object, so the difference is if you declare property with prototype, then every object who created from this prototype will have the property, otherwise, the property is only for Todo the object self.
A significant difference between method #1 and #2 (which is almost identical to example #3) is on new keyword that you need to use if you extend your function via prototype, e.g.
var Todo1 = function() {};
Todo1.prototype.name = "Foobar";
var Todo2 = {name: "Foobar" }
var a = Todo1;
console.log(a.name); // no property retrieved
var b = Todo2;
console.log(b.name); // Foobar
var c = new Todo1;
console.log(c.name); // Foobar

Categories

Resources