1

So, I have an element prototype function called hasClass.

Element.prototype.hasClass = function (className) 
{
    if  (className && typeof className === "string") 
    {
        if (this.classList) 
        {
            return this.classList.contains(className);
        } 
        else
        {
            return new RegExp(className).test("" + this.className + "");
        }
    }    
};

Now, since I have to define multiple functions and the code will become pretty messy then I thought, why not use Object.defineProperties function; same function with the function:

Object.defineProperties(Element.prototype, {
    "hasClass": {
        get: function (className) 
        {
            if (className && typeof className === "string")
            {
                if (this.classList)
                {
                    return this.classList.contains(className);
                }
                else
                {
                    return new RegExp(className).test("" + this.className + "");
                }
            }
        }
    }
});

The first function works fine as it should. However, when defining same function with Object.defineProperties my Firefox console starts spamming: TypeError: $test.hasClass is not a function (Where $test is element choosen with selector). This error does not appear when using normal, first example of function, while it appears when using the lattest one.

So the question is. Why it does throw such error while it should not?

Oh yeah, funny thing is that when I use the second function with console.log like this:

Object.defineProperties(Element.prototype, {
    "hasClass": {
        get: function (className) 
        {
            /*if (className && typeof className === "string")
            {
                if (this.classList)
                {
                    return this.classList.contains(className);
                }
                else
                {
                    return new RegExp(className).test("" + this.className + "");
                }
            }*/
            console.log( "HI");
        }
    }
});

Then the console says this: https://i.stack.imgur.com/K6X2p.png

I am confused.

Jenz
  • 8,280
  • 7
  • 44
  • 77

2 Answers2

1

You're mis-using the property get you should be using value otherwise hasClass will be the value returned from the get function.

Replace get with value

Object.defineProperties(Element.prototype, {
    "hasClass": {
        value: function (className) 
        {
            /*if (className && typeof className === "string")
            {
                if (this.classList)
                {
                    return this.classList.contains(className);
                }
                else
                {
                    return new RegExp(className).test("" + this.className + "");
                }
            }*/
            console.log( "HI");
        }
    }
});

Refer to documentation on defineProperty even though you're using defineProperties

The problem: in your version the value of hasClass will be undefined because you haven't returned anything from the get function the last statement is console.log( "HI"); if you put return {}; after that, hasClass === {}

iConnor
  • 19,997
  • 14
  • 62
  • 97
  • Wow, thank you. It works no when I replaced get with value. Thanks! – user3595905 May 02 '14 at 11:43
  • @user3595905 you might also want to put `enumerable: false` and `writable: true` just to make it act exactly like native methods – iConnor May 02 '14 at 11:46
  • {IMHO} this is a mis-use of `Object.defineProperty`. The semantics of above `hasClass` is not like a typical property. So this answer is just a workaround not to get a `TypeError`, but it's not leading to good JavaScript.{/IMHO} – hgoebl May 02 '14 at 12:45
  • @hgoebl I answered the question, however, if OP is not using `enumerable: false` then yes this is pretty pointless, but he **should** be using `Object.defineProperty` with `enumerable: false` to achieve what he's trying to achieve – iConnor May 02 '14 at 16:55
  • I would say that at start I was thinking about using extend function, however then I started looking around if there are any better ways doing it like a native way and hence why I ended up with `Object.defineProperties`, which now after certain coding looks...useless. I think I will stay with simple extending rather than using some unripe native methods here. – user3595905 May 03 '14 at 07:33
0

Object.defineProperty and Object.defineProperties are not meant to define getter functions with params. In your case, just do it as in your first example (or look below, see Edit).

You can understand Object.defineProperty as a way to "intercept" the plain set/get action, but in the code where you get or set the property you can see no difference to "public" properties.

obj.abc = 'value';    // <-- set function called
console.log(obj.abc); // <-- get function called

There are no braces () - the functions are called transparently.

In case you really have properties, be careful not to run into recursive calls when the property you want to hide is actually as well public. See JS defineProperty and prototype

Edit

As I understand your question, you're looking for a less verbose way to add functions to a prototype. Many JavaScript libraries have a way to copy some properties from one object to another:

  • jQuery: jQuery.extend
  • Underscore/Lodash: _.extend
  • Leaflet: L.extend
  • sure there are many more (and maybe a modern version of ECMAScript already has such a utility method)

Here you have a very basic extend function, if you don't have one of those libraries at hand:

function extend1(dest, src) {
    var i;
    for (i in src) {
        if (src.hasOwnProperty(i)) {
            dest[i] = src[i];
        }
    }
    return dest;
}

Now you could extend your prototypes like this:

extend1(Element.prototype, {
    hasClass: function (className) {
        /* the body */
    },

    methodFoo: function (arg1, arg2) {},

    methodBar: function () {},
});

I think, for methods this is a better way than using Object.defineProperty. For real property definitions, take Object.defineProperty.

Community
  • 1
  • 1
hgoebl
  • 12,637
  • 9
  • 49
  • 72
  • Thanks for the response. I will take your tips and explanation. And again, thanks. – user3595905 May 02 '14 at 11:44
  • Yes, that is true that I was looking for a less verbose way to do that. In-fact I find the extending way better now after messing around with `Object.defineProperties`. Really. Thanks for the heads-up! – user3595905 May 03 '14 at 07:38
  • You're welcome. Are you looking for alternative solutions? If not, you may consider marking the answer as correct and/or helpful. – hgoebl May 03 '14 at 08:15