With this script I add variables to an object on runtime :
function MyDocument(someDocument)
{
if(!(someDocument instanceof KnownDocumentClass))
throw "Object must be an instance of KnownDocumentClass: " + someDocument;
this.Document = someDocument;
this.Fields = {};
this.updateValues = function()
{
for (var _it = this.Document.iterator(); _it.hasNext();)
{
var _property = _it.next();
try
{
this[_property.getQualifiedName()] = _property.getContent();
}
catch(err)
{
log("Error :"+err);
}
}
}
this.updateValues();
}
So, for example, I can use
var mydoc = new MyDocument(knownjavadoc);
log(mydoc.Creator) // Shows the original content.
This content could have multiple types (some are int, some Strings and a lot other custom java classes). So it can happen that log(mydoc.SomeProperty) returns :
PropertyObjectImpl[id=abc123, data=Some Data, type=Node, order=42]
I know, that I could add a function to MyDocument like
this.getValueAsString = function(name)
{
var _prop = this[name];
if(_prop instanceof PropertyObjectImpl)
return "PropertyObject with ID : " + _prop.getID();
else
return _prop;
}
But for exercise purposes I want to add this function as toValueString() directly on these properties, so that a call like :
var value = mydoc.SomeProperty.toValueString()
instead of
var value = mydoc.getValueAsString("SomeProperty");
Is this possible?
You can just override the .toString() implementation for the types in question, rather than implementing something which is likely going to do the same thing.
Overriding .toString() on existing types
Number.prototype.toString = function() {
// return .toString() logic for Number types
}
Boolean.prototype.toString = function() {
// return .toString() logic for Number types
}
Overriding .toString() on a custom type
var CustomType = (function() {
function CustomType() {
// CustomType logic
}
CustomType.prototype.toString = function() {
// return .toString() logic for CustomType
}
return CustomType;
})();
Remember, toString() is built into the JavaScript specification for all objects, so you'd likely stick so convention overriding this, rather than implementing your own method. This is also less likely to break than implementing a custom method because .toString() should be callable from any property, whereas .toValueString() will only be callable on properties that implement it.
EDIT: If your method needs to return a completely custom string, for any type, then you need to ensure that you bind your custom method implementation to exiting types (Number, String, Boolean, Function, Object etc)
EDIT 2: As pointed out, overriding the default implementation of toString is considered bad practice, so another idea would be to bind your custom method at the Object level, so that it is callable from anything (since virtually everything in JavaScript extends Object)
Object.prototype.toValueString = function() {
// return default implementation for this method;
}
CustomType.prototype.toValueString = function() {
// return specific implementation for this method;
}
I'm a bit confused to your question, but I'll give it a shot.
In JS, there is a standard interface for converting a value to a string: toString(). This is implemented on Object, which means all objects (and primitives casted to objects), will have the expected behavior.
var obj = {
age: 25,
customField: {
name: "test",
toString: function () { return this.name };
}
};
obj.age.toString(); // "25"
obj.customField.toString() // "test"
As a side note, I would only capitalize variables/references that are function constructors (js classes). This is pretty much standard in the community.
Related
I have been experimenting with getters and setters with the following pattern:
var mytab = {
_tab: undefined,
get: function () {
return this._tab;
},
set: function (tab) {
this._tab = tab;
return tab;
}
}
My question is, given you have to access those methods explicitly, ie:
mytab.get();
mytab.set('thistab');
Why bother having get or set at all? Why not call them whatever you like? ie:
var mytab = {
_tab: undefined,
getthetab: function () {
return this._tab;
},
setthetab: function (tab) {
this._tab = tab;
return tab;
}
}
I may have missed some fundamental principle here, but both these objects behave exactly the same.
I assumed having special 'setters' and 'getters' would allow the object to be modified using it's object name, ie:
var a = mytab;
mytab = 'thistab';
Or even:
var a = mytab();
mytab() = 'thistab';
This is what I expected, and what I wanted, however those instructions give errors, namely that mytab() is not a function.
I would appreciate some clarity on what special significance the set and get object methods actually have.
In your first example, you haven't declared getters/setters. You've created an object with two methods called get and set.
To declare getters/setters, you'll have to choose an arbitrary name, and prefix it with get or set, like:
var mytab = {
_tab: undefined,
get tab() {
return this._tab;
},
set tab(tab) {
this._tab = tab;
return tab;
}
}
In this case, they form a so-called accessor property, that has the chosen name:
console.log(mytab.get) //undefined
console.log(mytab.set) //undefined
mytab.tab = 'foo'
console.log(mytab._tab) //foo
mytab._tab = 'bar'
console.log(mytab.tab) //bar
console.log(Object.getOwnPropertyDescriptor(mytab, 'tab')) /*
{
get: function(){...},
set: function(tab){...},
...
}
*/
However, you cannot overload operators or otherwise define a single getter/setter pair for your objects, that would allow you to assign a value or read a value from the object itself.
You can only define getters/setters for the properties on the object.
So,
var a = mytab
or
mytab = a
cannot be intercepted, and doesn't do what you expect (the first assigns the object itself to another variable (a), while the second reassigns the variable mytab with the value of a without even affecting / interacting with the object).
The following use case can illustrate advantage of using getters and setters
var person = {
firstName: "John",
lastName: "Doe",
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
};
console.log(person.fullName);
Using getter we could use get fullName as if it was a property of person without the need of maintaining separate field.
Apologise if the title is worded poorly.
What I am essentially tying to do is create a custom type which has methods on it. It's intended that this act as a generic type, so it can accept a string, int, etc
const foo = new CustomType('value')
console.log(foo) // 'value'
console.log(foo + 'hello') // 'valuehello'
foo.method() // Do something
const bar = new CustomType([])
bar.push('foobar')
If I use a class to store the value, I couldn't do operations on it.
class CustomType {
constructor(value) {
this.value = value
}
method() {}
}
const foo = new CustomType('value')
console.log(foo + 'hello') // [object Object]hello"
Similar to how you can use new Array() or new String()
What you're trying to do is possible with prototypes (which is
mimicked by classes in JS)
function CustomType(param) {
this.default = param;
}
CustomType.prototype.toString = function(postfix = "") {
return this.default + postfix;
};
CustomType.prototype.doSomething = function() {
console.log("I am doing something")
};
let customTypedObject = new CustomType("value");
console.log(customTypedObject.toString());// "value
console.log(customTypedObject.toString("hello")); // "valuehello
//Prototypal function inherited
customTypedObject.doSomething() //"I am doing something"
Coercion Overriding
What you're actually explaining sounds a lot like overriding coercion rules in javascript which defines how a custom type behaves with another primitive type. This is also possible.
function CustomType(param) {
this.default = param;
}
CustomType.prototype.valueOf = function() {
return this.default;
}
let customTypedObject = new CustomType("value")
console.log(customTypedObject + "hello"); //valuehello
Read more here
If you are trying to override the browser's default approach to "toString()" an object. This is not possible. However there is a way to do this inside nodejs though. Node internally calls "inspect" on an object which is available in node js's root object prototype. This can be overriden
//Works on NodeJS code (not in browser)
CustomType.prototype.inspect = function() {
return this.default;
};
let customTypedObject = new CustomType("value")
console.log(customTypedObject); // "value"
It's nearly impossible for the code, as given, to produce the desired results. When you do console.log(foo), for that to result in 'value' (and only 'value') being logged, foo can only contain the exact string 'value'. It can't even be 'value' wrapped in a String (or custom String) object.
console.log(new String('value'));
console.log, if passed a single variable, will log exactly what that variable passed to it is. If the variable is an object, it'll log that object, not anything else (like a string or a number). If the variable is a plain string or a number, then it won't have other methods on it, unless you mutate String.prototype or Number.prototype, which you should not do.
The best you can probably achieve would be to have an instance which, when coerced to a string, returns the desired stringified value:
class CustomType {
constructor(item) {
this.item = item;
}
toString() {
return this.item;
}
method() {
console.log('doing something');
}
}
const foo = new CustomType('value')
console.log(foo) // CustomType {item: "value"} - look in browser console, not snippet
console.log(foo + 'hello') // 'valuehello'
foo.method() // Do something
I said nearly impossible, not absolutely impossible. If you mutate the prototype of the passed object, you can put method on it to make foo.method() work:
const addCustomMethod = (item) => {
Object.getPrototypeOf(item).method = () => {
console.log('doing something');
};
};
const foo = 'value';
addCustomMethod(foo);
console.log(foo) // 'value'
console.log(foo + 'hello') // 'valuehello'
foo.method() // Do something
But mutating built-in prototypes is a horrible idea. The above snippet is for informational purposes only, please do not use it.
The JSON.stringify behavior can be altered by overriding .toJSON method:
var obj = {toJSON: function() {return [1,2,3];}};
var x = JSON.stringify(dd);
console.log(x); // "[1,2,3]"
JSON.parse(x); // [1,2,3]
I'd like to pass the javascript pseudo-class instances (objects inheriting from other objects). However it doesn't seem possible to add any function call in the data:
function Pseudoclass(x) {
this.x = x;
//More operations
}
If you return function, .stringify fails. Not talking about the fact that it doesn't seem very possible to pass the class properties:
//JSON.stringify(inst) will be undefined
Pseudoclass.prototype.toJSON = function() {
//If converted to string, the function loses variables from this scope
return function() {return new Pseudoclass();};
}
If you return string, it's encoded as string:
//JSON.stringify(inst) returns "\"Pseudoclass.fromJSON(6)\"" if x was 6
Pseudoclass.prototype.toJSON = function() {
return "Pseudoclass.fromJSON("+this.x+")";
}
So does anybody has any hacks in mind? I'd use this to pass prepared class instances to Worker - the only option there is figuring this JSON question or a custom format.
Don't forget that the constructor call may not be the only way. All I need is that the object inherits from my predefined object!
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);
}
STORE = {
item : function() {
}
};
STORE.item.prototype.add = function() { alert('test 123'); };
STORE.item.add();
I have been trying to figure out what's wrong with this quite a while. Why doesn't this work? However, it works when I use the follow:
STORE.item.prototype.add();
The prototype object is meant to be used on constructor functions, basically functions that will be called using the new operator to create new object instances.
Functions in JavaScript are first-class objects, which means you can add members to them and treat them just like ordinary objects:
var STORE = {
item : function() {
}
};
STORE.item.add = function() { alert('test 123'); };
STORE.item.add();
A typical use of the prototype object as I said before, is when you instantiate an object by calling a constructor function with the new operator, for example:
function SomeObject() {} // a constructor function
SomeObject.prototype.someMethod = function () {};
var obj = new SomeObject();
All the instances of SomeObject will inherit the members from the SomeObject.prototype, because those members will be accessed through the prototype chain.
Every function in JavaScript has a prototype object because there is no way to know which functions are intended to be used as constructors.
After many years, when JavaScript (ES2015 arrives) we have finally Object.setPrototypeOf() method
const STORE = {
item: function() {}
};
Object.setPrototypeOf(STORE.item, {
add: function() {
alert('test 123');
}
})
STORE.item.add();
You can use JSON revivers to turn your JSON into class objects at parse time. The EcmaScript 5 draft has adopted the JSON2 reviver scheme described at http://JSON.org/js.html
var myObject = JSON.parse(myJSONtext, reviver);
The optional reviver parameter is a
function that will be called for every
key and value at every level of the
final result. Each value will be
replaced by the result of the reviver
function. This can be used to reform
generic objects into instances of
pseudoclasses, or to transform date
strings into Date objects.
myData = JSON.parse(text, function (key, value) {
var type;
if (value && typeof value === 'object') {
type = value.type;
if (typeof type === 'string' && typeof window[type] === 'function') {
return new (window[type])(value);
}
}
return value;
});
As of this writing this is possible by using the __proto__ property. Just in case anyone here is checking at present and probably in the future.
const dog = {
name: 'canine',
bark: function() {
console.log('woof woof!')
}
}
const pug = {}
pug.__proto__ = dog;
pug.bark();
However, the recommended way of adding prototype in this case is using the Object.create. So the above code will be translated to:
const pug = Object.create(dog)
pug.bark();
Or you can also use Object.setPrototypeOf as mentioned in one of the answers.
Hope that helps.
STORE = {
item : function() {
}
};
this command would create a STORE object. you could check by typeof STORE;. It should return 'object'. And if you type STORE.item; it returns 'function ..'.
Since it is an ordinary object, thus if you want to change item function, you could just access its properties/method with this command.
STORE.item = function() { alert('test 123'); };
Try STORE.item; it's still should return 'function ..'.
Try STORE.item(); then alert will be shown.