Search code examples
javascriptdefineproperty

use defineProperty get and value together


If I use get with defineProperty

Object.defineProperty(Object.prototype,'parent',{
    get:function(){return this.parentNode}
});

and I can call it like: document.body.parent, then it works.

When I use value with defineProperty

Object.defineProperty(Object.prototype,'parent',{
    value:function(x){
        var temp=this.parentNode;
        for(var i=1;i<x;i++){temp=temp.parentNode};
        return temp
    }
});

I can call it like: document.getElementsByName("newtag").parent(2), means to find the parent node of newtag's parent node.

But when I put them together it says Uncaught TypeError: Invalid property. A property cannot both have accessors and be writable or have a value.

How can I do it so that I can call it both ways, .parent & .parent(n)?

No jQuery


Solution

  • MDN describes the reason for the error

    Property descriptors present in objects come in two main flavors: data descriptors and accessor descriptors. A data descriptor is a property that has a value, which may or may not be writable. An accessor descriptor is a property described by a getter-setter pair of functions. A descriptor must be one of these two flavors; it cannot be both.

    There is reason for this: the way you want would be ambiguous: if parent() was a function, then parent would return that function and the getter?

    Also do not change object that you don't own. Such code would not be maintainable: if someone would define its own Object.prototype.parent() in his library, you could not use it. Prior to any use of any code, you would need to track down what was changed. This would be the same effort as writing everything from scratch.

    Object.prototype is particularly bad idea to change: by the prototype you add the parent() function to every array, JSON object, webAPI objects... they don't have parentNode, so that function is completely useless to them, it is just a performance burden.

    The previous two paragraphs are the reason why we have Object.defineProperty and not Object.prototype.defineProperty. Note that if it would be so, you could code myAPI.defineproperty(...) in the code below, which is shorter, BUT... the performance and the design... schrecklich.

    You could code something like this

    var myAPI = {
      node: document.body,
      level: 1
    };
    
    Object.defineProperty(MyAPI,'parent',{
        get:function(){
            var temp=MyAPI.node.parentNode;
            // some sanity check should be performed here
            for(var i=1;i<MyAPI.level;i++){temp=temp.parentNode};
            return temp;
        }
    });
    
    myAPI.node = document.getElementById("myNode");
    myAPI.level = 2;
    var grandFather = myAPI.parent; // actually returns the grandparent
    

    but I doubt it would be useful.