• 你不知道的JS系列上( 45 ) - 显式混入


    JS 的对象机制并不会自动执行复制行为,由于其他语言中表现出来的复制行为,因此 JS 开发者也想出了一个方式来模拟类的复制行为,这个方法就是混入。我们先看第一种,显式混入

    // 非常简单的 mixin() 例子
    function mixin(sourceObj, targetObj) {
      for (var key in sourceObj) {
        // 只会在不存在的情况下复制
        if (!(key in targetObj)) {
          targetObj[key] = sourceObj[key]
        }
      }
      return targetObj;
    }
    
    var Vehicle = {
      engines: 1,
      ignition: function() {
        console.log('Turning on my engine.');
      },
      drive: function() {
        this.ignition();
        console.log('Steering and moving forward!');
      }
    }
    
    var Car = mixin(Vehicle, {
      wheel: 4,
      drive: function(){
        Vehicle.drive.call(this);
        console.log('Rolling on all ' + this.wheel + ' wheels!');
      }
    })

    我们处理的已经不再是类了,因为在 JS 中不存在类,Vehicle 和 Car 都是对象。现在 Car 中就有了一份 Vehicle 属性和函数的副本。从技术角度来说,函数实际上没有被复制,复制的是函数引用。Car 已经有了 drive 属性,所以这个属性引用没有被 mixin 重写,从而保留了 Car 中定义的同名属性,实现了 ‘子类’ 对 ‘父类’ 属性的重写。


    我们再类分析一下这条语句 Vehicle.drive.call(this)。 这就是所说的显式多态。之前我们代码中 super.drive(),我们称之为相对多态。由于 Car 和 Vehicle 中都有 drive() 函数,为了指明调用对象。我们通过名称显示制定 Vehicle 对象并调用它的 drive() 函数。

    但是如果直接执行 Vehicle.drive(),函数调用中的 this 会被绑定到 Vehicle 对象而不是 Car 对象,这并不是我们想要的。

    我们分析一下 mixin 的工作原理。它会遍历 sourceObj 的属性,如果在 targetObj 没有这个属性就会进行复制。

    如果我们是先进行复制然后对 Car 进行特殊化对话,就可以跳过存在性检查。不过这种方法并不好并且效率更低,所以不如第一种方法常用:
    // 另一种混入函数,可能又重写风险
    function mixin(sourceObj, targetObj) {
      for (var key in sourceObj) {
        targetObj[key] = sourceObj[key];
      }
      return targetObj;
    }
    
    var Vehicle = {   // ... }
    // 首先创建一个空对象并把 Vehicle 对内容复制进去 var Car = mixin( Vehicle, {} ); // 然后把新内容复制到 Car 中 mixin({   wheel: 4,   drive: function() {     // ...   } }, Car)

    这两种方法都可以把不重叠对内容从 Vehicle 中显示复制到 Car 中。‘混入’ 这个名字来源与这个过程对另一种解释: Car 中混合了 Vehicle 的内容,所以这叫混合复制。复制操作完成后, Car 和 Vehicle 分离了,向 Car 中添加属性不会影响到 Vehicle,反之亦然。


    JS 中的函数无法真正的复制,所以只能复制对共享函数的引用,如果修改了共享函数,那么 Vehicle 和 Car 都会受到影响。

    显示混入是 JS 中一个很棒的机制,不过它的功能也没有看起来那么强大。虽然它可以把一个对象的属性复制到另一个对象中,但是这其实并不能带来太多的好处,无非是少几条定义语句,而且还会带来我们能提到的函数对象引用问题。

    之前我们说只有父类和子类,这个比喻不太恰当,因为现实生活中,除了父类,还有母类。绝大多数后代是由双亲产生的。如果类可以继承两个类,那看起来更符合现实的比喻了。有些面向类的语言允许继承多个‘父类’。多重继承意味着所有父类的定义都会复制到子类中。

    JS 中本身并不提供多重继承的功能,许多人认为这是件好事,因为使用多重继承的代价太高,然而这无法阻止开发者门的热情,他们会尝试多种方法来实现多重继承

    比如此例中,如果向目标函数显示混入多个对象,就可以部分模仿多重继承行为,但是从根本上来说,使用这些‘诡计’通常会得不偿失


    显示混入模式还有一种变体被称为 “寄生继承”
    // 传统的 JS 类,Vehicle
    function Vehicle() {
      this.engines = 1;
    }
    Vehicle.prototype.ignition = function () {
      console.log('Turning on my engine.');
    }
    Vehicle.prototype.drive = function () {
      this.ignition();
      console.log('Steering and moving forward!');
    }
    
    // 寄生类 Car
    function Car() {
      // 首先, Car 是一个 Vehicle
      var car = new Vehicle();
    
      // 接着我们对 Car 进行定制
      car.wheels = 4;
    
      // 保存到 Vehicle::drive() 的特殊引用
      var vehDrive = car.drive;
    
      // 重写 Vehicle::drive()
      car.drive = function() {
        vehDrive.call(this);
        console.log('Rolling on all ' + this.wheels + ' wheels!');
      }
      return car;
    }
    var myCar = new Car()
    myCar.drive();

    首先我们复制一份 Vehicle 父类的定义,然后混入子类的定义(如果需要的话保留父类的特殊引用),然后用这个复合对象构建实例。

     

  • 相关阅读:
    .NET、C#和ASP.NET,ASP.NET MVC 四者之间的区别
    Row_Number()显示行号
    iframe高度宽度自适应(转)
    Windows CMD命令大全(转)
    wndows系统命令总结
    删除ORACLE目录OCI.dll文件无法删除 (转)
    IE8兼容性调试及IE 8 css hack
    backbone学习笔记:视图(View)
    js中toFixed() 的使用(转)
    backbone学习笔记:集合(Collection)
  • 原文地址:https://www.cnblogs.com/wzndkj/p/12641139.html
Copyright © 2020-2023  润新知