【问题标题】:Access a Class property within the static method by "this" keyword通过“this”关键字访问静态方法中的类属性
【发布时间】:2021-02-05 13:22:29
【问题描述】:

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

这是渲染每个产品的 ProductItem 类

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

在另一个类中,我们循环并实例化这个类

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

所以现在的问题是我创建了另一个名为“ShoppingCart”的类,以便在我点击按钮时将产品添加到购物车中

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: ${1}</h2>`;
  }


}

当我阅读静态方法时,可以在没有实例化类的情况下调用 所以我所做的是当我单击“ProductItem”类中的按钮时 我调用了一个 fn() 然后你可以在那个函数中看到我所做的是

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

但这给了我一个错误

未捕获的类型错误:无法读取未定义的属性“推送”

当我在 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…........

【问题讨论】:

  • 一个产品项必须完全不知道它将被添加到哪里。因此原型addToCart 方法没有用,甚至会产生误导。人们需要找到另一种建模方法。我开始考虑...
  • 是的,这种方法不好,因为我正在学习一个教程,而他只是在上这么多课来解释更多的概念……你能告诉我什么是最好的方法吗??
  • 还有什么问题吗?
  • @PeterSeliger no mate 非常感谢您的详细解释

标签: javascript events binding es6-class decoupling


【解决方案1】:

问题在于this.items.push(product) 试图将产品推送到this.items,但是您从未初始化this.items,因此它的值为undefined。构造函数仅在您创建类的新实例时执行,而不是为类本身执行。

要解决这个问题,你必须在ShoppingCart 上定义一个静态的items 属性:

class ShoppingCart {
  static items = [];

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

【讨论】:

  • 在这种情况下,ShoppingCart 只是降级为命名空间,因此根本不需要 class。任何摆脱静态 addProduct 并使字母成为实例方法的方法都可能更有帮助,因为当时网页往往呈现的不仅仅是一个代表购物车的视图。
  • @PeterSeliger 是的 ShoppingCart 在这一点上基本上是一个命名空间。我不是想在这里写出最好的解决方案。我试图指出 OP 的代码出了什么问题,以便他们可以从错误中吸取教训。
【解决方案2】:

此类问题的基本方法是通过事件解耦模块或依赖项(跨此类模块),以便以最可能的转储/不可知/干净的方式实现每个模块。

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

用于通道模型数据的事件总线不一定需要是DOM的一部分。应该鼓励人们自己构建这样的抽象,或者使用提供此类实用程序/功能的众多可用库之一。

就我个人而言,我仍然对模块的结构不满意,因为模型和视图相关代码的组合仍然过于疯狂,例如购物车模块。但是解耦是实现更简洁代码的最基础的方法。

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

// 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;
}

【讨论】:

    猜你喜欢
    • 2012-03-30
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    • 2016-01-28
    • 1970-01-01
    • 2014-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多