原文链接:http://raganwald.com/2015/06/10/mixins.html
预备知识:该文章假设读者熟悉JavaScript 对象,知道原型是如何给对象定义行为的,知道构造函数是什么,知道构造器的 prototype 属性和它创建的对象之间是如何产生联系的。对 ES2015 语法有些了解也将有所帮助。
 
一直以来,我们可以这样创建一个 JavaScript 类: 
function Person (first, last) {this.rename(first, last);
}Person.prototype.fullName = function fullName () {return this.firstName + " " + this.lastName;
};Person.prototype.rename = function rename (first, last) {this.firstName = first;this.lastName = last;return this;
}

Person 是一个构造函数,同时也是一个类,当然是 JavaScript 世界中的“类”。
 
ECMAScript 2015 提供了 class 关键字以及“紧凑的方法记号”,它们是编写函数,并给其 prototype 属性赋值方法的语法糖(虽然实际情况更复杂,但和此处不相关)。所以我们现在可以如下编写 Person 类: 
class Person {constructor (first, last) {this.rename(first, last);}fullName () {return this.firstName + " " + this.lastName;}rename (first, last) {this.firstName = first;this.lastName = last;return this;}
};

不错。但究其本质,你仍然不过编写了一个名为 Person 的构造函数,并且 Person.prototype 是这样的一个对象:

{fullName: function fullName () {return this.firstName + " " + this.lastName;},rename: function rename (first, last) {this.firstName = first;this.lastName = last;return this;}
}

原型即对象 

如果我们想改变一个 JS 对象的行为,可以通过添加、删除,或修改作为对象属性的函数,它们就是对象的方法。这和大多数基于类的语言不同,这些语言往往提供特殊的语法形式来定义方法(如 Ruby 的 def)。
 
JavaScript 中的原型不过是普通的对象,正由于它们是普通的对象,我们便可以对原型的方法进行增删改,即对绑定到原型对象作为其属性的那些函数进行操作。
 
这也正是上面 ES5 代码所干的事,并且 ES6 的 class 语法“去语法糖”后也会得到相同代码。
 
原型即普通对象的概念意味着我们可以把原型当普通对象对待。例如,除了将函数一个个绑定到原型上,还可以使用 Object.assign 一次性绑定。 
function Person (first, last) {this.rename(first, last);
}Object.assign(Person.prototype, {fullName: function fullName () {return this.firstName + " " + this.lastName;},rename: function rename (first, last) {this.firstName = first;this.lastName = last;return this;}
})

当然,我们也可以使用紧凑方法声明语法

function Person (first, last) {this.rename(first, last);
}Object.assign(Person.prototype, {fullName () {return this.firstName + " " + this.lastName;},rename (first, last) {this.firstName = first;this.lastName = last;return this;}
})

mixins

由于 class 语法最终转化为构造函数和原型的形式,我们可以混合使用两种技术: 
class Person {constructor (first, last) {this.rename(first, last);}fullName () {return this.firstName + " " + this.lastName;}rename (first, last) {this.firstName = first;this.lastName = last;return this;}
};Object.assign(Person.prototype, {addToCollection (name) {this.collection().push(name);return this;},collection () {return this._collected_books || (this._collected_books = []);}
})

上例中我们将书籍收集相关的方法糅合到了 Person 类上。这点非常不错,因为我们得以让代码具有 point-free 风格,同时命名方面也很棒。

const BookCollector = {addToCollection (name) {this.collection().push(name);return this;},collection () {return this._collected_books || (this._collected_books = []);}
};class Person {constructor (first, last) {this.rename(first, last);}fullName () {return this.firstName + " " + this.lastName;}rename (first, last) {this.firstName = first;this.lastName = last;return this;}
};Object.assign(Person.prototype, BookCollector);

只要愿意,这一过程可一直进行

const BookCollector = {addToCollection (name) {this.collection().push(name);return this;},collection () {return this._collected_books || (this._collected_books = []);}
};const Author = {writeBook (name) {this.books().push(name);return this;},books () {return this._books_written || (this._books_written = []);}
};class Person {constructor (first, last) {this.rename(first, last);}fullName () {return this.firstName + " " + this.lastName;}rename (first, last) {this.firstName = first;this.lastName = last;return this;}
};Object.assign(Person.prototype, BookCollector, Author);

为什么 mixins 可能有用武之地

通过基础功能(Person)和 mixins(BookCollector 和 Author)相结合的方式创建类可获得几点好处。
 
首先,有时候无法将功能完美分解成树形结构。书的作者有时是公司,而不是人。并且古籍书店也可能像藏书家一样收集图书。
 
类似于 BookCollector 或 Author 的 mixin 可被糅合到多个类上。试图使用“继承”来实现功能有时不太确切。
 
另一个好处不容易从玩具用例上看出,但在实际项目中类的定义可以变得非常庞杂。即使某个 mixin 没有在多个类中使用,将一个大类分解成多个 mixin 也有助于实现“单一责任原则”。每个 mixin 可以只做一件事。这使得代码变得容易理解和测试。
 

为什么要知道这些

还有其他方法可以分解类的功能(如委托和组合),但这里想说的是如果我们想使用 mixin,这是非常容易的,因为 JavaScript 并没有庞杂的 OOP 机制对程序施加进行严格的模型限制。
 
例如在 Ruby 中,mixins 用起来也很方便,这得益于与生俱来的 modules 特性。但在其他面向对象语言中,mixins 就没那么顺手了,因为类系统没有相应的支持,并且 mixin 对元编程也不是很友好。
 
JavaScript 使用简单部件(对象,函数以及属性)实现 OOP 的这一选择促进了新思潮的发展。 

 

转载于:https://www.cnblogs.com/averyby/p/10021383.html

原型即对象(以及认识这点的重要性)相关推荐

  1. 有原型的对象和没有原型的对象

    在学习Mobx时接触到了有原型对象和没原型的对象概念 声明一个对象 1 let o = Object.create({}) // 方法一 2 let o = {} // 方法二3 let o = Ob ...

  2. Prototype(原型)--对象创建模式

    Prototype(原型)–对象创建模式 一.意图 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 二.动机 1.在软件系统中,经常面临着"某些结构复杂的对象"的 ...

  3. JavaScript学习(四十九)—构造方法、原型、对象图解

    JavaScript学习(四十九)-构造方法.原型.对象图解

  4. JavaScript中的原型和对象机制

    1 对象相关的一些语言特性 1.1 一切皆为对象 JavaScript里所有的东西都是对象. 对象是属性的集合. 数字, 字符串, 布尔值等原始值是"伪对象", 它们同样拥有属性, ...

  5. 设计模式_4_原型模式(对象的拷贝)

    原形模式(PrototypePattern, 创建型模式,创建重复对象且保证性能, 对象的克隆) 通常使用原型模式创建一个原型接口, 用于获取创建对象的克隆, 对于浅拷贝与深拷贝不用纠结, 他们二者的 ...

  6. 当对象与原型有相同的属性,调用时的上下文指向问题

    在查阅对象自定义事件及其触发实现的过程中,发现了如下代码: 即:构造函数的原型可以是其他构造方法new出来的对象实例,虽然理解构造函数原型就是一个对象,但通常都是用对象直接量来指定,如:F.proto ...

  7. 043_对象构造器和原型

    1. 对象类型(蓝图)(类) 1.1. 通过文本和new Object()的方式, 我们只能创建单一对象. 1.2. 有时我们需要创建相同"类型"的许多对象的"蓝图&qu ...

  8. 关于对象、构造函数、原型、原型链、继承

    对象: 在传统的面向过程的程序设计中,会造成函数或变量的冗余.而js中对象的目的是将所有的具有相同属性或行为的代码整合到一起,形成一个集合,这样就会方便管理,例如: var person1={name ...

  9. [js高手之路]原型对象(prototype)与原型链相关属性与方法详解

    一,instanceof: instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型. 我在之前的两篇文章 [js高手之路]构造函数的基本特性与优缺点 [js高手 ...

最新文章

  1. 15岁成杀人犯,监狱里学编程,37岁获释后年薪70万
  2. 计算机网络 --- 数据链路层CSMA/CD协议
  3. 巨星陨落,光芒永存—回顾霍金对人工智能的思考
  4. mysql 复制功能_MySQL实现主从复制功能
  5. Redis集群部署(半自动)
  6. jzoj 6301. 普及组
  7. ExtendSim 10.0.8发布于 2021年8月7日
  8. 2020年东三省数学建模联赛(辽宁赛区)获奖名单
  9. hdu1728 逃离迷宫
  10. cin.get()的用法
  11. one choise or a complain
  12. CSS 边框 圆角 盒子阴影 圆角 solid dotted dashed
  13. 通过SqlDbx导出*.sql
  14. CentOS 6.3安装chrome
  15. kubernetes-----pod资源创建与Harbor私有仓库
  16. 关于大象,冰箱和软件项目报价的故事
  17. SAP-MM科目自动分配解析-(4-1)- 物料的配置影响
  18. node文件系统 常用文件处理方法
  19. 大疆飞行模拟器 下载、安装及使用教程
  20. 新唐单片机keil驱动问题

热门文章

  1. Spring框架中的控制反转和依赖注入
  2. 两个数据库表同步的可视化WEB同步程序
  3. 解题报告 Toy Bricks
  4. VBS脚本压缩IIS日志
  5. java和python哪个先学c_C 和 Python语言先学哪个好?
  6. (97)FPGA手写RAM(Verilog)
  7. (57)UART外设驱动波特率(二)(第12天)
  8. (95)FPGA仿真文件保存(VCD文件)
  9. nvme驱动_用户态NVMe运维利器 SPDK NVMe 字符设备
  10. Keil5消除未调用警告