如何使用函数式编程正确替换“扩展”?

我正在研究如何在 javascript 中应用函数式编程,并且正在尝试避免使用class关键字。


我不喜欢仅仅为了遵循某种范式而采取极端措施,但我很想知道是否可以在不使用类的情况下编写好的代码。


到目前为止,我主要使用函数取得了成功,但有一种情况我无法真正弄清楚。


当我们有想要在不同对象之间重用的行为时,我们通常(在 OOP 中)创建一个扩展另一个类的类。


class FlyingThing {

   private let _isFlying = false


   fly() {

       _isFlying = true

       return this

   }


   land() {

       _isFlying = false

       return this

   }


   isFlying() {

      return _isFlying

   }

}


class Duck extends FlyingThing {

   quack() {

       return 'Quack!'

   } 

}


const duck = new Duck()

console.log(duck.fly().quack())

现在到功能方法...


示例取自:https ://medium.com/javascript-scene/functional-mixins-composing-software-ffb66d5e731c


const flying = o => {

  let isFlying = false

  return Object.assign({}, o, {

    fly () {

      isFlying = true

      return this

    },

    isFlying: () => isFlying,

    land () {

      isFlying = false

      return this

    }

  })

}


const quacking = quack => o => Object.assign({}, o, {

  quack: () => quack

})


const createDuck = quack => quacking(quack)(flying({}))

const duck = createDuck('Quack!')

console.log(duck.fly().quack())

好的,我喜欢这个主意;我们正在使用组合,并且我们没有任何父母和孩子之间的紧密耦合。凉爽的。


但是,通常当我们使用类时,子级可以访问父级的成员,并且可能需要在某些方法中使用它。例如:


class FlyingThing {

   private let _isFlying = false


   fly() {

       _isFlying = true

       return this

   }


   land() {

       _isFlying = false

       return this

   }


   isFlying() {

      return _isFlying

   }

}


class Duck extends FlyingThing {

   quack() {

       return 'Quack!'

   }


   // New method - Depends on 'isFlying' defined in parent

   layEgg() {

       if(isFlying) return

       return 'Laying egg...'

   }

}


const duck = new Duck()

console.log(duck.fly().quack())

所以问题是,我们如何仅使用函数优雅地解决这个问题?


catspeake
浏览 151回答 1
1回答

慕的地10843

前言OP 正在寻找的可能解决方案背后的机制仍然是 OO,就像 OO 一样;毕竟,人们正在处理的是通过调用 javascript 函数进行对象组合(或对象/类型扩充)。Eric Elliott -功能性 mixin - 和 Douglas Crockford -功能性继承- 每个都很好地解释了他们的方法。他们可能错过了命名/标签。在我看来,它应该像基于函数的 mixin一样简单。JavaScript 开发人员之间的混淆会减少,因为函数式术语将不再指向或误导»The Land of FP«。JavaScript 的强大功能function来自于它的每一个能力,第一是通过创建闭包来保持范围,第二是通过它的调用方法之一访问上下文并this提供前者。在第 3 位,它本身是一个可以传递的一流对象,它只是对完整的包进行了四舍五入。callapply方法OP 的问题是如何实现模块化行为,该行为依赖于由另一个行为封装的状态,可以通过传递此状态来解决。这种状态不一定要公开展示。Eric 和 Douglas 的概念将通过实际应用而得到尊重/认可。在我看来,JavaScript 中的模块化可组合行为始终应该由一个函数提供,该函数既不应该通过new关键字调用,也不应该由调用运算符调用...... (),但它总是必须应用于/应用于其他对象/通过call或类型apply。具有共享但受保护(本地范围)飞行状态的 OP 示例代码...function withFlightStateAlteringFlightCapability(state) {  const flightCapableType = this;  flightCapableType.fly = () => {    state.flying = true;    return flightCapableType;  };  flightCapableType.land = () => {    state.flying = false;    return flightCapableType;  };  flightCapableType.isFlying = () => state.flying;  return flightCapableType;}function withFlightStateDependedEggLayingBehavior(state) {  const oviparousType = this;  oviparousType.layEgg = () => {    let returnValue;    // if (!this.isFlying()) {    if (!state.flying) {      returnValue = 'Laying egg...'    }    return returnValue;  };  return oviparousType;}function withMetaBehavior(label, behavior) {  this[label] = behavior;}class Duck {  constructor() {    // - glue code wrapped by constructor.    // - type will feature a class signature.    // - `state` gets preserved by the closure that is created with each instantiation.    // local state (shared and protected)    const state = {      flying: false    };    const duck = this;    withFlightStateAlteringFlightCapability.call(duck, state);    withFlightStateDependedEggLayingBehavior.call(duck, state);    withMetaBehavior.call(duck, 'quack', () => 'Quaaack...Quaaack...');  }}const duck = new Duck;function createDuckAlikeType() {  // - glue code wrapped by factory function.  // - type will be an augmented but ordinary `Object` type.  // - `state` gets preserved by the closure that is created with each invocation of the factory.  // local state (shared and protected)  const state = {    flying: false  };  const type = {};  withFlightStateAlteringFlightCapability.call(type, state);  withFlightStateDependedEggLayingBehavior.call(type, state);  withMetaBehavior.call(type, 'quack', () => 'Quack!');  return type;}const duckAlikeType = createDuckAlikeType();console.log('composed "real duck" : ', duck);console.log('composed "duck alike type" : ', duckAlikeType);console.log('\nduck.fly() ...');duck.fly();console.log('\nduck.isFlying() ? ', duck.isFlying());console.log('duckAlikeType.isFlying() ? ', duckAlikeType.isFlying());console.log('\nduck.layEgg() ? ', duck.layEgg());console.log('duckAlikeType.layEgg() ? ', duckAlikeType.layEgg());console.log('\nduck.land().layEgg() ? ', duck.land().layEgg());console.log('duckAlikeType.fly().layEgg() ? ', duckAlikeType.fly().layEgg());console.log('\nduck.isFlying() ? ', duck.isFlying());console.log('duckAlikeType.isFlying() ? ', duckAlikeType.isFlying());console.log('\nduck.quack() ? ', duck.quack());console.log('duckAlikeType.quack() ? ', duckAlikeType.quack());.as-console-wrapper { max-height: 100%!important; top: 0; }
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript