在es2015中,如果我有基类来表示看起来像这样的List
:
class List {
constructor(data){
this.data = data
}
sortBy(attribute){
return this.data.sort((a,b) => {
return (a[attribute] < b[attribute]) ? 1 : -1;
})
}
get count() { return this.data.length }
}
然后我可能想要用不太通用的数据子类化该基类,即,如果我是一个精灵,玩具:
class ToyList extends List {
constructor(toys){
super(toys);
this.toys = toys;
}
}
此时ToyList
与List
没有区别,但名称除外。但是,如果您查看ToyList
的实例,则它具有 data
和toys
属性。这些引用相同的数组,在概念化ToyList
的点时,data
没有多大意义。
如果我制作ToyList
,我同时拥有.data
和.toys
属性:
tl = new ToyList(['truck', 'plane', 'doll'])
Object { data: Array[3], toys: Array[3] }
然后我的tl
同时拥有data
和toys
属性。它们都是对同一个数组的引用,但我想要的是仅的子类具有toys
引用。
这是另一个在基类上使用方法的例子:
class Todos extends List {
constructor(todos){
super(todos);
this.todos = todos;
}
get byPriority(){
return this.todos.sortBy('priority')
}
}
var thingsToDo = [
{task: 'wash the dog', priority: 10},
{task: 'do taxes', priority: 1},
{task: 'clean the garage', priority: 0}
]
var todos = new Todos(thingsToDo);
todos.byPriority
这样会很好,因为我可以引用.byPriority
来获取列表的排序版本,该版本非常特定于这种特定类型的数据。但我无法看到如何实现这一点,因为
但我得到的是:
TypeError: this.todos.sortBy is not a function
总而言之,我想要的是一种使用特定于子类语义的名称来引用基类属性的方法,而不会丢失基类的方法。
答案 0 :(得分:2)
在评论中引用我们的不满,更好的实施(imo),可扩展并避免您询问的问题
var AP = Array.prototype; //just lazy
class List {
constructor(elements){
for(var i = 0, j = (elements && elements.length)|0; i<j; ++i)
this[i] = elements[i];
//use length instead of count, stay compatible with the Array-methods
//will make your life easier
this.length = i;
}
length: 0
sortBy(attr){
return this.sort(function(a,b){
return (a[attribute] < b[attribute]) ? 1 : -1
});
}
//some functions have to be wrapped, to produce a List of the right type
filter(fn){
return new (this.constructor)(AP.filter.call(this, fn));
}
clone(){ return new (this.constructor)(this) }
}
//some functions can simply be copied from Array
//no need to re-implement or even wrap them.
List.prototype.sort = AP.sort;
List.prototype.push = AP.push;
List.prototype.pop = AP.pop;
子类
class ToyList extends List {
constructor(toys){
//maybe you want to filter the input, before you pass it to the list
//or convert it, or whatever, it's all up to you
super(toys && AP.filter.call(toys, v=>v instanceof Toy));
}
//... additional functionality
}
和示例用法
class Toy {
constructor(name){
this.name = name;
}
}
var a = new ToyList([
new Toy("foo"),
new Toy("bar"),
"not a toy",
new Toy("baz")
])
console.log(a instanceof ToyList, a);
var b = a.filter(toy => toy.name.charAt(0) === "b");
console.log(b instanceof ToyList, b);
编辑:使用Todos
添加了您的示例class Todos extends List {
//don't even need a constructor, since I simply want to pass
//the array to the parent-constructor
//don't use getter for functionality, use methods!
byPriority(){
return this.sortBy('priority');
}
}
var thingsToDo = [
{task: 'wash the dog', priority: 10},
{task: 'do taxes', priority: 1},
{task: 'clean the garage', priority: 0}
]
var todos = new Todos(thingsToDo);
todos.byPriority()
答案 1 :(得分:2)
ToyList
同时拥有data
和toys
属性。这些引用相同的数组,在概念化ToyList
的点时,data
没有多大意义。
您的实际问题是:ToyList
作为List
的子类有意义。
如果(出于任何原因)您的课程应该与List
类似,但没有data
属性,那么它不再是一个子类 。它会违反Liskov substitution principle。
现在你有什么选择?
.toys
属性是.data
的别名。这很好,但你也不能避免在那里拥有data
财产。data
属性并将元素直接存储在对象上。您的List
类看起来像#34;数组,但有有用的辅助函数&#34;。如果这是您的意图,您应该考虑将其作为Array
的实际子类。 @ Thomas的答案就是那个方向。List
个实例在Array
属性中包含data
个。如果你有Wishlist
或Toylist
或其他任何专门用于处理眼镜或玩具的方法,并且有相应的方法,你只需在他们的List
中存储.toys
个实例插槽。TodoList
的调用(其中this.todos.sortBy('priority')
将是this.todos
),您实际上似乎希望List
能够像这样工作。在子类上,只需要this.sortBy('priority')
即可完成工作。ToyList
是List
的专长。如果除了名字之外没有什么特别之处,也许你真的不需要一个完全不同的课程。如果JavaScript具有泛型或类型变量,则您使用List<Toy>
,但它不会直接使用List
。答案 2 :(得分:2)
我认为你有很多不同的问题。
您的列表对sortBy
的定义有疑问,您需要考虑3个案例,如下:
class List {
constructor(data){ this.data = data; }
sortBy(attribute){
console.log("sortBy");
return this.data.sort( (a,b) => {
if (a[attribute] < b[attribute]) return -1;
if (a[attribute] > b[attribute]) return 1;
return 0;
});
}
get count() { return this.data.length; }
}
现在您可以扩展List
,如果您想将data
命名为toys
,请定义名为get
的{{1}}方法以返回数据。它可能很奇怪,但是如果你继承toys()
那么你应该使用List
(如果没有,不要将它子类化)。还有一种方法:您可以删除data
属性,然后创建data
但是唉,在toys
中设计sortBy
方法会很困难(或使用第二个参数来命名要排序的数组?)。所以,让我们使用第一个建议:
List
为class ToyList extends List {
constructor(toys){ super(toys); }
get toys() { return this.data; }
}
:
Todos
class Todos extends List {
constructor(todos){ super(todos); }
get todos() { return data; }
get byPriority(){
return this.sortBy('priority');
}
}
的定义有点奇怪,因为它有边框效应(排序元素)。我(个人)会把它写成标准方法。
然后让我们进行一些测试:
byPriority
我建议您多阅读一下有关OOP和继承的内容吗?需要子类但删除数据属性当然是一个糟糕的设计。
答案 3 :(得分:0)
IMO,如果可能的话,更好的方法是将List类视为纯虚拟,意味着你永远不会创建该类的实例,而只是从它扩展。
纯虚拟类不应该具有构造函数,并且方法是假设存在某些属性。但是,您可以使用构造函数来设置基类应该用于“data”属性的名称。
class List {
constructor(keyName) { this.keyName = keyName }
sortBy(attr) { return this[this.keyName].sort(...) }
}
class ToyList extends List {
constructor('toys') {
super(toys)
this.toys = toys;
}
}