You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ES6类方法中this指向异常及私有属性访问问题

ES6类中this指向异常与私有属性访问问题

刚接触ES6类,原本以为类中的this指向应该可预测且始终指向实例对象,但实际情况并非如此:

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();
    }
}

上述代码中,this仅在构造函数内指向实例对象,调用init()方法时this指向Window,请问该现象的原因是什么?如何在类方法内正确访问私有属性?


原因分析

JavaScript中函数的this指向完全由调用方式决定,和定义位置无关。当你把this.init作为回调传给addEventListener时,本质是单独传递了这个函数的引用——事件触发时,浏览器会直接调用该函数,此时函数的执行上下文是触发事件的对象(这里是window,因为load事件默认绑定在全局window对象上),所以this自然指向window,而非类实例。私有属性#target属于类实例,window没有这个私有成员,因此报错。

解决方法

三种常用方式可确保类方法里的this始终指向实例,从而正常访问私有属性:

1. 在构造函数中用bind绑定this

修改构造函数,把init方法的this强制绑定到类实例:

constructor(target){
    this.#target = target;
    console.log(this);
    // 绑定this到实例
    this.init = this.init.bind(this);
    addEventListener('load', this.init);
}

bind会返回一个新函数,这个函数的this永久绑定为指定实例,不管后续调用方式如何。

2. 用箭头函数作为事件回调

箭头函数的this继承自外层作用域的this,构造函数里的this就是类实例,用箭头函数包裹调用即可:

constructor(target){
    this.#target = target;
    console.log(this);
    addEventListener('load', () => this.init());
}

事件触发时,箭头函数里的this是构造函数的实例,调用this.init()时,init内的this自然也指向实例。

3. 将init定义为类字段箭头函数

直接把init方法定义成类字段形式的箭头函数,箭头函数的this会固定指向类实例:

class BodyPixController {
    #target;

    constructor(target){
        this.#target = target;
        console.log(this);
        addEventListener('load', this.init);
    }

    // 用箭头函数定义方法
    init = () => {
        console.log(this); // 此时this指向类实例
        const img = document.getElementById(this.#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();
    }
}

这种写法无需额外绑定,更简洁,缺点是每个类实例都会创建一个新的函数副本(普通类方法存在于原型上),但多数场景下该影响可忽略。


内容的提问来源于stack exchange,提问作者wagster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 11:16:12