在 CustomElement 中引用 self (this)

Reference to self (this) inside CustomElement

我正在使用此 polyfill 在 JavaScript 中实现自定义元素。然而,self 变量在我的方法中引用了 Window,除非我先调用 const self = this

任何人都可以解释一下为什么对我来说如此,并且也许可以建议一种在方法中访问自定义元素实例的更好方法吗?

class DocumentPreview extends HTMLElement {
  constructor(self, documents) {
    self = super(self);
    self.documents = documents || [];
    self.innerHTML = Handlebars.templates['document_preview']();
  }

  connectedCallback() {
    // if I don't do this first ...
    const self = this;   // <<----------------------------------
    console.log("connected now");
    document.addEventListener('mqttsub', function(event) {
      // ... onMessage is undefined here:
      self.onMessage(event.detail);
    });
  }

  disconnectedCallback() {
    console.log("disconnected");
  }

  onMessage(message) {
    // Same story ...
    const self = this;   // <<----------------------------------
    Promise.resolve(true)
    .then(json("/documents/"))
    .then(ds => ds
      .filter(x => x.name==message.payload)
      .reduce((x, y) => y, undefined)
    )
    .then(d => json(sprintf("/document/%d/", d.id))())
    // ... here:
    .then(d => self.renderDocuments(d))
    .catch(console.log);
  }

  renderDocuments(d) {
    console.log('render', d);
  }
}

在JavaScript中的this关键字,指的是当前函数的上下文。

document.addEventListener('mqttsub', function(event) {
  // this here corresponds to your function(event) {...} context
  this.onMessage(event.detail); // Will fail
});

解决这个问题的一个简单方法是使用箭头函数。箭头函数默认使用外部上下文。

document.addEventListener('mqttsub', (event) => {
  // this here corresponds to the outer context = your class
  this.onMessage(event.detail);
});

否则你也可以绑定上下文

document.addEventListener('mqttsub', (function(event) {
  this.onMessage(event.detail);
}).bind(this)); // Set the function context to be the class context

尝试使用 bind() 在构造函数中绑定方法 onMessage()renderDocuments(),如 this.methodName = this.methodName.bind(this) 中那样。有了这个,您将能够通过 this.

访问属性和方法
class DocumentPreview extends HTMLElement {
  constructor(documents) {
    super();

    this.documents = documents || [];
    this.innerHTML = Handlebars.templates['document_preview']();

    this.onMessage = this.onMessage.bind(this);
    this.renderDocuments = this.renderDocuments.bind(this);
  }

  connectedCallback() {
    document.addEventListener('mqttsub', this.onMessage);
  }

  disconnectedCallback() {
    console.log("disconnected");
  }

  onMessage(event) {
    const { detail: message } = event;

    Promise.resolve(true)
      .then(json("/documents/"))
      .then(ds => ds
        .filter(x => x.name==message.payload)
        .reduce((x, y) => y, undefined)
      )
      .then(d => json(sprintf("/document/%d/", d.id))())
      // ... here:
      .then(d => this.renderDocuments(d))
      .catch(console.log);
  }

  renderDocuments(d) {
    console.log('render', d);
  }
}

希望对您有所帮助!