I want to create a new object with parameters from 'arguments', but I don't know
how to or even possible to convert it directly without cloning. Here's how it is possible using a clone:
function theClass(name, whatever) {
this.name = name;
this.whatever = whatever;
}
// I need to use the arguments passed from this function but without using clone
// as shown.
function doSomething()
{
function clone(args) {
theClass.apply(this, args);
}
clone.prototype = theClass.prototype;
return new clone(arguments);
}
// Usage expectation.
var myNewObject = doSomething("Honda", "motorbike");
console.log(myNewObject.name);
However, this suffers on performance because each time you call doSomething, you have to create a clone just to pass that arguments to be applied in it from theClass.
Now I want to pass that arguments without passing to a cloned object, but I don't know
how to convert it directly.
Any idea?
Note: As clarified by kaminari, the parameters passed are not strictly 'name' and 'whatever', but could be anything depends on the object I want to create. 'theClass' in the code is merely an example.
Thanks.
EDIT: In light of the intended use of these functions:
Probably your best option on maintaining your intended behavior is to implement your function in the following way:
function theClass(options){
this.name = options.name || ''; //or some other default value
this.whatever = options.whatever || '';
};
function doSomething(options){
options = options || {};
return new theClass(options);
};
With this implementation in mind, the code you supplied in "usage expectation" would look like this:
var myNewObject = doSomething({name: "honda", whatever: "motorbike"});
console.log(myNewObject.name);
In this manner, theClass can support as many or as few parameters as need be (only depends on what's supplied in the object and what you choose to extract from it) and similarly, the wrapper doSomething can be given as many or as few options as desired.
this suffers on performance because each time you call doSomething, you have to create a clone just to pass that arguments to be applied in it from theClass.
Simply define the clone function outside of doSomething, and it won't get recreated every time:
function theClass(name, whatever) {
this.name = name;
this.whatever = whatever;
}
function clone(args) {
theClass.apply(this, args);
}
clone.prototype = theClass.prototype;
function doSomething() {
return new clone(arguments);
}
Related
Is there a way for a function to get the object value before the function name? Below is an example of what I am kinda trying to achieve. I want to be able to read the myElement variable within the function but not pass it as a parameter but pass it before the function with the dot.
Any simple examples or explanations would be most helpful.
var myElement = document.getElementById('myID');
myElement.myFunction();
function myFunction() {
alert(myElement);
}
The only way you could do this is to add myFunction to HTMLElements prototype (which is what gets returned by document.getElementById(). That's usually frowned upon, but if it's your own project and you know what you do, you could do that.
var myElement = document.getElementById('myID');
HTMLElement.prototype.myFunction = function() {
console.log(this);
}
myElement.myFunction();
<div id="myID"></div>
With this prototype in place, you can call myFunction on every HTMLElement in your code.
In regards to your last comment, the function could be
HTMLElement.prototype.myFunction = function() {
alert(this.id);
}
I don't see why you should do it, as it's much easier to just do
alert(myElement.id);
In regards to the comments, here's what I'd do. Instead of extending anything, create your own class (or function), that takes a HTMLElement. Now on this class, you can add whatever method you want, manipulate your element and then return the plain HTMLElement from a getter. You can obviously change that to whatever return you want.
class MyHtmlElement {
constructor(htmlElement) {
this._htmlElement = htmlElement;
}
alertId() {
alert(this._htmlElement.id);
// optional
return this;
}
logId() {
console.log(this._htmlElement.id);
// optional
return this;
}
setId(newId) {
this.htmlElement.id = newId;
// optional
return this;
}
setStyle(prop, val) {
this._htmlElement.style[prop] = val;
// optional
return this;
}
get htmlElement() {
return this._htmlElement;
}
set htmlElement(value) {
this._htmlElement = value;
}
}
const el = new MyHtmlElement(document.getElementById('foo'));
el
.setId('bar')
.logId()
.alertId()
.setStyle('background-color', 'red')
.setStyle('width', '100vw')
.setStyle('height', '100vh');
// If you need the plain element, return it
const plainHTMLElement = el.htmlElement;
console.log(plainHTMLElement);
<div id="foo"></div>
When a function is stored in an object and then called with theObject.theFunction(), the value of this within the function will be theObject.
function sayHello() {
alert('Hello, my name is ' + this.name);
}
let myObject = { name: 'Bob', speak: sayHello };
myObject.speak(); // shows the message 'Hello, my name is Bob'
Now if you want to be able to create your own function and let it be used by an Element, you either need to store the function in the Element instance first or to add it to the Element prototype, both of which I highly discourage. If you feel like you have to do this, there's a flaw in your design.
Still, if you have a good reason to add a custom method to an existing object, I recommend you look up lessons about prototype inheritance in JavaScript, or read my old answer about it here if you're not sure how it works. You could say, make a function which adds methods to an object when it is called, like this:
function addMethods(elem) {
elem.speak = sayHello;
}
let myElement = document.getElementById('myID');
addMethods(myElement);
myElement.speak(); // Hello, my name is <value of the element's name attribute>
Or you could add the method to the prototype of all elements:
Element.prototype.speak = sayHello;
let myElement = document.getElementById('myID');
myElement.speak();
While browsers have let people do this since forever ago, there is technically no guarantee that Element is publicly available, or that its prototype is modifiable, or that you can add methods to Element instances. The Prototype framework (an inconveniently named third party library) has been using these techniques for a long time, but it did cause them a couple issues. jQuery prefers using a different approach, wrapping elements in another object on which custom methods are put.
I have following ViewModel:
var Order = function(data) {
this.fruits = ko.observableArray(ko.utils.arrayMap(data.Fruis, function(item) { return item; }));
this.vegetables = ko.observableArray(ko.utils.arrayMap(data.Vegetables, function(item) { return item; }));
};
I need to define some sub-properties and sub-observables bound to the specific instance, and some common methods for fruits and vegetables,:
var Items = function(data, type) {
var self = this;
self.type = type;
self.choice = ko.observable(-1);
self.choice.select = ko.computed(function(){
var id = self.choice();
// do stuff
});
self.choice.remove = function() {
var id = self.choice.peek();
// do stuff
};
self.add = function(code) {
//do stuff
self.choice(id);
};
};
Which is the right way to bind the function containing my set of methods and sub-observables, so that i can use the methods as follows:
orderViewModel.fruits.add("apples");
orderViewModel.fruits.add("bananas");
orderViewModel.fruits.choice(0);
orderViewModel.fruits.choice.remove();
console.log(ko.tpJSON(orderViewModel));
// prints: {fruits: [bananas], vegetables: []};
I think there is no need to use extenders, as the properties and methods aren't generic, and don't need to be common to all observables.
I tried by returning an observable array from my Item function, but i wasn't able to get this to work, as sub-properties and sub-observables have been lost. How can i bind Items to my observable arrays?
Even though you might not want to create an extender, what you're doing here is extending an observable array...
If you don't want to register an extender, you can create a small helper function to create an observableArray and add some methods and properties to it before you return.
In the example below you can see some example code. Some important advice:
If you use this approach, I'd suggest not overwriting the default methods in observableArray. E.g.: remove takes an item by default; you want it to work with an external choice index... It's best to pick a different name so you keep supporting both.
If you end up using the extension a lot, it might be worth it to create a clean viewmodel that stores the observable array internally. You can define a toArray method for exporting to a plain array.
var obsCollection = function(initialItems) {
var items = ko.observableArray(initialItems);
items.choice = ko.observable(-1);
items.add = items.push;
var ogRemove = items.remove.bind(items);
// I'd rename this to "deleteChoice"
items.remove = function() {
var index = items.choice();
ogRemove(items()[index]);
// Reset choice to -1 here?
};
return items;
};
var fruits = obsCollection(["Apple"]);
log(fruits);
fruits.add("Banana");
fruits.choice(0);
fruits.remove();
log(fruits);
fruits.remove();
fruits.add("Mango");
fruits.add("Lemon");
log(fruits);
function log(d) {
console.log(JSON.stringify(ko.unwrap(d)));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Edit to clarify the (lack of) use of this:
Since we don't use the new keyword, there's no real need to use this. Internally, the observableArray creates a new instance, but our only way of referring to this instance is through items. When detaching prototype methods from the array, we need to make sure we call them with the right context, by either bind or .call(items) (or apply).
If you want the code to look like a "class", you can either do: var self = items; and continue with the self keyword, or rewrite it to use the new keyword (last bullet point in my answer).
var myArray = ko.observableArray([1,2,3]);
try {
// Reference the function without binding `this`:
var removeFromMyArray = myArray.remove;
// Internally, the observableArray.prototype.remove method
// uses `this` to refer to itself. By the time we call it,
// `this` will refer to `window`, resulting in an error.
removeFromMyArray(2);
} catch(err) {
console.log("ERROR:", err.message);
console.log(myArray());
}
// By binding to the array, we ensure that the function reference
// is always called in the right context.
var boundRemoveFromMyArray = myArray.remove.bind(myArray);
boundRemoveFromMyArray(2);
console.log(myArray());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Yes, I know I used terms that do not apply at all or the way they apply to OOP languages.
When I define extension method in C# I can call it as instance method foo.call(bar) or Foo.call(foo,bar). I defined a "extension" method for Array equals(secondArray,comparer) that checks the equality of the elements. I call it now as myArray1.equals(myArray2).
However I would like to call it also as Array.equals(myArray1,myArray2).
How to make it is possible JS-way?
To elaborate on SLaks answer with an example: You can provide a "static" method and then provide an instance method that explicitly passes the instance to the static method.
var Obj = function(){
var _this = this;
this.x = 5;
this.equals = function(other){
return Obj.equals(_this, other);
}
}
Obj.equals = function(obj1, obj2){
return obj1.x == obj2.x;
}
obj1 = new Obj();
obj2 = new Obj();
console.log(obj1.equals(obj2));
console.log(Obj.equals(obj1, obj2));
Console output:
true
true
You need to make two separate methods; one on the prototype and one one the function.
One of them can simply call the other one.
Similarly to OozeMaster's answer, you can also write it in a more "OO" fashion this way (but still, you have to explicitly declare the "static" and member methods):
var Operation = (function () {
function Operation(firstOperand) {
this.firstOperand = firstOperand;
}
Operation.prototype.add = function (other) {
console.log(this.firstOperand + other);
};
Operation.add = function (first, second) {
console.log(first + second);
};
return Operation;
})();
Operation.add(1, 2); // prints 3
var op = new Operation(3);
op.add(4); // prints 7
PS: this is the kind of code that is generated by Typescript when you write static methods. If you want to write JS is a OOP fashion, you may want to have a look at typescript: http://www.typescriptlang.org/
I have a commercial application that has an existing JavaScript object structure using prototype chains. I have had success extending this API by adding more methods to the prototypes of objects. However, I realize that it would be best to add a namespace in front of my methods in case the application vendor decides to name a new method the same as one of my methods in a future release.
If I have an existing object called State, I would add a method called getPop like so:
State.prototype.getPop = function(){return this.pop;};
var Washington = new State('Washington',7000000);
Washington.getPop(); //returns 7000000
What I want to do is add a namespace called 'cjl' before my custom method to avoid name collision so that I can call it like so:
Washington.cjl.getPop();
I tried:
State.prototype.cjl = {};
State.prototype.cjl.getPop = function(){return this.pop;};
The problem is this. It doesn't point to the instance but instead points to the 'cjl' object.
I tried various methods, including using .bind() but none of them seemed to work. I finally found an answer here: Is it possible to organise methods on an object's prototype into namespaces? This works using the Object.defineProperty() method. The problem is the commercial application only works in compatibility mode in IE which doesn't support the Object.defineProperty() method for non-DOM elements.
Is there another way to accomplish this? I don't want to have to call multiple functions, which is the result of some techniques, e.g.:
Washington.cjl().getPop();
You could namespace in the following way, reading your comments I see that you can't change the original constructor so you'll have to replace the original with your own and save the original in a closure.
Every state instance will have it's own cjl instance but that only has a reference to current State instance, all the cjl functions are shared as they exist only once:
[UPDATE]
Forgot to get State.prototype in myState's prototype chain.
//the original constructor
function State(name, pop){
this.name=name;this.pop=pop;
}
State.org="original constructor";
//original constructor is available through
// closure and window.State is replaced with
// your constructor having the cjl namespace
(function(State){
//cjl namespace
function cjl(stateInstance){
this.stateInstance=stateInstance;
};
//cjl functions
cjl.prototype.getPopInThousands=function(){
//do not use this, instead use this.stateInstance
return this.stateInstance.pop/1000;
}
function myState(){
//apply State constructor
State.apply(this,arguments);
//create a clj instance, all methods
// are on cjl.prototype so they're shared
this.cjl = new cjl(this);
}
//inherit from State (use polyfil for older browsers)
myState.prototype = Object.create(State.prototype);
//replace window.State with your constructor
window.State=myState;
}(State))
var s = new State("Wasington", 7000000);
console.log(s.cjl.getPopInThousands());
//non standard, name
console.log("constructor name",s.constructor.name);
console.log("constructor tostring",s.constructor.toString());
More on constructor functions and prototype can be found here: https://stackoverflow.com/a/16063711/1641941
I have to agree with friend and cookie that pre fixing the function names may be the better solution but if you want to use the same methods for an object named Country then you may think of using the previous code as you can re use the cjl object.
Instead of defining State.prototype.cjl outside of the function, try to set the cjl "namespace" inside the constructor function.
function State(){
var thisObject = this;
this.cjl = {
getPop: function(){
return thisObject.pop;
}
};
}
Then you can do Washington.cjl.getPop();.
Try:
var State = function(name, pop) {
this.name = name;
this.pop = pop;
};
State.prototype.cjl = function(method) {
return this.cjlDefs[method].apply(this, Array.prototype.slice.call(arguments, 1) );
};
State.prototype.cjlDefs = {
getPop: function() {return this.pop;}
};
var Washington = new State('Washington', 80000);
console.log( Washington.cjl('getPop') );
https://jsfiddle.net/ghbjhxyh/
Or another shape if you prefer:
var State = function(name, pop) {
this.name = name;
this.pop = pop;
};
State.prototype.cjl = function(method) {
this.cjlDefs.obj = this;
return this.cjlDefs;
};
State.prototype.cjlDefs = {
assertObj: function() { /* Make sensible assertion */ },
getPop: function() { this.assertObj(); return this.obj.pop; }
};
var Washington = new State('Washington', 75000);
console.log( Washington.cjl().getPop() ); // 75000
https://jsfiddle.net/7vjrz2mn/
I admit this question is getting to the limits of what I know of JavaScript & jQuery, and there is probably a more proper way to state my question (which would help in finding an existing solution), but if you can bear with me, this is what I'm after.
I have an existing object class I've defined. I'm making a jQuery ajax call using getJSON, and I want my callback parameter (which is an object) to be classed as my custom object, so that I can access that class' methods from it.
So I have some object class
function Boo() {
this.param1;
this.param2;
this.yah = function() {
...
}
}
and then I have something elsewhere of the sort
$.getJSON(url,function(new_instance) {
//from my php source this passed object is already loaded with param1, param2...
alert(new_instance.param1); //no probs
//but i want to be able to then call
new_instance.yah();
});
In other words, I want new_instance to be considered an instance of Boo(). I know in stuff like ActionScript you have to class the incoming parameters for exactly this reason, dunno what flexibility I have in JS.
I thought maybe about having an intermediate function that takes in the incoming object and creates/populates a new instance of Boo() but not sure if there is a more clever method.
Many thanks!!
Do not define methods in the constructor function, you are
defining them over and over again every time the costructor
is called. Move them over to the prototype:
Boo.prototype = {
yah: function() {
},
bah: function() {
}
...
};
a little helper function:
function coerceTo( proto, values ) {
var r = Object.create( proto );
for( var key in values ) {
r[key] = values[key];
}
return r;
}
Depending on browser, Object.create might not be available, so:
if (!Object.create) {
Object.create = function (o) {
if (arguments.length > 1) {
throw new Error('Object.create implementation only accepts the first parameter.');
}
function F() {}
F.prototype = o;
return new F();
};
}
Usage:
new_instance = coerceTo( Boo.prototype, new_instance );
new_instance instanceof Boo //true
new_instance.yah();
What you can do:
$.getJSON(url,function(newObjData) {
var newObj = $.extend(new Boo(), newObjData);
newObj.yah();
});
Also consider moving your Boo methods to object prototype so the methods don't get recreated for each Boo instance:
var Boo = function() {
this.param1;
this.param2;
}
Boo.prototype.yah = function() {
console.log(this.param1);
}