Fairly new to using a prototype design pattern so I had a quick question about the most efficient way of creating a setter/getter for multiple constructor variables.
Say I had a "trip" object :
function trip(){
this.numberOfDays = 0
this.price = 0
this.activites = []
this.bPacks = []
this.pPacks = []
}
Now the setters for the activities, bPacks, and pPacks all do primarily the same thing. They will add or remove to the array that they are associated with.
so instead of writing 6 separate setters for these properties such as :
trip.prototype.addActivity = function(itemToAdd){
var index = this.activites.push(itemToAdd)
}
trip.prototype.addBPack = function(itemToAdd){
var index = this.bPack.push(itemToAdd)
}
etc...
Is it possible to do something like this :
trip.prototype.addPacksOrActivity = function(item,typeOfItem){
this.typeOfItem.push(item);
}
where we target the specific property of the trip object we want to push to?
Any suggestions for more efficient ways of building setters/getters are welcome but I am trying to avoid transpiling from ES6 so ES5 type answers are preffered.
Yes, you can use a simple loop to do that:
["activities", "bPacks", "pPacks"].forEach(function(typeOfItem) {
var methodName = "add"+typeOfItem[0].toUpperCase()+typeOfItem.slice(1);
Trip.prototype[methodName] = function(item) {
this[typeOfItem].push(item);
};
});
Notice the use of bracket notation and the closure over typeOfItem.
Related
Kind of strange thing is happening with my code. What I observe is something like this:
var prototype = {
property : "",
array : []
}
var objectArray = [];
function myFunction {
objectArray[0] = Object.create(prototype);
objectArray[1] = Object.create(prototype);
objectArray[0].property = "Hello 1";
objectArray[0].array.push("Hello 1");
objectArray[1].property = "Hello 2";
objectArray[1].array.push("Hello 2");
// At this point when running the program I get:
// objectArray[0].property == "Hello 1"
// objectArray[1].property == "Hello 2";
// objectArray[1].array == objectArray[1].array == prototype.array
// == ["Hello 1", "Hello 2"]
}
What I want, and expected, was two separate arrays for the two objects. What am I missing here?
In JavaScript, objects are copied by reference, so both objectArray objects are simply references to the same object ( prototype ). You need to clone the object or create instances using the new keyword and a constructor to create separate objects.
Example on how to do it using the new keyword:
var prototype = function() {
this.property = "";
this.array = [];
};
objectArray[0] = new prototype();
objectArray[1] = new prototype();
You can also do:
var prototypeFactory = function() {
return {
property: "",
array: []
};
};
objectArray[0] = prototypeFactory();
objectArray[1] = prototypeFactory();
The prototype object exists the same as the [[Prototype]] for each object. They don't get a fresh copy when you use Object.create().
You'd need to use assignment, which never walks the prototype chain.
I wonder if you aren't asking yourself "why does it work for property, but not for array?". The fact is, it doesn't work for property either, JavaScript is fooling you.
When objects share the same prototype, one must consider that:
All properties of the prototype are shared by the objects that inherit from that prototype.
You cannot assign to properties of the prototype, only read them (unless it's an accessor property, but let's keep that aside).
So what's actually happening here:
objectArray[0].property = "Hello 1";
objectArray[1].property = "Hello 2";
is that a new own property called "property" is being created on each object, while prototype.property remains untouched. The array property is behaving differently because you're not assigning to it, you're accessing prototype.array and calling the push method on it.
Don't forget that Object.create() isn't yet standardized, and won't work in IE8 or FF3.6. A simple work-around to clone an object is to use the JSON object:
function clone(obj) {
return JSON.parse(JSON.stringify(obj));
}
When using AJAX, I tend to pass objects from my server to Javascript in the form of JSON objects (aka Javascript). Certain functions within my Javascript rely on the specific type of object I am using. For instance, lets use a phone number for example. I have a constructor:
function PhoneNumber(number, type, isPrimary, contactId, id, className) {
this.number = number;
this.type = type;
this.isPrimary = isPrimary;
this.contactId = contactId;
this.id = id;
this.className = className;
}
Which I use when creating a phone number object in my Javascript. In some situations I don't create the object in JS, I get the object from the server so it comes in the form of a generic object with the exact same fields. So when my code relies on the specific type by using something such as this:
var objType = objArray[i].constructor.name;
var mappedObj;
switch(objType) {
case 'PhoneNumber':
currentArray = currentArray.phone;
//Convert response to javascript object.
mappedObj = mapPhone(jsonResponse[i]);
break;
case 'Email':
currentArray = currentArray.email;
mappedObj = mapEmail(jsonResponse[i]);
break;
case 'Address':
currentArray = currentArray.address;
mappedObj = mapAddress(jsonResponse[i]);
break;
case 'Website':
currentArray = currentArray.website;
mappedObj = mapWebsite(jsonResponse[i]);
}
In this situation, I check the name of the objects constructor and set certain variables based on that name. If the object I check the name on is a JSON from the server, it simply gives me a generic "Object" response and thus the code does not work. I get around this by using a mapping function for each object such as:
function mapPhone(phoneObj) {
var id = phoneObj.id;
var contactId = phoneObj.contactId;
var number = phoneObj.number;
var type = phoneObj.type;
var primary = phoneObj.isPrimary;
var className = phoneObj.className;
var phoneNumber = new PhoneNumber(number, type, primary, contactId, id, className);
return phoneNumber;
}
This works just fine, but to me seems a little redundant. Is this the best way to solve the JSON Object problem, or is there a better solution? I understand this is more of a "Am I doing this the best way possible" type of question, but I repeat this type of logic CONSTANTLY in my Javascript code and I figure I might as well get another opinion or two on whether or not its the proper way to do this before I have to spend hour upon hour fixing it in the future.
EDIT: I ended up accepting a jQuery solution because I happen to use jQuery in my project. There are however multiple solutions that also worked for me before I found the jQuery option. They just weren't quite as clean and efficient.
The following requires you to have the same properties in your object and your JSON object.
var phoneNumber = $.extend(new PhoneNumber(), yourJSONObject);
This basically creates a new PhoneNumber object and then copies all properties from your JSON object onto it. The $.extend() method is from jQuery, but you could also use as similar method from e.g. Underscore.js or one of the other js libraries/frameworks.
This similar question has a lot of interesting answers:
Parse JSON String into a Particular Object Prototype in JavaScript
Based off the poster's own answer, I think this would be an effective solution for you:
function recastJSON(jsonObject) {
// return generic object if objectType is not specified
if (!jsonObject.objectType)
return jsonObject;
// otherwise create a new object of type specified
var obj = eval('new '+jsonObject.objectType+'()');
for(var i in jsonObject)
obj[i] = jsonObject[i];
return obj;
}
You will need to add objectType to the JSON objects you are receiving to define the javascript class you want to instantiate. Then when you call this function, it will cast the object to that type and copy the data over (including the variable 'objectType').
Using your phone number example, your code would look like this:
// generic object from JSON
var genericObject = {objectType:'PhoneNumber', number:'555-555-5555', type:'home', primary:true, contactId:123, id:1234, className:'stdPhone'};
var phoneObject = recastJSON(genericObject);
AFAIK, in everything that is not IE, you can do this:
// define a class
var Foo = function(name) {
this.name = name;
}
// make a method
Foo.prototype.shout = function() {
return "I am " + this.name;
}
// make a simple object from JSON:
var x = JSON.parse('{ "name": "Jason" }');
// force its class to be Foo
x.__proto__ = Foo.prototype;
// the method works
x.shout();
Unfortunately, IE does not support the __proto__ accessor, so what you would need to do is first create an empty instance of your object, then just copy everything over:
// make a simple object from JSON:
var x = JSON.parse('{ "name": "Jason" }');
// make an empty Foo
var y = Object.create(Foo.prototype);
// copy stuff over
for (a in x) {
y[a] = x[a];
}
y.shout();
Both of these approaches are quite a bit more generic than your mapWhatever functions, keeping it DRY.
If not supporting older browsers is ok, You can use Object.create to do the mapping for you. (dropping the shim—at least the shim at MDN—in will not fix older browsers, since that shim does not accept the second parameter.)
DEMO
function makeThisExtend(obj, CtorFunc) {
for (var k in obj)
if ({}.hasOwnProperty.call(obj, k))
obj[k] = { value: obj[k] };
return Object.create(CtorFunc.prototype, obj);
}
var objFromServer = { Number: "123", id: 5 };
objFromServer = makeThisExtend(objFromServer, PhoneNumber);
alert(objFromServer.Number + " " + objFromServer.id); //123 5
alert(objFromServer.constructor); //function PhoneNumber ...
I'm trying to write a wrapper on an array and I've come up with code below:
myList = function () { };
myList.prototype.innerArray = [];
myList.prototype.add = function(pt) { this.innerArray.push (pt); return this; };
For every object myList i create, I hope to get an empty attribute innerArray. But I'm afraid i haven't really understood the concept of prototypes yet, because:
m = new myList().add(4).add(5);
m.innerArray.length;
returns 2, so far so good, but now I do:
j = new myList();
j.innerArray.length;
which returns also 2, and I would have expected 0 (a fresh new innerArray); I'm afraid i'm missing something fundamental.
You don't want to use prototype with innerArray. In your constructor simply do this.innerArray = [] and now you have an instance variable. By using prototype you are creating a class attribute (e.g. shared amongst class instances), not an instance variable (e.g. unique to an instance of the class).
All instances of myList share the exact same prototype object. It is not copied or cloned to each instance. For functions this is fine because when you call foo.add() then this is the instance, allowing you to fetch per instance values. foo.add and bar.add are the same function object but this is different in each call. But with values directly on the prototype when you change them they change for all instances, since they all point to the same objects.
Instead you want to make a new array in the constructor:
myList = function () {
this.innerArray = [];
};
myList.prototype.add = function(pt) {
this.innerArray.push (pt);
return this;
};
You should do
myList = function () { this.innerArray = []; };
as this:
myList.prototype.innerArray = [];
creates innerArray variable common to all instances.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Use of 'prototype' vs. 'this' in Javascript?
I donot know why some developer use javascript prototype object exactly.
so, I made a sample code below.
one using prototype object, the other plain syntax.
what is difference btwn two of them?
Is there any benefit to use prototype syntax, the first case?
Car.prototype.engine_ = 'bmw';
Car.prototype.getEngine = function(){
return this.engine_;
}
Car.prototype.setEngine = function(engine){
this.engine_ = engine;
}
function Car(){
//....
}
var myCar = new Car();
myCar.getEngine();
myCar.setEngine('benz');
console.debug(myCar.getEngine());
vs
function Car(){
this.engine_ = 'bmw';
this.setEngine = function(engine){
engine_ = engine;
}
this.getEngine = function() {
return engine_;
}
//...
}
var myCar = new Car();
myCar.getEngine();
myCar.setEngine('benz');
console.debug(myCar.getEngine());
Yes, there is a difference. Using prototype, your property or method is declared once in the Object and the same for all instances. Using direct properties/methods, these properties and method are added to every instance of the Object. So here's your Car constructor:
function Car(){this.engine = 'no known engine'};
Car.prototype.setEngine = function(val){
this.engine = val;
}
Now the method setEngine is equal to all instances of your Car, but the engine property can vary per instance. The prototypal methods are defined once and looked up via the prototype chain (see also this). In this case setEngine sets the 'engine' property of the current instance of your Car. So you can do:
var bmw = new Car, merc = new Car;
bmw.setEngine('BMW');
merc.setEngine('Mercedes');
console.log(bmw.engine); //=> 'BMW'
console.log(merc.engine); //= 'Mercedes'
AFAIK there is no difference between this ones
This may not be possible (or might be dead easy! :) ) so here it is...
I want to be able to create objects of a type that is dependant on a variable set, without the need for a big switch statement.
I think it is possible in PHP to do something like...
$objectType = "myNewClass";
$newObject = new $objectType();
where the $newObject variable will hold an instance of the Class "myNewClass".
Is this (or any similar technique) possible with Javascript?
Thanks
Stuart
If your constructor functions are defined in the global scope, you can access it trough the bracket notation (window[fnName]):
function ObjectType1(){ // example constructor function
this.type = 1;
}
var objectType = 'ObjectType1'; // string containing the constructor function name
var obj = new window[objectType](); // creating a new instance using the string
// variable to call the constructor
See: Member Operators
CMS's answer is good, but in EXT you're probably dealing with namespaces.
I create an object map that holds any dynamic classes:
// within a namespace:
var ns = {
Thinger: function(){}
};
// globals:
var Zinger = function(){}
// map:
var classes = {
zinger:Zinger,
thinger:ns.Thinger
};
var type = "thinger";
var myClass = new classes[type](props, type, etc);
Should be doable using eval():
var obj = eval("new " + objectType + "()");