通过 "this" 关键字在静态方法中访问 Class 属性

Access a Class property within the static method by "this" keyword

大家好,我开始阅读有关 Javascript ES6 classes 的文章,所以正在尝试一些东西

这是我的 ProductItem class,它呈现每个产品

class ProductItem {
  constructor(product) {
    this.product = product;
  }
  addToCart() {
    console.log("adding product to cart", this.product);
    ShoppingCart.addProduct(this.product);
  }
  render() {
    const prodEl = document.createElement("li");
    prodEl.className = "product-item";
    prodEl.innerHTML = `
      <div>
        <img src="${this.product.imageUrl}" alt="${this.product.title}" >
        <div class="product-item__content">
          <h2>${this.product.title}</h2>
          <h3>$${this.product.price}</h3>
          <p>${this.product.description}</p>
          <button>Add to Cart</button>
        </div>
      </div>
    `;
    const addCardButton = prodEl.querySelector("button");
    addCardButton.addEventListener("click", this.addToCart.bind(this));
    return prodEl;
  }
}

在另一个 class 中,我们循环并实例化这个 class 就像

for (const prod of this.products) {
      const productItem = new ProductItem(prod);
      const prodEl = productItem.render();
      prodList.append(prodEl);
    }

所以现在的问题是我制作了另一个名为“ShoppingCart”的 class 来在我点击按钮时将产品添加到购物车,就像

class ShoppingCart {
  constructor(items){
    console.log(this)
    this.items=items
  }
  static addProduct(product) {
    console.log(this);
    this.items.push(product);
    this.totalOutput = `<h2>Total Amount: </h2>`;
  }


}

据我所知,无需实例化即可调用静态方法 class 所以我所做的是当我点击“ProductItem”中的按钮时 class 我调用了一个 fn() 然后你可以在那个函数中看到我做了什么

addToCart() {
    console.log("adding product to cart", this.product);
    ShoppingCart.addProduct(this.product);
  }

但这给了我错误

Uncaught TypeError: Cannot read property 'push' of undefined

而且当我在 addProduct 中控制“this”时,我看到一些奇怪的输出

> class ShoppingCart {   constructor(items){
>     console.log(this)
>     this.items=items   }   static addProduct(product) {
>     console.log(this);
>     this.items.push(product);
>     this.totalOutput = `<h2>Tot…........

解决此类问题的基本方法是 通过事件 解耦模块或依赖项(跨此类模块),以便尽可能 dump/agnostic/clean 实现每个模块方式。

对于 OP 的示例代码,此答案以某种方式滥用 document 作为 事件总线 用于 custom events

用于传输模型数据的事件总线不一定需要是DOM的一部分。应该鼓励人们自己构建这样的抽象,或者使用提供这种抽象的众多可用库之一 utility/functionality.

就我个人而言,我仍然对模块的结构方式不满意,因为模型和视图相关代码的混合仍然过于疯狂。购物车模块。但是 解耦 是一个非常基础的方法,可以使代码更清晰。

但是现在还应该清楚的是......任何摆脱静态 addProduct 的方法(......直接固定到 ShoppingCart 命名空间的方法永远没有机会通过 this 访问 ShoppingCart 实例 ...) 并使字母成为实例方法,可能会有更多帮助,因为网页往往呈现的不仅仅是 [=34= 的一个视图代表]购物车当时。

// e.g. module ... view/ShoppingCart.js

/*export */function handleAddProductToBoundCart(evt) {
  const shoppingCart = this;
  shoppingCart.addProduct(evt.detail.product);
}

/*export */class ShoppingCart {
  constructor(elmRoot, items) {
    this.elmRoot = elmRoot;
    this.items = items;
    this.renderItemCount();
  }
  renderItemCount() {
    this.elmRoot.innerHTML = `<h2>Total Amount: ${ this.items.length }</h2>`;
  }
  addProduct(product) {
    this.items.push(product);
    this.renderItemCount();

    console.log('ShoppingCart :: addProduct :: product :', product);
  }
  
}
// -----


// e.g. module ... view/ProductItem.js

function handleAddBoundProductItem() {
  const productItem = this;
  const customEvent = new CustomEvent("addtocart", { detail: { product: productItem } });
  document.dispatchEvent(customEvent);
}

/*export */class ProductItem { 
  constructor(product) {
    this.product = product;
  }
  render() {
    const prodEl = document.createElement("li");
    prodEl.className = "product-item";
    prodEl.innerHTML = `
      <div>
        <img src="${this.product.imageUrl}" alt="${this.product.title}" >
        <div class="product-item__content">
          <h2>${this.product.title}</h2>
          <h3>$${this.product.price}</h3>
          <p>${this.product.description}</p>
          <button>Add to Cart</button>
        </div>
      </div>
    `;
    const addCardButton = prodEl.querySelector("button");
    addCardButton.addEventListener("click", handleAddBoundProductItem.bind(this));
    return prodEl;
  }
}
// -----


// some product list model data from wherever it came from ...

const productList = [{
  imageUrl: '',
  title: 'Product A',
  price: '12.30 Currency',
  description: 'best whatsoever'
}, {
  imageUrl: '',
  title: 'Product B',
  price: '450.00 Currency',
  description: 'most expensive'
}];
// -----


// render functionality from yet another view module ...

// ... import statements ...

const cartView = document.createElement('div');
document.body.appendChild(cartView);

const shoppingCart = new ShoppingCart(cartView, []);
document.addEventListener('addtocart', handleAddProductToBoundCart.bind(shoppingCart));

// ...
// ...

const prodListView = document.createElement('ul');
document.body.appendChild(prodListView)

//for (const prod of this.products) {
for (const product of productList) {

  const productItem = new ProductItem(product);
  const prodEl = productItem.render();

  // prodList.append(prodEl);

  prodListView.append(prodEl);
}

// additional cart, just in order to demonstrate the appoache's capabilities ...

const miniCartView = document.createElement('div');
document.body.appendChild(miniCartView);

const miniCart = new ShoppingCart(miniCartView, []);
document.addEventListener('addtocart', handleAddProductToBoundCart.bind(miniCart));
.as-console-wrapper {
  min-height: 100%!important;
  width: 50%;
  top: 0;
  left: auto!important;
  bottom: auto!important;
}


h2, h3, p, ul, button {
  font-size: .85em;
  margin: 1px 0;
}
h2 {
  font-size: .7em;
}
li {
  margin-bottom: 3px;
}

问题是 this.items.push(product) 试图将产品推入 this.items,但是您从未初始化 this.items,所以它的值为 undefined。构造函数仅在您创建 class 的新实例时执行,但不会为 class 本身执行。

要解决此问题,您必须在 ShoppingCart:

上定义静态 items 属性
class ShoppingCart {
  static items = [];

  static addProduct(product) {
    this.items.push(product);
    this.totalOutput = `<h2>Total Amount: </h2>`;
  }
}