简体   繁体   中英

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

Hey guys i started reading about Javascript ES6 classes so was trying out something

Here is my ProductItem class which render each product

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

In another class we loop and instantiate this class like

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

So now the problem is that i made another class called "ShoppingCart" to add products to cart when i click on button so thats like

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


}

as i read static method can be called without Instantiating class so what i did was when i click on button in my "ProductItem" class i called a fn() and then u can see in that function what i did was

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

But this gives me error that

Uncaught TypeError: Cannot read property 'push' of undefined

and also when i console "this" in addProduct i see something weird as output

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

The problem is that this.items.push(product) tries to push a product into this.items , however you never initialize this.items so its value is undefined . The constructor is only executed when you create a new instances of a class, but not for the class itself.

To solve the problem you have to define a static items property on ShoppingCart :

class ShoppingCart {
  static items = [];

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

The base approach for this kind of problem is decoupling modules or dependencies (across such modules) via events in order to implement each module in the most dump/agnostic/clean possible way.

For the OP's example code this answer is somehow misusing the document as event bus for custom events .

An event bus for channeling model data does not necessarily need to be part of the DOM . One should feel encouraged to build such an abstraction oneself or to make use of one of the many available libraries which provide such utility/functionality.

Personally I was still not pleased with how the modules are structured for there is still a too wild mix of model and view related code within eg the shopping cart module. But the decoupling is the very base one can work ones way towards even cleaner code.

But what also should be clear by now ... any approach that gets rid of the static addProduct (... a method pinned directly to the ShoppingCart namespace has no chance of ever accessing a ShoppingCart instance via this ...) and makes the letter an instance method, might be of more help, since webpages tend to have rendered more than just one view representative of a shopping cart at the time.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM