Hifi JS API: Foo.setBar() vs Foo.bar


#1

The Hifi JS API has more than one strategy for exposing parameters and properties. I’m wondering if anyone has an opinion as to which make the mose sense.

(1) Some features can accessed by setters/getters with a format like:

Foo.setBar(value) and Foo.getBar()

For example:

var oldVelocity = MyAvatar.getVelocity();
MyAvatar.setVelocity(newVelocity);

(2) Other features are accessed via a “property” which can be used to both get and set:

Foo.bar

For example:

var oldPosition = MyAvatar.position;
MyAvatar.position = newPosition;

(3) There is a third pattern that looks like:

Foo.getProperties()

Such as:

var properties = Particles.getProperties()
Particles.addParticle(properties)

So what system do you prefer?

Personally I kinda like method (3) “set/getProperties()” system because we can add new properties without adding new function calls. It also allows for object API introspection. However system (1) and/or (2) can make for a bit more readable code since the code will tend to read more like english sentences.


#2

Methods 1 and 2 also allow API introspection: the script currentAPI.js can see the methods and properties in both examples.


#3

Personally, I prefer 2) :

a) it’s more concise, and I think it speaks more to a "beginner"
b) If I want to modify the position of a particle, I want to be able to do:

var particle = new Particle();
...
particle.position = Vec3.sum(particle.position, { x: 1, y: 0, z: 0 }); 
// getting rid of Vec3... somehow would be good too

instead of

var particle = new Particle();
...
var properties = particle.getProperties();
properties.position = Vec3.sum(properties.position, { x: 1, y: 0, z: 0 }); 
particle.setProperties(properties);

However, I think you have a point Andrew about not having to write new function calls.
Also what we avoid in creating new functions, we lose by having to add code to read/write those properties to the JS object. (i.e.: classToScriptValue() / classFromScriptValue())


#4

Yeah, I’d be bummed if I had to write JS to extract “position” out of Properties, do some math, and then convert back into a Properties object.

Suppose a Particle had ten features: feature000 through feature009. You could imagine an interface that looks like this:

var oldValue = particle.getFeature001();
particle.setFeature001(newValue);

That requires two C++ methods to be implemented for each feature, so twenty methods in all. Meanwhile something like this:

var oldValue = particle.getProperty("feature001");
particle.setProperty("feature001", newValue);

could probably be implemented in only two C++ methods total.

Edit: removed reference to “boolean” properties. This system might work for most types.


#5

Variation (4) …

var oldPosition = MyAvatar.position;
...
var newPosition = ...;
var newOrientation = ...;
var newThrust = ...;
MyAvatar
    .position(newPosition)
    .orientation(newOrientation)
    .thrust(newThrust);

#6

I think this makes the most sense all round - fairly easy to read on the JS side and not too much work on the C++ side:


#7

@ctrlaltdavid this is a good idea but I think it is important we keep a certain symmetry between get/set.

@leviathan If we inherit from QScriptClass we don’t actually have to write those 20 methods but instead implement:

virtual QScriptValue	property(const QScriptValue & object, const QScriptString & name, uint id)
virtual QScriptValue:: PropertyFlags propertyFlags(const QScriptValue & object, const QScriptString & name, uint id)
virtual QScriptValue	prototype() const
virtual QueryFlags queryProperty(const QScriptValue & object, const QScriptString & name, QueryFlags flags, uint * id)

Those let you define any kind of behavior and object[“feature000”] === object.feature000
Every property access (get/set) goes through those methods.

But I have to say, I’d rather we avoid strings as much as possible.
This will make it much more easy to have autocompletion in the console as we will just have to do an introspection.


#8

Using a “property” (ie no setters/getters) is standard Javascript. However, it implies/requires that all of the vars that are effectively “bound” to values be constantly updated under the hood. Whereas using a getter, for instance, only evaluates and returns the value when asked for. There is a fundamental performance enhancement to be had using getters and setters however, it is non-standard Javascript.

Not sure that JS performance is an issue at this point, however.


#9

I think its best way if we can create own javascript units with simplified commands that thendo things the complex way. but for that we need to have access to other javascript function and variables like we talked about in other thread.


#10

Method 3 reminds me of Windows Powershell, which uses properties in that manner. That can work quite well as long as it’s straight forward enough to filter and manipulate what you’re getting and setting.


#11

It occurs to me that sometimes it will be important to be able to set multiple properties in one call. The JS script could get descheduled at any point so there is a possibility that a short period of time might separate these two calls:

foo.position = {x:1, y:2, z:3};
foo.velocity = {x:1, y:0, z:0};

whereas this call would change both at the same time:

foo.setProperties({position : {x:1, y:2, z:3}, velocity: {x:1, y:0, z:0}});

#12

I agree with @webdood that dot notation is the JavaScript norm. But I don’t think that there’s a performance hit for maintaining property values in the background …

A simple read/write property in MyAvatar.h:

Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
bool getShouldRenderLocally() const { return _shouldRender; }
bool _shouldRender;

And there’s no special maintaining of _shouldRender values in the background.

I think we should optimize for the JavaScript user rather than ease of implementation, and not stray too far from the JavaScript way of doing things. So I vote for option (2).

For time-critical property setting, having a setProperties() method such as @leviathan suggests available on all objects could be a worthy addition.


#13

Given that we have already encountered JS / C++ thread syncing issues when reading properties (MyAvatar.position), setting multiple properties at once would be a welcome (safer) option from my point of view - and having read other’s comments here, I’m with @ctrlaltdavid and @webdood - dot notation sounds like the way forward as the other option, if only because it represents the JavaScript norm.