This a sample of function:
function User (name) {
this.options.name = name;
};
User.prototype.options = {
name: 'Default'
};
var foo = new User('foo');
var bar = new User('bar');
console.log( foo.options.name ); // 'bar'
console.log( bar.options.name ); // 'bar'
The question is, how to get 'foo' and 'bar'?
Thanks
When you add anything in a constructor's prototype, that will be shared by all the instances. It is the best thing for the functions, but may not be for the data. So, you want to construct the options objects in the constructor itself, like this
function User(name) {
this.options = {
name: name || "Default"
};
}
When you do like this, whenever an object of User is created, each object will get its own options object. So, changing the options with one object will not affect any other object's options.
Related
I have an object that sits on the prototype, if it has a simple property like bar:3, then any instance can change bar without affecting other instances. However, if it has an object property instead (baz), any instance changing bar.x will be reflected on all other instances, I presume because objects are by reference.
Example object that sits on a prototype:
var foo = {
bar: 3, // single prop - this is okay
baz:{x: 4,y:5} // object prop - an instance can't change baz.x
};
My question - in the code below, how do I make porsche show "object prop: V12"?
jsfiddle
var vehicle = {
colour: 'blue',
info: function() {
console.log('wheels:' + this.wheels + ' colour:' + this.colour);
}
};
var engine = {
size: 'V12', // single prop
type:{size: 'V12',fuel:'petrol'}, // object prop
showSize: function() {
console.log('single prop: ' + this.engine.size );
console.log('object prop: ' + this.engine.type.size);
}
};
var car = Object.assign(Object.create(vehicle), {
wheels: 4,
drift: function() { console.log('drifting'); }
});
var ferrari = Object.assign(Object.create(car), {
colour:'red',
engine: Object.create(engine)
});
var porsche = Object.assign(Object.create(car), {
colour:'silver',
engine: Object.create(engine)
});
// ferrari owner changes his engine
ferrari.engine.size = '100cc';
ferrari.engine.type.size = '100cc';
console.log('ferrari:');
ferrari.engine.showSize.call(ferrari);
console.log('\nporsche:');
porsche.engine.showSize.call(porsche);
/*
OUTPUT
ferrari:
single prop: 100cc
object prop: 100cc
porsche:
single prop: V12
object prop: 100cc <------ WRONG, should be V12
*/
EDIT : for anyone that stumbles across this, I'm going to use this pattern; it's more intuitive for me to create constructors and use call(this). The irony is that it's very close to Amit's answer, but I feel that function constructors aren't in the true spirit of prototypal inheritance/delegation.
Having to do this in each 'class' seems clunky:
car.prototype = Object.create(vehicle.prototype ); // <- new way
car.prototype = new vehicle(); // <- old way
car.prototype.constructor = car;
Instead, I know exactly what's going on using this pattern:
var car = Object.create(vehicle, {
constructor : { value: function (colour, wheels) {
vehicle.constructor.call(this, colour, wheels);
return this;
}}
});
It's six of one, half a dozen of the other ;)
You will have to give each instance its own object, there's no way around this.
Your code actually shows that you're already familar with the concept: you've given each car its own engine. We can employ the same pattern to give every engine we create its own type:
var engine = {
size: 'V12',
showSize: function() {
console.log('single prop: ' + this.engine.size );
console.log('object prop: ' + this.engine.type.size);
}
};
var enginetype = {
size: 'V12',
fuel: 'petrol'
};
…
var ferrari = Object.assign(Object.create(car), {
colour:'red',
engine: Object.assign(Object.create(engine), {
type: Object.create(enginetype);
})
});
var porsche = Object.assign(Object.create(car), {
colour:'silver',
engine: Object.assign(Object.create(engine), {
type: Object.create(enginetype);
})
});
(but I'm not saying that duplicating the .size on .type.size is a good design, I'll assume it's only an example)
First let's untangle what you've done and why you get the result you do...
Object.create(proto) creates an empty object ({}) and sets it prototype to proto. In your case, this method is used to create an empty object for car, with a vehicle prototype, and the 2 Italian speedsters with prototype car. It is also used to create 2 "engines".
You're also wrapping each of the 3 "car" create calls with a call to Object.assign(target, source) that appends new properties to your target object (the newly created object) as instance properties.
So, what happens is that whenever you access (read or write) a property of an object, if that property belongs to the instance, you'll be reading or writing the value of that specific instance. If however the instance doesn't have that property defined, the prototype chain is traversed till the property is found, and then it is used in the context of the relevant prototype. In your case, what this means is that since the car's engines are empty objects with a shared prototype (the engine object you initialized at the top), accessing properties of the engine really goes to that specific, single instance. If you modify it, you modify it for all objects.
Having said all that, you could be doing things a little different... I prefer using proper constructor functions and create objects with the new keyword.
Here's your code refactored:
function vehicle(colour) {
this.colour = colour || 'blue'; // blue default if nothing else provided
};
vehicle.prototype.info = function() {
console.log('wheels:' + this.wheels + ' colour:' + this.colour);
};
function engine(size, fuel) {
this.size = size || 'V12'; // V12 default if nothing else provided
this.fuel = fuel || 'petrol'; // petroc default if nothing else provided
};
engine.prototype.showSize = function() {
console.log('size: ' + this.size );
};
function car(colour) {
vehicle.call(this, colour);
this.wheels = 4;
this.engine = new engine();
};
car.prototype = new vehicle();
car.prototype.constructor = car; // <-- otherwise, (new car()).constructor != car
car.prototype.drift = function() { console.log('drifting'); };
var ferrari = new car('red');
var porsche = new car('silver');
// ferrari owner changes his engine
ferrari.engine.size = '100cc';
console.log('ferrari:');
ferrari.engine.showSize();
ferrari.info();
console.log('\nporsche:');
porsche.engine.showSize();
porsche.info();
Do you know how to create a name for a constructor object in javascript? I have a fiddle please look at this. http://jsfiddle.net/m8jLoon9/2/
ex.
// you can name the object by using this
function MyConstructorName() {}
// with this one, the name of the objct is the variable
var varConstructorName = function() {};
// MyConstructorName{}
console.log( new MyConstructorName() );
// varConstructorName{}
console.log( new varConstructorName() );
// I have a function that creates an object
// with the name arguments provided
function createANameSpace(nameProvided) {
// how to create a constructor with the specified name?
// I want to return an object
// EDITED, this is wrong, I just want to show what I want on this function
var TheName = function nameProvided() {};
// returns an new object, consoling this new object should print out in the console
// the argument provided
return new TheName();
}
// create an aobject with the name provided
var ActorObject = createANameSpace('Actor');
// I want the console to print out
// Actor{}
console.log( ActorObject );
Its actually achieved quite simply as follows
Create it by:
var my_name_space = { first: function(){ alert("im first"); }, second: function(){ alert("im second"); } };
Access it by:
my_name_space.first();
or
my_name_space.second();
It is very similar to storing variables in an object:
var car = {type:"Fiat", model:500, color:"white"};
Except the "Fiat" is another function itself. You could consider the namespace being and object with functions.
This seems like an abuse of the language, but you can return arbitrarily named objects by doing something like this:
function createANamespace(nameProvided) {
return {
constructor: {name: nameProvided}
};
}
I only tried this on chrome, so ymmv.
Edit: Or, if you really want to abuse the language:
function createANamespace(name) {
return new Function('return new (function '+ name + '(){} )')
}
What is the best design pattern for achieving the following (which doesn't work)?
var obj = (function() {
// code defining private variables and methods
var _obj = {
property: value,
method1: function() {
// do stuff
},
method2: function() {
// use property
var prop = _obj.property; // obviously doesn't work
// call method1
obj.method1(); // "obj" not finished being defined yet!
}
};
// obviously now I could do...
var prop = _obj.property;
return _obj;
})();
// and I could now do...
obj.method1();
A variation which I think should work is
var obj = (function() {
var property = value,
method1 = function() {
// do stuff
},
method2 = function() {
// use property
var prop = property;
// call method1
method1();
},
_obj = {
property: property,
method1: method1,
method2: method2
};
return _obj;
})();
Similarly, how does it work for objects meant to be created with the new operator? Within the constructor function itself you can write this.method(). But what if you want to keep the constructor small, only defining those things which will likely be customized upon creation, and then defining the rest in the prototype? (This seems to be the common pattern.) Can the properties / methods within the prototype interact in any way?
var MyObj = function(name) {
this.name = name;
};
var obj = new MyObj('Bob');
MyObj.prototype = {
called_often: function() {
// lots more code than just the following
return document.getElementById('someID').value;
},
global_default: 'value', // can be changed, so need to pull value when run
does_stuff: function(value) {
var str = global_default + value, // can't access global_default on its own
input = MyObj.called_often(), // doesn't work; MyObj.prototype.called_often() DOES
name = this.name; // 'this' used in the prototype doesn't work
// even within a created object
return name + input + str;
}
};
I'm sure there's better ways to achieve my result whenever I run into this problem. This code isn't situation specific and just illustrates the general problem. So you won't be able to give me an alternative for those specific situations I run into. But maybe you can help my overall thinking.
Well, from your first example:
var _obj = {
property: value,
method1: function() {
// do stuff
},
method2: function() {
// use property
var prop = this.property;
// call method1
this.method1();
}
};
That's what the this value is for.
Now, what you cannot do is refer to a property of an "under construction" object from elsewhere in the object literal syntax. (It's hard to give an example because it's just not syntactically possible.) In cases where you want to do that, you do need one or more separate assignment statements.
Guess what? You are making simple stuff complex. Pointy's answer is good, but the prototype way is better for several reasons. That's why I am describing (rather, making corrections in) the last method. Check this fiddle.
var MyObj = function(name) {
this.name = name;
};
MyObj.prototype = {
called_often: function() {
// lots more code than just the following
return 'VALUE'; //document.getElementById('someID').value;
},
global_default: 'value', // can be changed, so need to pull value when run
does_stuff: function(value) {
var str = this.global_default + value, // can't access global_default on its own
input = this.called_often(), // doesn't work; MyObj.prototype.called_often() DOES
name = this.name; // 'this' used in the prototype doesn't work
// even within a created object
return name + input + str;
}
};
var obj = new MyObj('Bob');
Has anyone noticed this behavior before? This really threw me off... I would have expected prototyped arrays to be private to each class instance rather than shared between all class instances.
Can someone verify that this is the correct behavior and perhaps explain this behavior in more detail?
Notice the commented code and how it affects the behavior of the script.
<html>
<head>
<script type="text/javascript">
function print_r( title, object ) {
var output = '';
for( var key in object ) {
output += key + ": " + object[ key ] + "\n";
}
output = title + "\n\n" + output;
alert( output );
}
function Sandwich() {
// Uncomment this to fix the problem
//this.ingredients = [];
}
Sandwich.prototype = {
"ingredients" : [],
"addIngredients" : function( ingArray ) {
for( var key in ingArray ) {
this.addIngredient( ingArray[ key ] );
}
},
"addIngredient" : function( thing ) {
this.ingredients.push( thing );
}
}
var cheeseburger = new Sandwich();
cheeseburger.addIngredients( [ "burger", "cheese" ] );
var blt = new Sandwich();
blt.addIngredients( [ "bacon", "lettuce", "tomato" ] );
var spicy_chicken_sandwich = new Sandwich();
spicy_chicken_sandwich.addIngredients( [ "spicy chicken pattie", "lettuce", "tomato", "honey dijon mayo", "love" ] );
var onLoad = function() {
print_r( "Cheeseburger contains:", cheeseburger.ingredients );
};
</script>
</head>
<body onload="onLoad();">
</body>
</html>
Many thanks.
The prototype of an object is just an object. The prototype properties are shared between all objects that inherit from that object. No copies of the properties are made if you create a new instance of a "class" (classes don't exist anyway in JS), i.e. an object which inherits from the prototype.
It only makes a difference on how you use the these inherited properties:
function Foo() {}
Foo.prototype = {
array: [],
func: function() {}
}
a = new Foo();
b = new Foo();
a.array.push('bar');
console.log(b.array); // prints ["bar"]
b.func.bar = 'baz';
console.log(a.func.bar); // prints baz
In all these cases you are always working with the same object.
But if you assign a value to a property of the object, the property will be set/created on the object itself, not its prototype, and hence is not shared:
console.log(a.hasOwnProperty('array')); // prints false
console.log(a.array); // prints ["bar"]
a.array = ['foo'];
console.log(a.hasOwnProperty('array')); // prints true
console.log(a.array); // prints ["foo"]
console.log(b.array); // prints ["bar"]
If you want to create own arrays for each instance, you have to define it in the constructor:
function Foo() {
this.array = [];
}
because here, this refers to the new object that is generated when you call new Foo().
The rule of thumb is: Instance-specific data should be assigned to the instance inside the constructor, shared data (like methods) should be assigned to the prototype.
You might want to read Details of the object model which describes differences between class-based vs. prototype-based languages and how objects actually work.
Update:
You can access the prototype of an object via Object.getPrototypeOf(obj) (might not work in very old browsers), and Object.getPrototypeOf(a) === Object.getPrototypeOf(b) gives you true. It is the same object, also known as Foo.prototype.
The behaviour is correct.
[] is tranlated to new Array() in runtime, but only one such array is ever created.
In other words, Obj.prototype = {...} is executed just like any other assigment.
When you do var exp1 = new C(), JavaScript sets exp1.[[Prototype]] = C.prototype. When you then access properties of the instance, JavaScript first checks whether they exist on that object directly, and if not, it looks in [[Prototype]]. This means that all the stuff you define in prototype is effectively shared by all instances, and you can even later change parts of prototype and have the changes appear in all existing instances.
I know how to use JSON to create objects, but there doesn't seem to be away to use JSON to create an object that is of a specific object type.
Here's an example of an Object and creating an instance of it:
Person = function() { };
Person.prototype = {
FirstName: null,
GetFirstName: function() {
return this.FirstName;
}
};
//Create an instance of the Person Object
var me = new Person();
me.FirstName = "Chris";
alert(me.GetFirstName()); //alert the FirstName property
Now, I would like to use JSON to create a new Person object so that the GetFirstName function works on it.
Here's something like that I'm looking to do (but this code doesn't work):
var you = new Person() { FirstName: "Mike" };
// OR
var you = new Person{ FirstName: "Mike" };
Is there anyway to use JSON to create an object that is of a specific type?
UPDATE: My sample with the Person object is just to simplify the question. In fact, I am unable to modify the constructors of the actual objects that I need to create instances of. The objects are part of a third-party library.
UPDATE: Using some of the suggestions below, I was able to figure out a way to create an object that inherits from the original, and accept JSON in it's constructor. This is neat!
personWrapper = function(obj){
for(var o in obj){
this[o] = obj[o];
}
};
personWrapper.prototype = new Person();
var you = new personWrapper({FirstName: "Chris"});
alert(you.GetFirstName());
alert(you instanceof Person); // returns True - we are successfully inheriting from Person!
I don't imagine so. I'd create a function on the Person class to initialise from a JSON object if I were you.
function Person() {
this.loadFromJSON = function(json) {
this.FirstName = json.FirstName;
};
}
If you didn't know what class the JSON object was representing beforehand, perhaps add an extra variable into your JSON.
{ _className : "Person", FirstName : "Mike" }
And then have a 'builder' function which interprets it.
function buildFromJSON(json) {
var myObj = new json["_className"]();
myObj.loadFromJSON(json);
return myObj;
}
Update: since you say the class is part of a third-party library which you can't change, you could either extend the class with prototyping, or write a function which just populates the class externally.
eg:
Person.prototype.loadFromJSON = function(json) {
// as above...
};
or
function populateObject(obj, json) {
for (var i in json) {
// you might want to put in a check here to test
// that obj actually has an attribute named i
obj[i] = json[i];
}
}
You could allow new Person() to accept an object to populate attributes with as a parameter.
var you = new Person({ firstName: 'Mike' });
You can derive an object from theirs. Your constructor can accept the object you want, but call their constructor in an unaffected fashion:
function yourWrapper(obj) {
theirObject.call(this);
for (var s in obj) {
this[s] = obj[s];
}
}
yourWrapper.prototype = new theirObject();
Or something like that :)