简体   繁体   中英

'this' is misbehaving (again) inside an ES6 method

Just getting started with ES6 classes. As far as I understood it, this was supposed to behave predictably inside a class and always point to the object. However, that doesn't seem to be the case:

class BodyPixController {

    #target; //Declare a private property

    constructor(target){
        this.#target = target; // Set the property

        console.log(this); // logs: BodyPixController {#target: 'photo'}

        addEventListener('load', this.init); // Calls the method
    }

    init() {
        console.log(this); // logs: Window {window: Window, self: Window, document: document, name: '', location: Location,…}
        const img = document.getElementById(this.#target); // Throws error: Cannot read private member #target from an object whose class did not declare it
        console.log(img);
        async function loadAndPredict() {
            const net = await bodyPix.load( /** optional arguments, see below **/ );

            const segmentation = await net.segmentPerson(img);
            console.log(segmentation);
        }
        loadAndPredict();
    }
}

In the code above, this only points to the instantiated object inside the constructor. As soon as the init() method is called, this points to Window. Why? And how do I access private properties from inside methods?

this inside event listeners refer to the object that the listener is attached to in case of the load event the Window .

Either pass event listener as arrow function as they have no scope or bind this to your function in constructor like:

constructor() {
  // bind this inside init to current object
  this.init = this.init.bind(this);

  addEventListener('load', this.init);
}

"as far as I understood it", it's all in the docs .

Classes arent special, just syntactic sugar, so when you pass that function to the event listener, it wont be calling "yourObj.func()", just "func()" (for simplicity), it has no reference to your object.

You're thinking of ES6 functions, which are special. Change init() { to init = () => { and it'll behave how you want; this will always refer to your instantiated class object (not even .apply() can override).

Perhaps you can work with this. I created a class and added an event handler; then created an instance of the class and triggered one of the events.

So here we have an event target e.target and this as things to work with.

 class BodyPixController { constructor(target) { this.target = target; console.log(this); this.target.addEventListener('load', this); } boundLoad = () => this.loadEvent() boundInit = this.init.bind(this) init(e) { console.log(e); const img = document.getElementById(e.target); console.log(img); async function loadAndPredict() { const.net = await bodyPix.load( /** optional arguments, see below **/ ); const segmentation = await.net.segmentPerson(img); console.log(segmentation); } loadAndPredict(); } loadEvent(e){ // Some action related to the event (e) console.log(e.target); console.log("loadEvent:",this); console.log("What:",e.target.innerText); } handleEvent(e) { switch (e.type) { case "load": this.loadEvent(e); break; case "init": this.init(e); break; } } } var target = document.querySelector(".fun-target"); var myInstance = new BodyPixController(target); target.dispatchEvent(new CustomEvent('load'), {})
 <div class="fun-target">howdy</div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM