最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 边学边译JS工作机制--26. 多态的3种类型

    正文概述 掘金(葱小白)   2021-08-11   551

    在现实世界中,一个女性可以具有不同的角色。她可以是同时是妈妈,职工,妻子。根据不同的角色,她需要做不同的行为。这就是多态的概念,只是用另一种形式表现。

    在JS中,多态这个概念并不广为人知,但是在面向对象编程中,它是非常核心的一个概念。

    面向数据的编程语言,比如Rust。也会用Entity Component System (ECS)实现多态。JS中的编程写法具有不同的模式。在这个主题里,我们将会揭示多态是什么,在JS中怎么使用,多态的不同类型。

    什么是多态

    假如我们要计算一个区域的面积和周长,我们可能会定义两个方法area() 和 perimeter()。但是我们不可能用一套算法,来计算不同的形状。比如圆形和三角形的周长公式是不一样的。 那我们要先定义出基类 shapes,然后把不同的形状当做它的子类或派生类,他们具有各自的周长计算公式。这就叫多态。 在编程中,多态的定义是一个对象的能力具有多种模式。下一小节,我们会更加深入的理解JS是如何处理多态的。

    JS如何处理多态

    不同语言实现多态的方式有所不同。比如JS和JAVA都是面向对象的编程语言,但是实现多态的方式也不同。我们也会看到在封装和继承中多态是如何工作的。

    多态和面向对象编程

    面向对象模型依赖的概念是对象和类。通过this 或 self来改变它的数据字段

    我们会对比一下,用JS实现多态和其他面向数据编程实现多态有什么区别(使用ECS)。 使用面向对象编程,我们可以创建一个类来计算不同形状的面积和周长:

    边学边译JS工作机制--26. 多态的3种类型

    class Shape {
      area() {
        return 0;
      }
      perimeter() {
        return 0;
      }
      toString() {
        return Object.getPrototypeOf(this).constructor.name;
      }
    }
    
    class Circle extends Shape {
      constructor(r) {
        super();
        this.radius = r;
      }
    
      area() {
        return Math.PI * this.radius ** 2;
      }
    
      perimeter() {
        return Math.PI * this.radius * 2;
      }
    }
    
    class Rectangle extends Shape {
      constructor(w, h) {
        super();
        this.width = w;
        this.height = h;
      }
    
      area() {
        return this.width * this.height;
      }
      perimeter() {
        return 2 * (this.width + this.height);
      }
    }
    
    function cumulateShapes(shapes) {
      return shapes.reduce((sum, shape) => {
        if (shape instanceof Shape) {
          console.log(`Shape: ${shape.toString()} - area: ${shape.area()}`);
          console.log(
            `Shape: ${shape.toString()} - perimeter: ${shape.perimeter()}`
          );
          return sum + shape.area();
        }
        throw Error("Bad argument shape.");
      }, 0);
    }
    
    const shapes = [new Circle(3), new Rectangle(2, 3)];
    
    console.log(cumulateShapes(shapes));
    

    边学边译JS工作机制--26. 多态的3种类型

    如果使用ECS,那么会变成这样

    var Position;
    var Circle;
    var Rectangle;
    
    class CirlceSystem extends Position {
      OnUpdate() {
        ComponentQuery.SelectReadOnly(typeof Position, typeof Circle).ForEachEntity(
          (Entity, Position, Circle) => {
            /* find area and perimeter */
          }
        );
      }
    }
    
    class RectangleSystem extends Position {
      OnUpdate() {
        ComponentQuery.SelectReadOnly(
          typeof Position,
          typeof Rectangle
        ).ForEachEntity((entity, Position, Rectangle) => {
          /* find area and perimeter */
        });
      }
    }
    

    在JS代码中,我们使用了继承。在ECS代码中,我们使用了ECS模型来实体分发到组件中,解耦数据。

    我们深入理解一下,JS中的继承,以及它如何跟多态相关

    多态和继承

    在面向对象的多态中,继承是非常重要的一个特性。 看一个car的例子

    class Car {
      constructor(color, speed) {
        this._speed = speed;
        this._color = color;
      }
    }
    

    现在我们有不同的子类了,比如宝马,丰田,本田等等,他们的属性比如颜色和速度有所不同

    class BMW extends Car {
      constructor(color, speed, make) {
        super(color, speed);
        this._make = make;
      }
    
      showInfo() {
        console.log(
          "I’m " +
            this._make +
            ", my color is " +
            this._color +
            ", and my speed is " +
            this._speed
        );
      }
    }
    
    class Toyota extends Car {
      constructor(color, speed, make) {
        super(color, speed);
        this._make = make;
      }
    
      showInfo() {
        console.log(
          "I’m " +
            this._make +
            ", my color is " +
            this._color +
            ", and my speed is " +
            this._speed
        );
      }
    }
    
    class Bentely extends Car {
      constructor(color, speed, make) {
        super(color, speed);
        this._make = make;
      }
    
      showInfo() {
        console.log(
          "I’m " +
            this._make +
            ", my color is " +
            this._color +
            ", and my speed is " +
            this._speed
        );
      }
    }
    

    现在,我们给我们的车添加不同的颜色,速度。

    var myBentely = new Bentely('Red', '20mph', 'Bentely');
    var myBMW = new BMW('Green', '40mph', 'BMW');
    var myToyota = new Toyota('White', '60mph', 'Toyota');
    console.log(myBentely.showInfo()); 
    console.log(myBMW.showInfo());  
    console.log(myToyota.showInfo());
    

    边学边译JS工作机制--26. 多态的3种类型 在例子中,子类获取了父类的属性,并定义它。继承可以由当前的从父类甚至祖父类中派生。

    JS种继承的类型

    JS 继承时一个很大的话题,有很多不同的实现方式,比如基于原型的,类的(虚假的类),函数的。我们简单看一下区别,以及他们如何实现多态:

    1.  原型继承 原型的比较简单,就在原型上加方法就可以了

    let Car = {
      color: "Red",
    };
    let BMW = {
      make: "BMW",
    };
    
    BMW.__proto__ = Car;
    
    // we can find both properties in BMW now:
    console.log("This is a " + BMW.color + " " + BMW.make);
    

    边学边译JS工作机制--26. 多态的3种类型 2.  类继承

    前面说过,JS种的类时为伪概念,语法糖,所以我们称之为伪类。 class的实现是基于new关键字,但是调用的是一个函数。比如,我们有一个car对象。

    function Car(make, color, speed) {
      this.make = make;
      this.color = color;
      this.speed = speed;
    }
    

    我们可以使用new 关键字,来给他创建不同的子类。

    var Toyota = new Car ("Toyota", "Red", "100mph");
    var Bentley = new Car ("Bentley", "White", "120mph");
    var BMW = new Car ("BMW", "Green", "90mph");
    

    使用原型,我们创建了不同的car对象。下一步,我们会看一下如何像继承意向传递原型,以及这样做对多态有什么样的影响 首先,我们创建一个dialogue函数,让我们的card继承它。

    function dialogue() {
      console.log('I am ' + this.make);
    }
    

    利用原型,让我们的cars来继承它

    Car.prototype.dialogue = function () {
      console.log(
        "I am a " +
          this.color +
          "  " +
          this.make +
          " with  " +
          this.speed +
          "  speed "
      );
    };
    console.log(Toyota.dialogue());
    console.log(BMW.dialogue());
    console.log(Bentley.dialogue());
    

    边学边译JS工作机制--26. 多态的3种类型

    3.  基于函数的继承

    基于函数的继承,是给对象加上增强函数

    function Person(data) {
      var that = {};
      that.name = data.name;
      return that;
    }
    
    // Create a child object, to inherit from the base Person
    function Employee(data) {
      var that = Person(data);
      that.sayHello = function () {
        return "Hello, I'm " + that.name;
      };
      return that;
    }
    
    var myEmployee = Employee({ name: "Rufi" });
    console.log(myEmployee.sayHello());
    

    边学边译JS工作机制--26. 多态的3种类型

    多态和封装

    理解了继承,再理解封装就很容易了。在写代码的时候,我们经常需要把一些代码封装起来,这样用户从外面就无法访问里面的值。

    例如,我们把验证学生特征的数据组合在一起,然后使用基于原型多态的方式来继承。

    function Student(name, marks) {
      var student_name = name;
      var student_marks = marks;
      Object.defineProperty(this, "name", {
        get: function () {
          return student_name;
        },
        set: function (student_name) {
          this.student_name = student_name;
        },
      });
    
      Object.defineProperty(this, "marks", {
        get: function () {
          return student_marks;
        },
        set: function (student_marks) {
          this.student_marks = student_marks;
        },
      });
    }
    var stud = new Student("Mercy's score is: ", 60);
    console.log(stud.name + " " + stud.marks);
    

    边学边译JS工作机制--26. 多态的3种类型

    这个例子很好的帮助我们理解JS中的封装和多态。 很多人不理解抽象和封装的区别。抽象,只能看到一部分信息,其他的部分被隐藏了。而封装,是把数据包带一个单独的实体中,外界无法访问。使用封装最主要的原因是控制和校验数据--就像上面的例子一样。

    多态的类型

    JS中实现多态有多种方式,我们讨论以下几种

    Ad-hoc Polymorphism(特设多态)

    特设多态是指'视觉上'不同的类型,表现的行为也是不同的。特设多态可以包含同名的,但是参数或返回值不同的函数。

    这种类型也被叫做重载,我们看一个操作符的重载。

    Operator Overloading

    5 + 5; // will print 10
     'I am' + ' ' + '5 years old' // will print I am 5 years old
    

    在上面的例子中,+ 表示了数字相加以及字符串拼接两种范式。

    参数化多态

    参数化多态处理普通的函数和数据类型,同时维持静态类型安全。普通函数和数据类型,可以被重写,所以不会基于他们的类型进行区分对待。 例如,对象保存了不同的数据类型。它不会基于他们的类型来区分他们的值。

    const Ann = {
    firstName: 'Ann',
    lastName: 'Kim',
    Age: 4,
    Adult: false
    }
    

    上面的Ann对象,包含了Ann的名字-字符串类型,年龄--数字类型,是否成年--布尔类型。尽管他们的类型不同,但是对于对象说,处理的方式是差不多的。 类似的例子还有数组。在JS中,数组可以承载不同的元素。 const Ann = [‘Ann’, ‘Kim’, 4, false];

    数组对它包含的元素处理也是类似的,如果你在控制台运行console.log(Ann),能发现所有的元素都被打印出来。

    边学边译JS工作机制--26. 多态的3种类型

    边学边译JS工作机制--26. 多态的3种类型 看另外一个例子

    const id = (x) => x;
    id(1); // 1
    id("foo"); // "foo"
    

     id不会因为参数1 and foo的类型来判断他们的值。所以你可以给 id 传入不同类型的参数。

    子类型多态

    子类型多态包含子类型和子类型数据类型。它不会包含新对象的创建,主要基于接口的实现,以及不同的实现方式。

    假如你获得了亲人的遗产---一个书店。那么你可以查阅里面的书,查阅遗产账户,书店客户等等,这叫做继承,你获得了遗产里所有的东西。

    假如亲人这份遗产没有给你,你可以选择自己重新开一个,然后承担起你亲戚原来的角色,但是根据自己的喜欢做一些改变---这叫子类型。

    看个例子

    function Animal () {
    
    }
    
    Animal cat = new Cat ("Kitty");
    Animal Dog = new Cat ("puppy");
    Animal cat = new Cat ("Kiddy");
    
    //you can go ahead to create different properties for different animals
    

    cat ,dog,goat都是animals的子类型。一个animal可以是任何一个。你可以不同的animal做不同的实现。

    常见的JS多态陷阱

    我们大概的讲了一下多态,但是也要谨记一些陷阱:

    1. 多态会影响你代码的性能。一个单一的函数要比多态函数运行的快。
    2. 多态会降低代码的可阅读性。为了解决这个问题,所以在使用多态时写好注释。
    3. JS中实现多态很容易,但是要理解继承。因为JS中多态时围绕继承实现的。

    为什么使用多态

    为了复用。 一方面,因为要使用继承,提升了代码的复用能力。 另一方面,可以把不同类型的数据放在一起处理。比如我们熟悉的数组。

    const Ann = [‘Ann’, ‘Kim’, 4, false];
    

    程序中使用多态,最主要的还是让程序扩展性和维护性更好。


    起源地下载网 » 边学边译JS工作机制--26. 多态的3种类型

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元