多态性只适用于JavaScript中的OOP概念吗?我正在尝试在 JavaScript 中使用对象组合与原型/类继承。如果这个概念适用,在对象组合中实现多态性会是什么?
下面是实现多态性的原型继承示例(方法重写,在子对象重新实现render
方法(
function HtmlElement() {
this.click = function() {
console.log('clicked');
}
}
HtmlElement.prototype.focus = function(){
console.log('focued');
}
function HtmlSelectElement(items = []) {
this.items = items;
this.addItem = function(item) {
this.items.push(item);
}
this.removeItem = function(item) {
this.items.splice(this.items.indexOf(item), 1);
}
this.render = function() {
return `
<select>${this.items.map(item => `
<option>${item}</option>`).join('')}
</select>`;
}
}
HtmlSelectElement.prototype = new HtmlElement();
HtmlSelectElement.prototype.constructor = HtmlSelectElement;
function HtmlImageElement(src) {
this.src = src;
this.render = function() {
return `<img src="${this.src}" />`
}
}
HtmlImageElement.prototype = new HtmlElement();
HtmlImageElement.prototype.constructor = HtmlImageElement;
const elements = [
new HtmlSelectElement([1, 2, 3]),
new HtmlImageElement('http://')
];
for (let element of elements)
console.log(element.render());
实际上很难在JavaScript中谈论多态性,因为维基百科将多态性定义为:
多态性[...]是为不同类型的实体提供单个接口[1]或使用单个符号来表示多个不同类型的实体。[2]
由于只有基元类型和"对象",因此很难谈论这些对象的类型。由于原型链和instanceof
运算符,可以说还有其他类型,也可以说javascript有鸭子类型。举个例子:
class User {
constructor() { this.name = "test"; }
}
value | type | instanceof User | "name" in
-----------------------------------------------------------------------------------
new User() | object | true | true
{ name: "test" } | object | false | true
{} | object | false | false
现在哪一个是用户类型?是否有用户类型?没有对错,只有意见。以下是我对此的看法:
多态性仅适用于 JavaScript 中的 OOP 概念吗?
我会说不。范式是"写某些东西的风格",多态性是描述对象关系的一种方式,你可以在所有范式中使用对象。
如果这个概念适用,在对象组合中实现多态性会是什么?
如果我们有两个工厂,假设user
和reader
它们看起来像:
function createUser() {
return { name: "test" };
}
function createReader() {
return { books: ["a book"] };
}
const user = createUser(), reader = createReader();
现在,我们可以通过多种方式获得多态性:
1(行多态性
1a(我们可以通过添加书籍将用户变成读者:
user.books = ["another book"];
现在用户是读者,因为它有书(这是鸭子测试(
1b( 我们可以将用户和读者结合起来:
const readingUser = Object.assign(createUser(), createReader());
2( 子类型
如果我们将构造函数更改为:
function createReader() {
return { ...createUser(), books: ["a book"] };
}
现在每个读者都是用户,我们得到了继承(但没有经典继承(。
3(特设多态性
我们可以声明对用户或读者行为不同的函数:
function doSomething(obj) {
if(obj.name) {
console.log("a user");
}
if(obj.books) {
console.log("a reader");
}
}
doSomething(user); // a user
doSomething(reader) // a reader
doSomething(readingUser); // a user, a reader
4(参数多态性
读者和用户都是对象,因此我们可以将它们"作为对象"处理:
Object.freeze(reader);
Object.freeze(user);