Question

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

Était-ce utile?

La 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.

Autres conseils

No, you can't unfortunately. Instead you can create a bit different function that returns the first parentNode by default(if no parameter specified), otherwise it counts n parents.

Object.prototype.parent = function (n) {
    if (!this.hasOwnProperty('parentNode')) return null;
    if (!n || n === 1) return this.parentNode;
    var temp = this.parentNode;
    for (var i = 1; i < n; i++)
        temp = temp.parentNode;
    return temp;
};

Some notes: first of all, you should check if the Object has a property parentNode, otherwise the script will raise an exception. I used hasOwnProperty, but you can remove that line if you extend just HTMLElement.

Also, if n is 0 or 1, or it is not defined it will return the element's parentNode. Use it as element.parent() or element.parent(2).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top