问题描述
嘿,我开始阅读有关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);
}
未捕获的TypeError:无法读取未定义的属性“ push”
并且当我在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…........
解决方法
问题是this.items.push(product)
试图将产品推入this.items
,但是您从未初始化this.items
,因此其值为undefined
。构造函数仅在创建类的新实例时执行,而对于类本身不执行。
要解决该问题,您必须在items
上定义一个静态ShoppingCart
属性:
class ShoppingCart {
static items = [];
static addProduct(product) {
this.items.push(product);
this.totalOutput = `<h2>Total Amount: ${1}</h2>`;
}
}
,
解决此类问题的基本方法是通过事件将模块或依赖项(在此类模块之间)解耦,以便以最大的转储/不可知论/干净的方式实现每个模块。
对于OP的示例代码,此答案是将document
误用作custom events的事件总线。
用于引导模型数据的事件总线不一定是 DOM 的一部分。应该鼓励自己构建这样的抽象或使用提供这种实用程序/功能的众多可用库之一。
我个人对模块的结构仍然不满意,因为模型和视图相关代码之间的混合仍然太疯狂了。购物车模块。但是 decoupling 是最基本的方法,可以以一种更干净的代码来工作。
但是现在还应该清楚的是...任何摆脱静态addProduct
的方法(...直接固定到ShoppingCart
命名空间的方法都不可能访问通过ShoppingCart
的{{1}}实例并将该字母作为实例方法,可能会更有帮助,因为网页往往呈现的不仅仅是一个代表购物车。
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,handleAddProductToBoundCart.bind(miniCart));