How come newperson4 is created, and not errored? code below -
function person() {
}
var p = new person();
var q = null;
var r = "some string";
var newperson1 = Object.create(p); //Runs fine.
var newperson2 = Object.create(q); //Runs fine.
var newperson3 = Object.create(r); //Errors - Object.prototype requires to be an Object or Null only. Fine!
var newperson4 = Object.create(person); //Based on above error, person is a function, not an object. How is it working?
Every function is an object in javascript (like arrays are objects), not a primitive value. It can have properties, and you can inherit from it.
Related
I just cleared my mind on JavaScript objects and my question is really simple for most of the people here. I feel comfortable with the JavaScript object literal notation like:
var Obj = {
/**
* #type {string}
*/
name: '',
/**
* setName
* Set the `name` property.
*
* #param param
*/
set setName (param) {
this.name = param;
}
};
The only limit I found is that if I want to create two completely separate objects in the same page, with this notation I can't.
var a = Obj;
a.setName = "John";
var b = Obj;
b.setName = "Peter";
// OUTPUT
// a.name -> Peter
// b.name -> Peter
Set var whatever = Obj is just useless, 'cause it doesn't instantiate n separate objects and it just overwrites the code above. Using new keyword such as var a = new Obj doesn't work neither (probably I'm using it in the wrong way?). The solution I came up with is returning the object inside a function, like:
function Obj() {
return {
/**
* #type {string}
*/
name: '',
/**
* setName
* Set the `name` property.
*
* #param param
*/
set setName (param) {
this.name = param;
}
}
}
This way I can create two different objects and correctly access to their properties and methods:
var a = Obj();
a.setName = "John";
var b = Obj();
b.setName = "Peter";
// OUTPUT
// a.name -> John
// b.name -> Peter
So, my question are:
Is what I've done conceptually right?
Is there a more correct/efficient way to achieve it?
Your concept of a function that returns an Object instance is valid, but your implementation is very brittle because it is only set up to work with specific properties. Read on for more details on various ways to create instances and a more flexible way to return objects...
var a = Obj; doesn't create a new object. It just assigns a the memory address of the existing object Obj.
var myObj = {}; // Object instance is created and memory location is stored in myObj
var a = myObj; // No new object is created. a and myObj point to the same object
console.log("Are 'a' and 'myObj' both pointing to the same object?", a === myObj); // true
If you want to design a single object and then make more of that object, you need to be able to create "instances" of an object. You can't do that directly with an object literal:
var myObj = {
someProp:10
};
var myNewObj = new myObj(); // Fails because an object literal can't be instantiated
But, you can do it with the Object.create() method, which takes your Obj concept to fruition:
// Object instance is created and memory location is stored in myObj
var myObj = {
someProp: "default",
// "Methods" are just properties with functions as their value
someMethod: function(input){
// The || syntax that follows allows for a default value for the method
// if no argument is passed to the method.
this.name = input || "default";
}
};
// Create a new Object instance and set myObj as the prototype of the instance.
// This means that the new instance will inherit from that prototype:
var a = Object.create(myObj);
console.log(a.someProp); // "default";
a.someProp = "something specific";
a.someMethod("Test");
myObj.someMethod();
console.log(a.name, myObj.name); // "Test" "default"
console.log(a.someProp, myObj.someProp); // "something specific", "default"
Instances can be explicitly made with the new operator and a constructor function:
function foo(){
this.someProp = "something";
}
var a = new foo(); // Unique instance of foo
var b = new foo(); // Separate and distinct instance of foo
a.someProp = 10;
b.someProp = 20;
console.log(a.someProp, b.someProp); // 10 20
Or, the new operator and a class:
class foo{
constructor(val) {
this.someProp = val;
}
}
var a = new foo(10); // Unique instance of foo
var b = new foo(20); // Separate and distinct instance of foo
console.log(a.someProp, b.someProp); // 10 20
Have you tried with Object.create() ?
var b = { setName : "Mongo" };
a = Object.create(b);
a.setName = "John";
b.setName = "Peter";
console.log(a.setName);
console.log(b.setName);
I'm using below code.
var emp = function employee(name, sal) {
this.empname = name;
this.sal = sal;
}
emp.prototype.getName = function() {
return this.empname
};
var man = new emp("manish", 100);
console.log(man.getName()); //prints manish
var man1 = Object.create(emp);
man1.empname = "manish1";
console.log(man1.prototype.getName()); //prints undefined.
can some help me to understand why object create is printing undefined instead manish1.
new X() creates a new object with constructor X and prototype X.prototype. Object.create(X) creates a new object with prototype X (and therefore constructor X.constructor).
So you need to call it with the prototype you want:
var man2 = Object.create(emp.prototype);
man2.empname = "manish2";
console.log (man2.getName()); // prints manish2
Can I define an object as a prototype member? If yes then how can I stop the call by reference of an instantiated object?
function MediaUser (){
}
MediaUser.prototype.oThumb = {sUrl: 'noImage.png'};
var oMediaUser = new MediaUser();
var oMediaUser2 = new MediaUser();
oMediaUser.oThumb.sUrl = "a.png";
console.log(oMediaUser2.oThumb.sUrl); // prints a.png
Everything you define in prototype is shared by all objects. You have to put that inside the constructor if you want it to be different for all instances:
function MediaUser (){
this.oThumb = {sUrl: 'noImage.png'}
}
var oMediaUser = new MediaUser();
var oMediaUser2 = new MediaUser();
oMediaUser.oThumb.sUrl = "a.png";
console.log(oMediaUser2.oThumb.sUrl); // prints noImage.png
In fact when you are looking for separate value for each object , get rid of thet prototype property altogether
function MediaUser (url){
this.sUrl = url || 'noImage.png';
}
var oMediaUser = new MediaUser("a.png");
var oMediaUser2 = new MediaUser();
console.log(oMediaUser.sUrl); // prints a.png
console.log(oMediaUser2.sUrl); // prints noImage.png
This code work :
function class1(){
this.x5 = 5;
this.x6 = 6;
this.prototype = 5;
}
function class2(){
this.x3 = 3;
this.x4 = 4;
}
class2.prototype = new class1();
var obj1 = new class2();
alert(obj1.x5 ); // alert me 5
But why this not working :
function class1(){
this.x5 = 5;
this.x6 = 6;
this.prototype = 5;
}
function class2(){
this.x3 = 3;
this.x4 = 4;
this.prototype = new class1(); // or class2.prototype = new class1();
}
var obj1 = new class2();
alert(obj1.x5); // alert me "undefinded"
You can't set the prototype inside of the function like that. When invoking a function with the new operator, then a new object is created, and this gets set to that object. Objects don't have a publicly accessible prototype property that you can set. Their prototype property is actually __proto__, which is not accessible (although some browsers do let you get at it).
In your second example, you're just setting a plain ol' vanilla property named "prototype" with a value.
the prototype property on a function is also not the prototype! Confusing, eh? What it really is is something of a "prototype template". It basically means "when you create an object using this function as the constructor, set their prototype to whatever I have set in the prototype property." This can be very confusing until you grok it.
Also Note
Your first example doesn't work either (try it here), you are setting the prototype property of the function after the instance was already created. So that instance was already given a different prototype object. If you created a second instance of class2, it would alert the property correctly.
Here is an example:
var Box = function() {
this.id = generateUniqueId();
};
Box.prototype = {
add:function(parent) {
parent.appendChild(this.elm);
}
};
var NewBox = function() {
this.newThing = true;
};
NewBox.prototype = new Box();
NewBox.prototype.remove = function() {
this.elm.parentNode.removeChild(this.elm);
};
var a = new NewBox();
var b = new NewBox();
alert(a.id); // alerts 0
alert(b.id); // also alerts 0! :#
I would like to have a and b (basically each time I create a NewBox) have their own id. I understand that NewBox is using prototypal inheritance and is thus inheriting from a single instance of the generated id that it gets from Box, but I would like it so that each NewBox gets its own id without having to explicitly say that inside the NewBox constructor.
Maybe it's impossible or I'm doing something really wrong, so please help!
Thanks a lot!
In your example, the Box constructor gets executed only when you set the NewBox.prototype object.
You could workaround this by calling the Box constructor function inside NewBox with the Function.prototype.apply method, to set the this value and forward all the argument values, for example:
//..
var NewBox = function() {
Box.apply(this, arguments);
this.newThing = true;
};
//..
var a = new NewBox();
var b = new NewBox();
// assuming your `generateUniqueId` function
// increments a number
alert(a.id); // will alert 1
alert(b.id); // will alert 2
Now, each time the NewBox constructor is called to create a new object instance (new NewBox();), it will call the Box function to apply all its logic on it. This will help you to avoid repeating the logic of the parent constructor over and over.
The apply, is used call the "parent" constructor function setting the this value to the object instance that is being created by the NewBox constructor and we pass all arguments provided to this function.
Your example also shows a common problem, when you express inheritance relationship through NewBox.prototype = new Box(); the Box constructor gets called and it has side effects, this new object will be initialized and your generateUniqueId function will be executed for the first time, if you want to avoid that, you need to either use a temp constructor, just to make a new object that inherits from Box.prototype, or use the new ECMAScript 5 Object.create method for the same purpose, for example:
function inherit(o) {
function Tmp() {}
Tmp.prototype = o;
return new Tmp();
}
//.....
NewBox.prototype = inherit(Box.prototype);
Or:
NewBox.prototype = Object.create(Box.prototype);
In that way, you express the same inheritance hierarchy without running your Box constructor that first time, avoiding any side effect that it might cause.
At last but not least, whenever you replace a function's prototype property is always recommended to restore the constructor property of this new prototype object, otherwise it will point to the wrong function.
In your example, since you replace the NewBox.prototype with a new instance of Box, the NewBox.prototype.constructor property will point to Box, (your instances are affected, e.g. a.constructor === Box; // true) instead of to NewBox as you would expect, we need to set it back, e.g.:
NewBox.prototype = someObject; // as any of the examples above
NewBox.prototype.constructor = NewBox;
You could abstract those details into a function, as I did in the inherit function above:
function inherits(child, parent) {
var obj, Tmp = function () {};
Tmp.prototype = parent.prototype;
obj = new Tmp();
child.prototype = obj;
child.prototype.constructor = child;
}
//...
// instead of setting the `NewBox.prototype` manually
inherits(NewBox, Box); // "NewBox inherits from Box"
//...
Maybe it's impossible or I'm doing something really wrong, so please help!
You're doing something wrong.
If you want instance-specific values, initialize them in the constructor, not the prototype.
Matt Ball has the right idea. Instead try:
var Box = (function(){
var numberOfBoxes = 0;
function() {
this.id = numberOfBoxes++;
}
})();
Or in the case you want all your (different) classes to have unique ids:
var generateUniqueID = (function(){
var runningCount = 0;
return function (){
return runningCount++;
}
})();
var Box = function() {
this.id = generateUniqueId();
};
var NewBox = function() {
this.id = generateUniqueId();
this.newThing = true;
};