Mixin 函数的详细解析

本文介绍了Mixin函数,它是能给对象添加属性或行为的组合工厂函数,具有数据封装、多继承等特点。阐述了其由来,指出类继承存在高耦合等问题,而Mixin提供更灵活方法。还说明了Mixin概念、函数继承,以及Mixin函数可解决函数继承问题和组合方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是Mixin 函数

在编程中,mixin 类似于一个固有名词,可以理解为混合或混入,通常不进行直译,本文也是同样。

Mixin 函数 是指能够给对象添加属性或行为,并可以通过管道连接在一起的组合工厂函数,就如同流水线上的工人。Mixin 函数不依赖或要求一个基础工厂或构造函数:简单地将任意一个对象传入一个 mixin,就会得到一个增强之后的对象。

 

 

Mixin 函数的特点

  • 数据封装
  • 继承私有状态
  • 多继承
  • 覆盖重复属性
  • 无需基础类

 

 

Mixin 函数的由来

现代软件开发的核心就是组合:我们将一个庞大复杂的问题,分解成更小,更简单的问题,最终将这些问题的解决办法组合起来就变成了一个应用程序。他们的组合就定义了应用的结构。

组合的最小单位就是以下两者之一:函数和数据结构

通常,组合对象由类继承实现,其中子类从父类继承其大部分功能,并扩展或覆盖部分。这种方法导致了 is-a 问题,比如:管理员是一名员工,这引发了许多设计问题:

  • 高耦合:由于子类的实现依赖于父类,所以类继承是面向对象设计中最紧密的耦合。
  • 脆弱的子类:由于高耦合,对父类的修改可能会破坏子类。软件作者可能在不知情的情况下破坏了第三方管理的代码。
  • 层次不灵活:根据单一祖先分类,随着长时间的演变,最终所有的类都将不适用于新用例。
  • 重复问题:由于层次不灵活,新用例通常是通过重复而不是扩展来实现的,这导致不同的类有着相似的类结构。而一旦重复创建,在创建其子类时,该继承自哪个类以及为什么继承于这个类就不清晰了。
  • 大猩猩和香蕉问题:“...面向对象语言的问题是他们会获得所有与之相关的隐含环境。比如你想要一个香蕉,但你得到的会是一只拿着香蕉的大猩猩,以及一整片丛林。” - Joe Armstrong(Coders at Work)

假设管理员是一名员工,你如何处理聘请外部顾问暂时行使管理员职务的情况?如果你事先知道所有的需求,类继承可能有效,但我从没有看到过这种情况。随着不断地使用,新问题和更有效的流程将会被发现,应用程序和需求不可避免地随着时间的推移而发展和演变。

Mixin 提供了更灵活的方法

 

 

什么是 Mixin?

“组合优于继承。” - 设计模式:可重用面向对象软件的元素

Mixin 是对象组合的一种,它将部分特性混入复合对象中,使得这些属性成为复合对象的属性。

面向对象编程中的 "mixin" 一词来源于冰激凌店。不同于将不同口味的冰激凌预先混合,每个顾客可以自由混合各种口味的冰激凌,从而创造出属于自己的冰激凌口味。

对象 mixin 与之类似:从一个空对象开始,然后一步步扩展它。由于 JavaScript 支持动态对象扩展,所以在 JavaScript 中使用对象 mixin 是非常简单的。它也是 JavaScript 中最常见的继承形式,来看一个例子:

const chocolate = {
  hasChocolate: () => true
};
const caramelSwirl = {
  hasCaramelSwirl: () => true
};
const pecans = {
  hasPecans: () => true
};

console.log(chocolate.hasChocolate())

const iceCream = Object.assign({}, chocolate, caramelSwirl, pecans);

/*
// 支持对象扩展符的话也可以写成这样...
const iceCream = {...chocolate, ...caramelSwirl, ...pecans};
*/

console.log(`
  hasChocolate: ${ iceCream.hasChocolate() }
  hasCaramelSwirl: ${ iceCream.hasCaramelSwirl() }
  hasPecans: ${ iceCream.hasPecans() }
`);
/* 输出
  hasChocolate: true
  hasCaramelSwirl: true
  hasPecans: true
*/

 

 

什么是函数继承?

函数继承是指通过函数来增强对象实例实现特性继承的过程。该函数建立一个闭包使得部分数据是私有的,并通过动态对象扩展使得对象实例拥有新的属性和方法。

来看一下这个词的创造者 Douglas Crockford 所给出的例子。

// 父类
function base(spec) {
  var that = {}; // Create an empty object
  that.name = spec.name; // Add it a "name" property
  return that; // Return the object
}


// 子类
function child(spec) {
  // 调用父类构造函数,返回父类中的that对象,这个that对象中有name属性,也就是spec这个参数值
  var that = base(spec);
  
  that.sayHello = function() { // Augment that object
    return 'Hello, I\'m ' + that.name;
  };
  return that; // Return it
}


// Usage
var result = child({ name: 'a functional object' });
console.log(result.sayHello()); // "Hello, I'm a functional object"

由于 child() 同 base() 紧密耦合在一起,当你想添加 grandchild()greatGrandchild()等时,你将面对类继承中许多常见的问题。

 

 

Mixin 函数解决函数继承的问题

Mixin 函数是一系列将新的属性或行为混入特定对象的组合函数。它不依赖或需要一个基础工厂方法或构造器,只需将任意对象传入一个 mixin 方法,它就会被扩展。

const flying = function (o) {
  let isFlying = false
  return Object.assign({}, o, {
    fly () {
      isFlying = true;
      return this;
    },
    isFlying: () => isFlying,
    land () {
      isFlying = false;
      return this;
    }
  });
};
const bird = flying({});

console.log(bird)
console.log( bird.isFlying() ); // false
console.log( bird.fly().isFlying() ); // true

这里需要注意,当调用 flying() 时需要传递一个被扩展的对象。Mixin 函数被设计用来实现函数组合,继续看下去。

const quacking = quack => o => Object.assign({}, o, {
  quack: () => quack
});
const quacker = quacking('Quack!')({});
console.log( quacker.quack() ); // 'Quack!'

上面代码就相当于

const quacking = function(quack) {
  return function quacking_in(o) {
    return Object.assign({}, o, {
      quack: function() {
        return quack
      }
    });
  }
}
const quacker = quacking('Quack')
console.log(quacker) // [Function: quacking_in]
const quacker_in = quacker()
console.log(quacker_in) // { quack: [Function: quack] }
console.log(quacker_in.quack()) //Quack

 

 

组合 Mixin 函数

通过简单的函数组合就可以将 mixin 函数组合起来。

但是,这看上去有点丑陋,调试或重新排列组合顺序也有点困难。

当然,这只是标准的函数组合,而我们可以通过一些好的办法来将它们组合起来,比如 compose()pipe()。如果,使用 pipe() 就需反转函数的调用顺序,才能保持相同的执行顺序。当属性冲突时,最后的属性生效。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值