Class that extends jQuery is not an instance of itself

Viewed 66

I created a simple class that extends jQuery. It doesn't do much except add a couple of extra class-specific methods. The problem, these methods can't exist because the object converts itself into a type jQuery and the custom class prototype is gone.

This is what happens:

class myClass extends jQuery {
    constructor(){
        super("<div>")
        this.append("<a>")
        // This works as your would expect
    }

    createLink(){
        this.find("a").attr("href", "//google.com")
    }
}

let obj = new myClass();
obj.createLink() // Uncaught TypeError: obj.createLink is not a function

what's strange is that outputting the prototype to the console shows that .createLink is a function:

console.log(myClass.prototype) // {constructor: ƒ, createLink: ƒ}

What's even more strange, the object doesn't even seem to be an instance of itself:

class myClass extends jQuery {
    constructor(){
        super()
    }
}
console.log(new myClass instanceof myClass) // false

But anything else works:

class myNewClass extends anyOtherObject {
    constructor(){
        super()
    }
}
console.log(new myNewClass instanceof myNewClass) // true

The problem only exists when I try to extend jQuery. Why is that?

2 Answers

Don't write classes that extend jQuery.

jQuery has its own extension point for custom functionality at $.fn, and these custom extensions are called plugins.

A createLink plugin could look like this:

$.fn.createLink = function (url) {
    $("<a>", {href: url}).appendTo(this);
};

usage:

$("div.bla").createLink("https://www.example.com");

That being said, the global jQuery object is a factory function, not a constructor. When you call $("something"), a new jQuery instance will be created internally, and returned.

Extending the factory function will not extend the jQuery instances you receive.

This happens because jQuery is not intended as a constructor. Even if you would call it with new jQuery(), you would not get an instance of that "class" back. It simply returns an object explicitly. This is not specific to jQuery. It happens with any function that is not constructor "aware". Simple example:

function A() {
    return { a: 1 }; // Explicit return of object
};

let a = new A;
console.log(a instanceof A); // false

class B extends A {
    myMethod() { }
}

let b = new B;
console.log(b instanceof B); // false

To extend jQuery with your own features, define your functions on jQuery.fn. See the documentation on plugins.

Related