0

Example use case:

  1. I have an object with an attribute "myProperty", having getter and setter ("Property Getters and Setters" are supported since EcmaScript 5: https://www.w3schools.com/js/js_es5.asp):

    var obj = {
      myProperty:'myPropertyValue', 
      get myProperty(){
        return this.property;
      },
      set myProperty(value){
        this.property=value;
      }
    };
    
  2. I would like to bind that attribute to a view, which is the task of a custom function that is called bindProperty.

In order to pass the property myProperty to the function, I could do something like

bindProperty(obj, 'myProperty');

However, I would like to avoid to pass the property name as a hard coded String. Strings have the disadvantage, that they are not updated when the attribute name changes during refactoring.

If I would use

bindProperty(obj, obj.myProperty);

the function would only know the property value 'myPropertyValue' and not where the value comes from.

=>How can I pass/identify the property itself, without using a String?

A. Using reflection?

I could imagine something like

bindProperty(obj, ()=>obj.myProperty);

The function bindProperty would then have to do some reflection magic to find out the name of the attribute in the body of the lambda expression (pseudo code):

let attributeName = getNameofArgumentFromBodyOfLambdaExpression(lambda);
obj[attributeName] = 'newValue';

=>Is it possible in JavaScript to evaluate the body of the lambda expression using reflection to get the name of the property?

(I know that this can be done in .NET languages, e.g.

Private Sub BindProperty(Of T)(propertyExpression As Expression(Of Func(Of T)))

    Dim propertyName As String = GetPropertyName(propertyExpression)
'...

)

B. Using complex attributes

An alternative whould be that I use wrapping property objects, having their own getters and setters. Howerver, then I would have to use the property like

obj.myProperty.set('newValue')

or

obj.myProperty('newValue')  //similar to knockout observables

I still want to be able to use the great Getter/Setter feature. With other words: I want to use my properties like plain attributes:

obj.myProperty = 'newValue'

Therefore, this is not an option for me and I would prefer to use Strings instead of B.

C. Any other alternatives?

3
  • The problem in JavaScript is that an expression like obj.propertyName is fully evaluated in the context in which it appears, and all that's left is the value of the property. The value contains no trace of where it came from; it's just a value. Commented Sep 5, 2018 at 14:14
  • Exactly. That is why I would like to use a lambda expression or something to "encode" it. Commented Sep 5, 2018 at 14:15
  • You can't "introspect" or "reflect" on an expression, in other words. That's why code minifiers for JavaScript do not attempt to shorten object property names. Commented Sep 5, 2018 at 14:15

2 Answers 2

1

An object in javascript is more or less just a mapping of strings or symbols to values. There is no real reflection that you can call upon in the runtime environment that would enable you to move backward from the value to the property name.

If all you need is refactoring, the one way to do this would be to just configure your IDE to recognize string accessors by providing some sort of type information either via Flow or Typescript or something of that sort (the type information is likely what allows reflection to work in languages like .NET). Or you could just settle for a unique prefix like "viewable_propName" and just do simple find and replace if you need to rename.

If you are really focused on getting this to work without type information and in current ES6 syntax, you could do the following:

function getNameofPropFromVal(obj, val){
    for(let prop in obj){
       if(obj[prop] === val) return prop;
    }
}

obj[getNameofPropFromVal(obj, obj.myProp)] = 'newVal';

Though this has shortcomings: (1) There is no guarantee that two properties won't share the same value. (2) It introduces unnecessary runtime overhead.

Finally, if you're willing to be cutting edge and use a transformer like babel you could use decorators for your bindProperty method. That way you can just do the binding in the object definition itself. Here is an article explaining the gist and here is the more formal ECMAScript proposal.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the hint to decorators.
0

I just found following simple work around that might fullfill my needs:

function bindProperty(obj, lambdaExpression){

 let expression = lambdaExpression.toString();  // "()=> obj.myProperty"
 let subStrings = expression.split(".");
 let propertyName = subStrings[1]; 

 console.info(propertyName );
 //...

}

1 Comment

I'm glad you found something that works. I'm sure you know to document / abstract that well because that is definitely not going to be intuitive to read code for future maintainers of your project.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.