I have multiple eatable classes in javascript eg: food, drinks, snacks.
Each of this class requires a different set of parameters. I have another factory class which creates an instance of the eatable item that is sent to it.
I am not able to figure out how can we dynamically select the eatable item and pass the arguments (which are in an array form) using this factory?
I have come up with two solutions -
Solution 1:
var factory = function(eatable, argumentList){
var obj = new eatable(argumentList);
return obj
};
This is a problem because argumentList is an array.
Solution 2
var factory = function(eatable, argumentList){
var obj = eatable.apply({}, argumentList);
return obj
};
this does not really create an object of the eatable type.
The effect that I really want
Say I am able to convert the argumentList into a js argument type object then -
var obj = new eatable(argumentList.toArguments());
obj instanceOf eatable; // should return true
Please help!
Ah, yes. I've encountered this problem before - you can't use new and apply together in JavaScript. A similar question has been asked before: Use of .apply() with 'new' operator. Is this possible?
The problem is quite apparent - new is a keyword, not a function; and apply can only be used on a function. If new was a function instead of a keyword then we could use it in conjuction with apply.
To understand how to do so let's create a function called new which does exactly what the keyword new does:
Function.prototype.new = (function () {
function Factory(constructor, args) {
return constructor.apply(this, args);
}
return function() {
Factory.prototype = this.prototype;
return new Factory(this, arguments);
};
}());
Now instead of calling a constructor as follows:
var object = new constructor(arg1, ...);
You can call a constructor as follows:
var object = constructor.new(arg1, ...);
What's the advantage of doing so you ask? Well it's simple really. Because new is now a function instead of a keyword you can use it in conjunction with apply as follows:
var object = Function.new.apply(constructor, [arg1, ...]);
Hence your eatable factory function now becomes:
var factory = function(eatable, argumentList) {
var obj = Function.new.apply(eatable, argumentList);
return obj;
};
Edit: If all your factory function does is take an eatable constructor and an argumentList and return new.apply(eatable, argumentList) then as Bergi pointed out in his comment you could define factory as follows instead:
var factory = Function.apply.bind(Function.new);
Hope this helped.
You can use Object.create to set up the prototype chain correctly:
function factory(eatable, argumentList){
var obj = Object.create(eatable.prototyope);
return eatable.apply(obj, argumentList) || obj;
}
This is basically what the new operator does.
You can define a function init to initialize the object .
function Eatable(){
}
Eatable.prototype.init = function(/** arg1, arg2, arg3 **/){
// initialize object
}
In factory function
var eatable = new Eatable();
eatable.init.apply(eatable, /** pass arguments array here **/);
return eatable;
You have to provide context to apply, The context is the object you are trying to apply the arguments to. The context you are currently passing {} is of type Object
var factory = function(eatable, argumentList){
var obj = eatable.apply(new Eatable(), argumentList);
return obj
};
I can not use factories with out polymorphism so if you didn't create those eatables in way they extend an Eatalbe object you will not be able to do it.
One more way to achieve this is as follows -
var _bind = Function.prototype.bind;
var factory = function(_constructor, _argumentList){
var obj = _bind.apply(_constructor, [null].concat(_argumentList));
return obj
};
Related
I am working with Javascript and Appdescriptors in JSON format.
What I want to do is creating a instance of a Class, where the classname is saved as string in oModelConf[sModelName].type. If that is not the case I want to take "sap.ui.model.odata.ODataModel"
Related Question offers this solution:
function instantiate(className, args) {
var o, f, c;
c = window[className]; // get reference to class constructor function
f = function(){}; // dummy function
f.prototype = c.prototype; // reference same prototype
o = new f(); // instantiate dummy function to copy prototype properties
c.apply(o, args); // call class constructor, supplying new object as context
o.constructor = c; // assign correct constructor (not f)
return o;
}
This is not a very good solution I think.
EDIT It does not work for me because my class is not defined on window, so window[className] is undefined. I do not know where my function is defined in SAPUI5
A second Solution:
eval(`a = new ${oModelConf[sModelName].type || "sap.ui.model.odata.ODataModel"}(sServiceUrl, true);`);
This is not a better solution because we should not use eval().
Are there any better solutions?
EDIT2
Because of the url in pimskies answer I found an other solution:
Since window.sap.ui.model.odata.ODataModel is the same as sap.ui.model.odata.ODataModel and window.sap is the same as window[sap]
I could take my string, and replace all . with ][, put the right brackets to front and end.
I will not code that because it is not a going solution.(I should not have coded the evalthing too...)
You could use jQuery.sap.getObject to access the class:
var ModelClass = jQuery.sap.getObject(oModelConf[sModelName].type || "sap.ui.model.odata.ODataModel");
var model = new ModelClass();
Edit: An other way (which i would recommend if you use AMD)
If you are using the modern AMD modules and you don't know if the module containing your class has already been loaded, you should use sap.ui.require() to load the module asynchronously. It requires the module to be specified via its unified resource name (the conversion is probably the most ugly part):
var className = oModelConf[sModelName].type || "sap.ui.model.odata.ODataModel";
var urn = className.replace(".", "/"); //Convert to unified resource name
sap.ui.require([urn],function(ModelClass){
//This function is called when the module is available
var model = new ModelClass();
...
});
Maybe map the string to a class?
function Foo() {
console.log('new foo');
}
function Bar() {
console.log('new bar');
}
var objects = {
'foo': Foo,
'bar': Bar
};
var cls = objects.foo || Bar;
new cls();
https://jsfiddle.net/ckd56d9v/1/
Or take a look at this answer: https://stackoverflow.com/a/9804142/5930258
What not combine the two? Since window[className] is failing, replace it with
oModelConf[className].type || sap.ui.model.odata.ODataModel...
function instantiate(className, args) {
var o, f, c;
c = oModelConf[className] || sap.ui.model.odata.ODataModel;
f = function(){}; // dummy function
f.prototype = c.prototype; // reference same prototype
o = new f(); // instantiate dummy function to copy prototype properties
c.apply(o, args); // call class constructor, supplying new object as context
o.constructor = c; // assign correct constructor (not f)
return o;
}
I have an object that looks like
var customObject = function() {
this.property = "value";
};
customObject.prototype = new otherObject();
customObject.prototype.property2 = function() {};
etc. - it's much bigger than this.
I can successfully instantiate the object by writing new customObject().
Now I would like to create a rather similar object, although a little different. This involves modifying certain properties and perhaps even adding or removing some. As in the above example, I would like it to be invokable by writing new customObject2().
I thought I could simply do:
var customObject2 = new customObject();
customObject2.prototype = customObject.prototype;
customObject2.property = "modified value";
etc.
However, when I try to instantiate it by doing new customObject2() I receive an error, stating that the customObject2 is not a function.
I hope I could illustrate well enough as to what pattern I desire to create. What approach should I take to create such a pattern?
If customObject is not a host object (i.e. won't give you an illegal invocation error if you try to call it differently to expected) you can apply the constructor to a different this Object;
var customObject2 = function () {
customObject.call(this); // construct as if `customObject`
// now do more stuff
this.anotherProperty = 'foo';
};
customObject2.prototype = Object.create(customObject.prototype);
// inherit prototype but keep original safe
new customObject2();
Backwards compatible Object.create
function objectWithProto(proto) {
var f;
if (Object.create) return Object.create(proto);
f = function () {};
f.prototype = proto;
return new f();
}
I think this should answer your question. Basically, the new keyword is returning an object and not a function.
Why are you not using the same formula you used the first time? For example:
var customObject2 = function(){};
customObject2.prototype = new customObject();
customObject2.property = "modified value";
new customObject2(); // works!
All properties of customObject will be inherited by the instances of customObject2 through the prototype chain.
I happened to notice Javascript also supports the Function keyword just as in ActionScript. Since both are derived from ECMA Script, existence of such similarities is a common knowledge. But I was curious on what Function represents in case of Javascript, if not a Class(as in as3).
In JS,
var func = new Function(); or var func = new Function;
Assigns a function called 'anonymous' to the variable 'func'.
Could it be simply a method to implement the actual 'function' in an Object oriented way..
Since most browsers(ff, ie & chrome) seem to implement it in the same way, is it addressed in the spec?
Function is the "class" all function extend from. All functions are really Function objects.
(function(){}) instanceof Function === true
You can also use new Function to make a function from a string (like eval).
var func = new Function('x,y', 'return x+y;')
"Every function in JavaScript is actually a Function object." MDN Documentation.
function myFunc(a, b) {
console.log( 'test' );
}
is essentially equal to
var myFunc = new Function('a', 'b', 'console.log( "test" )');
There are however some differences between to two ways of declaring a function. Read the doc!
1 way:
var Person = function() {
this.name;
this.age;
}
var p1 = new Person();
2nd way:
function Person() {
this.name;
this.age;
}
var p1 = new Person();
I understand that there are multiple ways to create an object in javascript and I have been reading that object literal syntax is generally preferred. (Correct?)
What I haven't been able to figure out is if there is ever a reason to use any of the other ways to create objects, such as a custom constructor function (var p = new Person("Adam"))? Is it true to use a custom constructor function only if I want private variables or to add methods or properties to its prototype? Is there no way to do these in a literal?
You can use the custom constructor function when you want to create instances of objects, similar to Java.
For example:
function MyObj(x){
this.x = x;
}
MyObj.prototype.printX = function(){
alert(this.x);
}
var obj1 = new MyObj("hello");
var obj2 = new MyObj("hello2");
obj1.printX();//prints hello
obj2.printX();//prints hello2
Now I have two instances of this object. If I used String literals I would need to clone the object into a new var in order to get another instance.
The discussion usually is about to prefer
var myObject = {};
over
var myObject = new Object();
If you however create your own constructor functions you are perfectly allowed to instantiate them with the new keyword, nothing controversial there.
The preferred method would be to use JSON: var p = { "name":"Adam" };
If you have a lot of member variables you need to initialize, or will be using a lot of objects (such as an array of them), etc. then it only makes sense to go ahead and create a function (constructor) which will do all of this for you. Unless you want your code to look like this:
var a = { "name":"Adam", "age":23, "city":"Boston" };
var b = { "name":"Jeff", "age":24, "city":"San mateo" };
var c = { "name":"Aaliyah", "age":25, "city":"New York" };
var d = { "name":"Mary", "age":26, "city":"Dallas" };
I want to create a single object. Does the below code make sense?
singleObj = new function () {
// act as a constructor.
};
Am I hurting any good practice?
I need a constructor. A simple object literal would not be useful here.
If you want just a single object, in that you are never going to make one again, an object literal works perfectly here.
var x = { };
Will give you an object.
In order for
var F = function() {
};
to give you an object you will need to invoke F
var x = new F();
you could try someting like:
var objCreate = function() {
var obj = {};
// do constructor logic
return obj;
};
Just create a new object and then populate it. You don't need a contrustor to do this.
var singleObject = {};
singleObject.value1 = "whatever";
If you really want to use a function, then you need to actually call it.
var singleObj = new (function () {
// act as a constructor.
})();
We can use a self executing function by creating a anonymous function function(){}, and immediately calling it with an empty argument set.
http://www.w3schools.com/js/js_objects.asp
//Used to create an object
personObj=new Object();
personObj.firstname="John";
personObj.lastname="Doe";
personObj.age=50;
personObj.eyecolor="blue";
//Used as a constructor for the object
function person(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
}
//how to declare objects via constructor template
var myFather=new person("John","Doe",50,"blue");
var myMother=new person("Sally","Rally",48,"green");