Home > other >  JavaScript Class with same name for property and method
JavaScript Class with same name for property and method

Time:02-08

When researching a problem I ran into when trying to remove an event listener that uses bind() similar to the syntax in use by the class constructor below (I am not trying to remove this one, but it would not be successful if I tried), I found two sources that show a solution to that similar to that in use by the onDown method below (I do want this one removed once no longer needed) that creates a separte function that does the binding and the addEventListener attaches the function by its name:

class TestClass {
    constructor(el) {
        el.addEventListener('mousedown', function () { this.onDown(el.id) }.bind(this));
    }

    onDown(id) {
        // Here a property named onUp is created:
        this.onUp = this.onUp.bind(this, id);
        document.addEventListener('mouseup', this.onUp);
    }
    // Here a method named onUp is defined in the class:
    onUp(id) {
        document.removeEventListener('mouseup', this.onUp);
        console.log("Mouse down on "   id   " and then mouse up.");
    }
}

const d1 = new TestClass(document.getElementById("div1"));
const d2 = new TestClass(document.getElementById("div2"));
<div id="div1">Div 1: Click me and let go!</div>
<div id="div2">Div 2: Click me and let go!</div>

This code works and achieves my desired results, but what I observed is this creates a both property and a method with the same name, named onUp, in the objects created. This can also be verified by looking at the properties of the objects created (see image below). This is not otherwise possible as confirmed by the error received when calling the method that uses the same name as a property here:

class Test {
    sameName = "property";
    sameName() { return "method"; }
}

const testObject = new Test();
console.log(testObject.sameName); // Here Console outputs the value of the sameName property
console.log(testObject.sameName()); // This fails calling the sameName() method

Can someone help explain to me how this is working in the first example where an object has a property and method both with the same name? Is this a problem?

Note, I have alternate ways of doing this too. I can otherwise simply give one of them a different name:

onDown(el) {
    this._onUp = this.onUp.bind(this, el);
    document.addEventListener('mouseup', this._onUp);
}

onUp(el) {
    document.removeEventListener('mouseup', this._onUp);
    console.log("Mouse down on "   el.id   " and then mouse up.");
}

Or, the { once: true } option works for the example, but not in the actual code I am writing that uses different events (this is just a simplified example):

onDown(el) {
    document.addEventListener('mouseup', function () { this.onUp(el) }.bind(this), { once: true });
}

onUp(el) {
    console.log("Mouse down on "   el.id   " and then mouse up.");
}

Adding an image that shows the output properties of one of the TestClass objects. It first lists the property values. Since my example only had 1 property, I added another (named anotherProperty) to show the collection of properties, for control purposes. And under Prototype, it lists all the methods in the object. Both sections have an entry named onUp.

onUp included under properties and methods

And here is another image if the output of the other Test object I built expecting to fail. In here you can see it is built with the same design; it has a property named sameName (which can be referenced) and it has a method under Prototype also names sameName (which fails when calling).

enter image description here

This failure is as expected. But again, in my first example, there is both a property and a method, both with the same name, onUp, and the method does function.

CodePudding user response:

A method is just a property with a function for its value.

A property is a named “thing” attached to an object.

When you create an instance of a class in JavaScript (i.e. call a function with the new keyword) it creates an object with a prototype which is a reference to the class.

When you try to access a property on an object, it first checks the object itself to see if it has such a property. If it can’t find it, it looks at the prototype and checks that object for a property with that name. Then it checks the prototype of that object and so on until it runs out of objects on the prototype chain.

This is how inheritance works in JavaScript.

———

So when you call new TestClass you create an object which has a prototype which has an onUp method.

When the mousedown event fires, you say:

this.onUp = this.onUp.bind(this, id);

Which:

  1. Looks for onUp on the object and doesn’t find it
  2. Looks up the prototype chain
  3. Finds it on the call
  4. Calls bind on it to create a new function
  5. Copies that function to the onUp property of the object itself (creating it)

Now you have an onUp property on the object itself and on the object up the prototype chain.

This generally isn’t a problem but in your specific case, it creates an inefficiency.

The second time you trigger the mousedown event it finds onUp on the object itself, but still calls bind on it to create a new function (which calls the previous function which calls the previous function).

Every time there is another mousedown event you get an additional layer of binding.

You could avoid this by testing with the hasOwnProperty method (which will tell you if the value exists on the object itself and not up the prototype chain).

  •  Tags:  
  • Related