В целом, вы, похоже, просто запутались в в контексте и ссылках:
name.filter(product => name === undefined || product.name === this.name);
// в этом месте вы пытаетесь отфильтровать переданную опцию name, которая является строкой "anything 4",
// так же контекст this.name - будет undefined, потому что в данном случае вы передаете стрелочную функцию,
// this которой будет ссылаться на объект, в котором она была создана, т.е. - на Shop. У Shop нет свойства name
// правильный вариант:
const filtered = this.products.filter(product => {
return product.name === undefined || product.name === name
});
Все последующие функции вам нужно отредактировать по тому же принципу.
PS.
Не совсем понимаю, зачем вам парсить строчные операторы сравнения, когда можно просто отправлять функцию сравнения в метод фильтрации.
class Shop {
constructor(products) {
this.products = [];
}
addProduct(newProduct) {
this.products.push(newProduct);
}
filterProduct(fn) {
return this.products.filter(fn);
}
}
const shop = new Shop();
shop.addProduct(new Product("product 1", 1, 2000));
shop.addProduct(new Product("item 2", 2, 100));
shop.addProduct(new Product("some 3", 3, 500));
shop.addProduct(new Product("anything 4", 4, 1000));
const filtered = shop.filterProduct((product) => product.name === 'anything 4' && product.count > 3 && product.price >= 500);
// console.log(filtered): [ Product { name: 'anything 4', count: 4, price: 1000 } ]
На самом деле, есть еще много мелких моментов:
1) С данными лучше работать функционально и не допускать добавления, удаление и проч. операции в текущем объекте, т.е. объект должен быть иммутабельным. При добавлении товара нужно создавать новый объект Shop.
2) Вытекает из первого - зачем вам в конструкторе Shop сигнатура products, когда вы ее не используете? По сути, ее должно использовать именно для сохранения передаваемого массива продуктов.
3) В объектах, работающих с данными, особенно когда идет фильтрация, сортировка и проч операции, лучше сразу имплементировать fluent-interface.