3

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.

4
  • 3
    but what I observed is this creates a both property and a method with the same name, named 'onUp', in the objects created. This is not possible, a method is not different from a property, it's just an informal term for a property which happens to hold a function value. Can you please share what it is that makes you think this is the case? PS by far the easiest way to do what you seem to be wanting is to bind in the constructor: this.onDown = this.onDown.bind(this) and the same with onUp. Commented Feb 7, 2022 at 21:54
  • "this creates a both property and a method with the same name" - no it doesn't. There's only one property, holding a (bound) function. "This is not otherwise possible as confirmed by the error received when calling the method that uses the same name as a property here" - well in that example you don't assign a function to the property, but then attempt to call it. Commented Feb 7, 2022 at 22:53
  • To share with you what makes me this this is the case is: 1) In the definition of the class TestClass, there is a method on the line that starts with onUp(id) 2) The 1st line in the this.onUp(id) method is this.onUp = which sets a property. 3) I will attach an image of the properties on one of the TetsClass objects. In the image, you can see it first lists all the properties. Since my example only had 1 property I added another. And in the Prototype it lists all the methods. Both sections have an entry named onUp. Commented Feb 7, 2022 at 23:34
  • In the first case, the property is a property of the object instance itself. The method is a property of the prototype. This is how they both can exist. In the second case, you're trying to define them both on the object instance itself. Commented Feb 8, 2022 at 0:19

1 Answer 1

4

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

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for the detail. I did notice when I added the images per the one time I took a screenshot it happened to include bound multiple times name: "bound bound onUp". I see now, when I run the events in succession it keeps adding additional layers of binding like you said. I will checkout the use of the hasOwnProperty method. I don't know if this is efficient, but I think I also found that if I do a delete this.onUp at the same time as the removeEventListener, it also clears up the stacking of bindings, as I guess each time it starts all over.
@AdamSassano ...The points 1 to 5, as so perfectly explained as everything else, is the main reason I dislike this (anti)pattern which was most likely introduced, and if not then at least spread by the react community. In my opinion it confuses the novice developer more than it does help. A lot of FE developers are not capable of explaining (most don't even think about it) what actually is going on at construction time. And there was even a solution to it ...implement the to be bound handler as properly named function with local/module scope; never pollute the prototype with this kind of crap.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.