【问题标题】:How does one provide or bind a correct this context to an event handler in JavaScript?如何在 JavaScript 中提供或绑定正确的 this 上下文到事件处理程序?
【发布时间】:2020-10-25 03:30:29
【问题描述】:

据我所知,类的私有字段/函数的建议是 ES2019 附带的,带有 # 语法。

例如,我有以下课程:

class MyClass {
    #firstPrivateMethod(parameter) {
        // method functionality.
    }

    #secondPrivateMethod(parameter) {
        var someValueFromTheOtherMethod = this.#firstPrivateMethod(parameter);
    }

    constructor() {
        var firstPrivateMethod = this.#firstPrivateMethod;
        var secondPrivateMethod = this.#secondPrivateMethod;

        window.onresize = function() {
            this.#firstPrivateMethod = firstPrivateMethod; // <-- error: Cannot write private member #firstPrivateMethod to an object whose class did not declare it.
            secondPrivateMethod(parameter);
        }
    }
}

我想暂时将我的类的私有方法和字段传递给事件处理程序的 this 对象,然后我会在完成某个基于类的任务后删除这些事件处理程序或将它们设置为 null。

我需要这个的原因是我的类将元素附加到 DOM 并且这些元素的样式是根据它的依赖关系计算的。我认为让它对媒体查询做出响应是非常困难或不可能的。我有一些针对不同设备的断点。在每个断点之后,我都会删除附加的元素,然后我会更改依赖项的值并使用新样式重新附加它们。

起初我有一个想法。我试图像这样修改我的代码:

class MyClass {
    #firstPrivateMethod(parameter) {
        // method funcionality.
    }

    #secondPrivateMethod(firstPrivateMethod, parameter) {
        var someValueFromTheOtherMethod = firstPrivateMethod;
    }

    constructor() {
        var firstPrivateMethod = this.#firstPrivateMethod;
        var secondPrivateMethod = this.#secondPrivateMethod;

        window.onresize = function() {
            secondPrivateMethod(firstPrivateMethod, parameter);
        }
    }
}

但它让我无处可去,因为我的代码变得非常混乱以至于很难阅读。

你知道如何解决这个问题吗?

【问题讨论】:

  • 第二种方法看起来已经比第一种好得多了...请提供有关每个方法/函数需要在其中运行的上下文的更多信息...谈论事件处理程序并可能对firstPrivateMethodsecondPrivateMethod 有更多见解
  • 我正在尝试创建一个 js 库。这将是一个用于导航目的的轮子。我有一个 init() 函数创建轮子的“骨架”,然后根据预设或给定的选项在轮子上创建节点。有两种类型的节点:主菜单或子菜单的另一个轮子。它工作正常,但我想在 onresize 事件的帮助下使其响应。目前我正在尝试在事件处理程序中重新运行 init() 函数,但我的 init() 函数在很大程度上取决于我的私有方法。
  • 这个问题是关于私有方法的吗?你是说如果方法是公开的,代码会起作用吗?我真的不知道你想在这里做什么。
  • ... “但是我的 init() 函数在很大程度上取决于我的私有方法。” ...这是需要更多见解的地方,您的 private 方法构建?.. 它们在哪个上下文中运行,它们覆盖哪个范围?可能是您的基本结构需要不同的方法。
  • “你是说如果方法是公开的,代码会起作用吗?” - 是的。这样它会起作用,但我会失去他们是私人的全部意义。

标签: javascript binding event-handling this


【解决方案1】:

第一个提供的代码尝试预测 OP 可能真正的痛苦来源并提出一个解决方案,该解决方案证明,对于 OP 的给定示例,甚至可能不需要基于私有实例字段的方法/方法。

猜测是基于 OP 确实提供的错误消息...

error: Cannot write private member #firstPrivateMethod to an object whose class did not declare it.

...受影响的代码(部分)看起来像...

constructor() {
    var firstPrivateMethod = this.#firstPrivateMethod;
    // ...

    window.onresize = function() {
        this.#firstPrivateMethod = firstPrivateMethod; // <-- error: Cannot write private member #firstPrivateMethod to an object whose class did not declare it.
        // ...
    }
}

这个错误很可能是由于处理函数的 this 上下文不合适而引发的。对于给定的示例,调用时调整大小处理程序的this 上下文是window 对象本身。但是私有字段值只能通过该字段被保护绑定到的实例的直接或原型方法访问。

因此,关键方法应该是为调整大小处理程序提供正确的this 上下文,这可以通过Function.prototype.bind 轻松完成。

当然,下一个例子完全没有抓住重点的可能性很高……不过,我们开始吧……

// suggest a module scope for the `MyType` class

function doSomethingElseWithType(type, ...args) {
  console.log('doSomethingElseWithType :: type, args : ', type, args);
  // ... even more computation.
}

function doSomethingWithType(type, ...args) {
  console.log('doSomethingWithType :: type, args : ', type, args);

  // ... more computation.

  doSomethingElseWithType(type, ...args);
}


function handleWindowResizeWithBoundType(evt) {
  const type = this;

  doSomethingWithType(type, evt.type, ...Object.keys(evt));
}


/*default export */class MyType {
  constructor(label) {
    this.label = label;

    window.onresize = handleWindowResizeWithBoundType.bind(this);
  }
}


const type = new MyType('test');

// NOTE: please squeeze the window vertically but be careful
//       about the frequency of this action.
.as-console-wrapper { min-height: 100%!important; top: 0; }

如果第一种方法没有完全忽略这一点,可以提出另一种方法,这次必须使用私有实例字段,以便提供删除私有/保留的调整大小处理程序的机会...

// suggest a module scope for the `MyType` class

function doSomethingElseWithType(type, ...args) {
  console.log('doSomethingElseWithType :: type, args : ', type, args);
  // ... even more computation.
}

function doSomethingWithType(type, ...args) {
  console.log('doSomethingWithType :: type, args : ', type, args);

  // ... more computation.

  doSomethingElseWithType(type, ...args);
}


function handleWindowResizeWithBoundType(evt) {
  const type = this;

  doSomethingWithType(type, evt.type, ...Object.keys(evt));
}


/*default export */class MyType {

  #resizeHandler;

  constructor(label) {
    this.label = label;

    this.#resizeHandler = handleWindowResizeWithBoundType.bind(this);

    window.addEventListener('resize', this.#resizeHandler);
  }
  unlisten() {
    window.removeEventListener('resize', this.#resizeHandler);
  }
}


const type = new MyType('another test');

setTimeout(type.unlisten.bind(type), 7000);

// NOTE: please squeeze the window vertically.
.as-console-wrapper { min-height: 100%!important; top: 0; }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-26
    • 2011-07-26
    • 1970-01-01
    • 2017-02-20
    • 1970-01-01
    • 2012-02-11
    • 2019-04-15
    相关资源
    最近更新 更多