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



嘿,伙计们,我开始读关于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);
}

所以现在的问题是,我制作了另一个名为";购物车";当我点击按钮时将产品添加到购物车中,这就像

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

但这给了我一个错误,

未捕获类型错误:无法读取未定义的属性"push">

以及当i控制台";这个";在addProduct中,我看到一些奇怪的输出

> 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。构造函数只在创建类的新实例时执行,而不是为类本身执行。

为了解决这个问题,您必须在ShoppingCart:上定义一个静态items属性

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

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

对于OP的示例代码,这个答案以某种方式将document滥用为自定义事件的事件总线

用于引导模型数据的事件总线不一定需要是DOM的一部分。应该鼓励人们自己构建这样的抽象,或者使用提供这种实用性/功能的许多可用库中的一个。

就我个人而言,我仍然对模块的结构不满意,因为在购物车模块中,模型和视图相关的代码仍然混合得太多。但是去耦是实现更干净代码的基础。

但现在应该清楚的是。。。任何摆脱静态addProduct(…直接固定到ShoppingCart命名空间的方法都没有机会通过this访问ShoppingCart实例…(并使字母成为实例方法的方法都可能更有帮助,因为网页往往一次只呈现了一个以上的视图来表示购物车

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

最新更新