【问题标题】:I'm trying to find an example of polymorphism - one of the 4 pillars of OOP - in JavaScript. Is this one? [closed]我试图在 JavaScript 中找到一个多态性的例子——OOP 的 4 个支柱之一。是这个吗? [关闭]
【发布时间】:2020-12-01 11:01:52
【问题描述】:

class Person {
  constructor(name) {
    this.name = name
  }

  method() {
    console.log(this.name)
  }
}

class Student extends Person {
  constructor(name) {
    super(name)
  }
}

class Teacher extends Person {
  constructor(name) {
    super(name)
  }
}

student = new Student("James");
student.method()

person = new Teacher("Alastair");
person.method()

我理解多态性是指继承的方法会根据其所在的实例执行不同的行为。.method 是一个很好的例子吗?有什么更好的?

【问题讨论】:

  • method 这里不是多态性。它只是继承。
  • 多态,在传统意义上,就是你有多个同名的方法。在这种情况下,只有一个方法。
  • @Taplar 所以如果我必须概括它,我会说:两个子类上的同名方法(父类上也存在同名方法?),它们执行类似的功能,但是略有不同?
  • 这种情况的一个解释示例是,假设您有一个 State 类,其中包含一个对象定义。然后你有一个 XmlStateJsonState 扩展它,两者都有自己的 toString 方法。在这种情况下,两者都可以调用variable.toString(),但结果不同,具体取决于他们所拥有的方法的实现。

标签: javascript


【解决方案1】:

这将是一个多态性。

class Student extends Person {
  constructor(name) {
    super(name)
  }
  method() {
    console.log('Student ' + this.name)
  }
}

class Teacher extends Person {
  constructor(name) {
    super(name)
  }
  method() {
    console.log('Teacher ' + this.name)
  }
}

【讨论】:

  • 所以如果我必须概括它,我会说:两个子类上的同名方法(父类上也存在同名方法?),它们执行相似的功能,但略有不同不一样?
  • 是的,这就是多态性。
【解决方案2】:

JavaScript 信奉对象是对象的实例而不是类(从 Self 派生)的原则。结合动态类型系统,它允许“鸭子类型”,它可以通过(命名)约定为您提供(近似)子类型多态性(相同的方法名称和签名与相关对象中的不同实现)(参见@Hayden S.答案)。

但是,如果不利用 Arguments 对象或扩展运算符 (...args) 并动态询问输入,然后自行调度,您将无法获得临时多态性(在单个对象中具有不同签名的相同方法名称)。例如,将字符串与任何其他类型分派到特定方法:

someObject.method = function(...args)  {
  if (isString(args[0]) {
    this.doSomethingWithString.apply(this, args)
  }
  else 
  {
    this.doSomethingElse.apply(this, args)
  }
} 

【讨论】:

    【解决方案3】:

    在您发布的示例中,method() 方法在 TeacherStudent 类中继承,因此它不是多态的。

    如果您将 method() 添加到 Teacher 和/或 Student 子类,那么您的某些类的行为可能会略有不同,具体取决于它们是实例的类型

    例如

    class Person {
      constructor(name) {
        this.name = name
      }
    
      method() {
        // I've moved the console.log (side effect) for sake of simplicity
        return this.name
      }
    }
    
    // no need to add constructor because is inherited from Person
    // nor method, because it inherits method too
    class Student extends Person {}
    
    class Teacher extends Person {
       // inherits constructor...
    
      // override method decorating it
      method() {
           return super.method() + ", is a teacher"
      }
    }
    
    class Employee extends Person {
       static count = 0;
       
       // override constructor necessarily decorating it
       constructor(name){
          super(name)
          this.id = ++this.constructor.count;
       }
    
       // override method
       method(){
           return "employee " +this.id
       }
    }
    Employee.count = 0
    
    // fill a list of different types of Person
    var people = [
       new Student("James"),
       new Teacher("Alastair"),
       new Employee("Bob"),
    ]
    
    // print all the instances
    people.forEach(x => console.log(x.method()))
    

    您还必须通过层次结构维护类的兼容性(相同或更广泛的参数集,相同或更明确定义的返回类型),这被称为Liskov substitution principle

    IMO JS 和 ES6 不是学习传统 OOP 原则的最佳语言,因为它们都具有基于原型的继承和弱类型系统。

    在JS中很容易compose不同的行为(方法)当你创建实例,然后使用多态而不继承,一个简单的方法可以是下面的例子

    class Person {
       constructor({name, ...tail}){
          if(!name){
             throw new TypeError("person.name is missing")
          }
          Object.assign(this, {name, ...tail})
       }
       method(){
          return this.name
       }
       greet(){ return "hi"}
    }
    
    class Robot {
       constructor(...args){
          Object.assign(this, ...args)
          if(this.constructor.prototype !== Object.getPrototypeOf(this)) {
              // throw if __proto__ pollution on the instance
              throw new TypeError("override __proto__")
          }
    
       }
        method(){
            return `a robot that says ${
                Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString("2")
            }`
        }
    }
    
    // the following code might seem unclear but in return
    // it could be used with instances of different classes
    function introduce(){
       return this.greet() +
              ", I'm " +
              this.constructor.prototype.method.apply(this, arguments)
    }
    
    
    
    // fill a list of different instances of Person
    var people = [
       new Person({name: "James"}),
       new Person({
         name: "Alastair",
         method: introduce,
       }),
       new Person({
         name: "Bob",
         method: introduce,
         greet: () => "hola"
       }),
       new Robot({
         method: introduce,
         greet: () => "hello"
       })
    ]
    
    // print all the instances
    people.forEach(x => console.log(x.method()))
    

    归根结底,多态性是指您有不同的类共享方面但不共享行为,因此它们具有具有相同名称的方法,可能具有相同的参数,可能具有相同的返回类型,但具有不同的实现,并且可以互换使用。

    让我们拿枪、钻头和电动螺丝刀,它们都有相同的方面trigger,但如果你扣动扳机,它们的行为都会有所不同

    【讨论】:

      猜你喜欢
      • 2023-04-01
      • 2013-07-04
      • 2011-06-25
      • 2011-02-22
      • 2012-06-27
      • 1970-01-01
      • 2014-04-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多