在JS或OOP语言中,通过创建不同的类型来创建多态性。
例如:
class Field {...}
class DropdownField extends Field {
getValue() {
//implementation ....
}
}
想象一下我有一些方法的库form.js:
class Forms {
getFieldsValues() {
let values = [];
for (let f of this.fields) {
values.push(f.getValue());
}
return values;
}
}
这将获取所有字段值。请注意,图书馆不在乎它是哪个字段。
通过这种方式,开发人员A创建了库,而开发人员B可以创建新字段:AutocompleterField。
他可以在AutocompleterField中添加方法,而无需更改库代码(Forms.js)。
如果我在JS中使用函数式编程方法,该如何实现?
如果对象中没有方法,我可以使用case语句,但这违反了原理。与此类似:
if (field.type == 'DropdownField')...
else if (field.type == 'Autocompleter')..
如果开发者B添加新类型,他应该更改库代码。
那么有什么好办法可以解决javascript问题,而无需使用面向对象的编程。
我知道Js既不是OOP也不是FP,但是无论如何。
谢谢
答案 0 :(得分:3)
JavaScript是一种多用途语言,您当然可以用不同的方式解决它。切换到函数式编程时,答案非常简单:使用函数!您的示例的问题是:如此简单,您只需三行就可以完成完全相同的操作:
// getValue :: DOMNode -> String
const getValue = field => field.value;
// readForm :: Array DOMNode -> Array String
const readForm = formFields => formFields.map(getValue);
readForm(Array.from(document.querySelectorAll('input, textarea, select')));
// -> ['Value1', 'Value2', ... 'ValueN']
关键是:Field::getValue()
是如何实现的,返回是什么?或更准确地说:DropdownField::getValue()
与AutocompleteField::getValue()
有何不同,例如与NumberField::getValue()
有何不同?它们都只是返回值吗?他们返回一对名称和值吗?他们甚至需要与众不同吗?
问题在于,您的Field
类及其继承类是否因其getValue()
方法的工作方式而有所不同,还是由于它们具有的其他功能而有所不同?例如,文本字段的“自动完成”功能与(或不应)与从中获取值的方式有关。
如果您确实需要以不同的方式读取值,则可以实现一个函数,该函数采用{fieldtype: readerFunction}
对的map / dictionary / object / POJO:
/* Library code */
// getTextInputValue :: DOMNode -> String
const getTextInputValue = field => field.value;
// getDropdownValue :: DOMNode -> String
const getDropdownValue = field => field.options[field.selectedIndex].value;
// getTextareaValue :: DOMNode -> String
const getTextareaValue = field => field.textContent;
// readFieldsBy :: {String :: (a -> String)} -> DOMNode -> Array String
readFieldsBy = kv => form => Object.keys(kv).reduce((acc, k) => {
return acc.concat(Array.from(form.querySelectorAll(k)).map(kv[k]));
}, []);
/* Code the library consumer writes */
const readMyForm = readFieldsBy({
'input[type="text"]': getTextInputValue,
'select': getDropdownValue,
'textarea': getTextareaValue
});
readMyForm(document.querySelector('#myform'));
// -> ['Value1', 'Value2', ... 'ValueN']
注意:我故意在这里没有提到IO monad之类的东西,因为它会使事情变得更复杂,但是您可能希望查找它。
答案 1 :(得分:2)
在JS或OOP语言中,通过创建不同的类型来创建多态性。
是的。或者,通过在不同对象中实现 same 类型接口。
在没有OOP类的情况下如何使用Javascript多态性
您似乎在confuse class
es with types。您完全不需要JS class
语法来创建对象。
你可以拥有
const autocompleteField = {
getValue() {
…
}
};
const dropdownField = {
getValue() {
…
}
};
,然后在您的Forms
实例中使用两者。
答案 2 :(得分:2)
取决于您所说的“多态性”。在Haskell,Scala或PureScript中提供了所谓的 ad hoc多态性类型类,这种分派通常是通过将见证对象与其他函数参数,它们随后将知道如何执行多态功能。
例如,以下PureScript代码(来自docs)为某些类型提供了show
函数:
class Show a where
show :: a -> String
instance showString :: Show String where
show s = s
instance showBoolean :: Show Boolean where
show true = "true"
show false = "false"
instance showArray :: (Show a) => Show (Array a) where
show xs = "[" <> joinWith ", " (map show xs) <> "]"
example = show [true, false]
它被编译为以下JS(我简称):
var Show = function (show) {
this.show = show;
};
var show = function (dict) {
return dict.show;
};
var showString = new Show(function (s) {
return s;
});
var showBoolean = new Show(function (v) {
if (v) {
return "true";
};
if (!v) {
return "false";
};
throw new Error("Failed pattern match at Main line 12, column 1 - line 12, column 37: " + [ v.constructor.name ]);
});
var showArray = function (dictShow) {
return new Show(function (xs) {
return "[" + (Data_String.joinWith(", ")(Data_Functor.map(Data_Functor.functorArray)(show(dictShow))(xs)) + "]");
});
};
var example = show(showArray(showBoolean))([ true, false ]);
这里绝对没有魔术,只有一些附加参数。在实际上知道具体类型的“顶部”,您必须传递匹配的具体见证对象。
在您的情况下,您会像HasValue
证人那样传递不同的形式。
答案 3 :(得分:1)
您可以使用工厂模式来确保遵循开/关原则。 这项原则说:“软件实体(类,模块,功能等)应该开放以进行扩展,而封闭以进行修改”。
class FieldValueProviderFactory {
getFieldValue(field) {
return this.providers.find(p => p.type === field.type).provider(field);
}
registerProvider(type, provider) {
if(!this.providers) {
this.providers = [];
}
this.providers.push({type:type, provider:provider});
}
}
var provider = new FieldValueProviderFactory();
provider.registerProvider('DropdownField', (field) => [ 1, 2, 3 ]);
provider.registerProvider('Autocompleter', (field) => [ 3, 2, 1 ]);
class FieldCollection {
getFieldsValues() {
this.fields = [ { type:'DropdownField',value:'1' }, { type:'Autocompleter',value:'2' } ];
let values = [];
for (let field of this.fields) {
values.push(provider.getFieldValue(field));
}
return values;
}
}
现在,当您要注册新的字段类型时,可以在工厂为其注册一个提供程序,而不必修改您的字段代码。
new Field().getFieldsValues();