为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

catspeake 2022-06-16 14:37:52
我正在研究如何在 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-ffb66d5e731cconst 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())所以问题是,我们如何仅使用函数优雅地解决这个问题?
查看完整描述

1 回答

?
慕的地10843

TA贡献1785条经验 获得超8个赞

前言

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; }


查看完整回答
反对 回复 2022-06-16
  • 1 回答
  • 0 关注
  • 150 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号