《The Joy of Javascript》- 1 - Object/Function
2022-06-18
Abstract
早先在 Github 看到人提起这本书, 我简单翻了一下目录, 发现有一些内容还挺有意思, 里面有很多近几年的新方法, 正好补充一些之前开发未涉及的部分. 本以为两三周可以阅读完毕, 没想到啃这本书真是困难, 引用了数学分析和函数式的概念, 一些部分看得我一脸懵逼, 额外阅读了大量的 Funtional Programming 的文章才得以吃掉这本书.
请支持正版书籍 Amazon 购买链接
一本书里面内容较多, 因此分成了多篇 Post, 可以从此处看到相关文章:
Mixin 模式的优势:
- 支持多继承
- 可以对现有的对象添加独立的方法, 这些方法可以在多个对象里面复用
const TerrestrialAnimal = { walk() {}, breathe() { console.log("Using my lungs to breathe"); }, }; const AquaticAnimal = { swim() { }, breathe() { console.log("Using my gills to breathe"); }, }; const Amphibian = (name) => Object.assign( { name, }, AquaticAnimal, /* Using my gills to breathe */ TerrestrialAnimal /* Using my lungs to breathe */ ); const frog = Amphibian("Frog"); frog.walk(); frog.swim(); frog.breathe(); /* Using my lungs to breathe */
/* HasHash 返回一个含有 calculateHash 方法的对象 */ const HasHash = (keys) => ({ /* keys 接受一个字符串数组 */ calculateHash() { const data = keys.map((f) => this[f]).join(""); /* 注意此处会获取 this 里面的属性 */ let hash = 0, i = 0; while (i < data.length) { hash = ((hash << 5) - hash + data.charCodeAt(i++)) << 0; } return hash ** 2; }, }); const Transaction = { init(sender, recipient, funds = 0.0) { this.sender = sender; this.recipient = recipient; this.funds = Number(funds); return this; }, displayTransaction() { return `Transaction from ${this.sender} to ${this.recipient} for ${this.funds}`; }, }; /* 核心代码, 通过 Object.create 创建一个对象并绑定 this, 同时使用 Object.assign 对对象进行扩展 */ const HashTransaction = Object.assign( Object.create(Transaction), HasHash(["sender", "recipient", "funds"]) ); const t = HashTransaction.init("Alice", "Bob", 100.0); console.log(t.calculateHash()); // 620300900133850900 console.log(t.displayTransaction()); // Transaction from Alice to Bob for 100
const DEFAULT_ALGO_SHA256 = 'SHA256'; const DEFAULT_ENCODING_HEX = 'hex'; const HasHash = ( keys, options = { algorithm: DEFAULT_ALGO_SHA256, encoding: DEFAULT_ENCODING_HEX } ) => ({ calculateHash() { const data = keys.map(f => this[f]).join(''); let hash = 0, i = 0; while (i < data.length) { hash = ((hash << 5) - hash + data.charCodeAt(i++)) << 0; } return hash ** 2; } }) class Transaction { transactionId = ''; timestamp = Date.now(); #feePercent = 0.6; constructor(sender, recipient, funds = 0.0, description = 'Generic') { this.sender = sender; this.recipient = recipient; this.funds = Number(funds); this.description = description; this.transactionId = this.calculateHash(); /* 特别注意这个地方调用了 calculateHash, 并且 calculateHash 不是 Transaction 自带的方法 */ } displayTransaction() { return `Transaction ${this.description} from ${this.sender} to ${this.recipient} for ${this.funds}`; } get netTotal() { return Transaction.#precisionRound( this.funds * this.#feePercent, 2); } static #precisionRound(number, precision) { const factor = Math.pow(10, precision); return Math.round(number * factor) / factor; } } /* 在扩展前直接实例化会报错: TypeError: this.calculateHash is not a function, 因为 Transaction 自身还没有这个方法 */ // const t1 = new Transaction('Mike', 'Jack', 10.0, 'Generic'); /* 核心代码 */ /* 通过 Object.assign 将 HasHash Mixin 扩展到 Transaction 里面 */ Object.assign( Transaction.prototype, HasHash(['timestamp', 'sender', 'recipient', 'funds']), // HasSignature(['sender', 'recipient', 'funds']), // HasValidation() ) /* 此时进行实例化就可以调用 HasHash 返回的 calculateHash 方法 */ const t2 = new Transaction('Mike', 'Jack', 10.0, 'Generic'); console.log(t2.transactionId);
Functional Programming | OOP |
Does not exist State | Exists State |
Uses Immutable data | Uses Mutable data |
It follows Declarative Programming Model | It follows Imperative Programming Model |
Stateless Programming Model | Stateful Programming Model |
Main Fcous on: "What you are doing" | Main focus on "How you are doing" |
Good for Parallel (Concurrency) Programming | Poor for Parallel (Concurrency) Programming |
Good for BigData processing and analysis | NOT Good for BigData processing and analysis |
Supports pure Encaspulation | It breaks Encaspulation concept |
Functions with No-Side Effects | Methods with Side Effects |
Functions are first-class citizens | Objects are first-class citizens |
Primary Manipulation Unit is "Function" | Primary Manipulation Unit is Objects(Instances of Classes) |
Flow Controls: Function calls, Function Calls with Recursion | Flow Controls: Loops, Conditional Statements |
It uses "Recursion"concept to iterate Collection Data. | It uses "Loop"concept to iterate Collection Data. For example:-For-each loop in Java |
Order of execution is less importance. | Order of execution is must and very important. |
Supports both "Abstraction over Data"and "Abstraction over Behavior". | Supports only "Abstraction over Data". |
We use FP when we have few Things with more operations. | We use OOP when we have few Operations with more Things. For example: Things are classes and Operations are Methods in Java. |
功能编程 | 面向对象 |
不存在状态 | 存在状态 |
使用不可变数据 | 使用可变数据 |
它遵循声明式编程模型 | 它遵循命令式编程模型 |
无状态编程模型 | 有状态编程模型 |
主要观点: "你在做什么" | 主要关注"你的表现" |
适合并行 (并发) 编程 | 并行 (并发) 编程能力差 |
适用于 BigData 处理和分析 | 不适合 BigData 处理和分析 |
支持纯封装 | 它打破了包容性概念 |
无副作用的功能 | 副作用方法 |
职能是一等公民 | 对象是一等公民 |
主要操作单元是"功能" | 主要操作单位是对象 (类的实例) |
流控制: 函数调用, 带递归的函数调用 | 流控制: 循环, 条件语句 |
它使用"递归"概念来迭代收集数据. | 它使用"循环"概念来循环收集数据. 例如: Java 中的 for-for 循环 |
执行顺序不太重要. | 执行顺序是必须且非常重要的. |
支持"基于数据的抽象"和"基于行为的抽象". | 仅支持"基于数据的抽象". |
当我们的事物很少且需要更多操作时, 我们将使用 FP. | 当我们很少进行具有更多事物的操作时, 我们将使用 OOP. 例如: 在 Java 中, 事物是类, 而操作是 Java 中的方法. |
一个命令式编程的例子:
const arr = ['john-reese', 'harold-finch', 'sameen-shaw']; const newArr = []; for (let i = 0, len = arr.length; i < len ; i++) { let name = arr[i]; let names = name.split('-'); let newName = []; for (let j = 0, naemLen = names.length; j < naemLen; j++) { let nameItem = names[j][0].toUpperCase() + names[j].slice(1); newName.push(nameItem); } newArr.push({ name : newName.join(' ') }); } return newArr;
一个函数式编程的例子:
const capitalize = x => x[0].toUpperCase() + x.slice(1).toLowerCase(); const genObj = curry((key, x) => { let obj = {}; obj[key] = x; return obj; }) // 使用 compose 将多个方法进行结合 const capitalizeName = compose(join(' '), map(capitalize), split('-')); const convert2Obj = compose(genObj('name'), capitalizeName) const convertName = map(convert2Obj); convertName(['john-reese', 'harold-finch', 'sameen-shaw'])
FP 核心专注于以下几个方法:
- compose
- curry
使用这些方法可以对函数进行合成和连锁调用
主流三方库已经有类似实现, 比如 lodash 的
_.curry
,_.flow
. 甚至这些库还支持使用_.curryRight
,_.flowRight
来进行从右向左的执行顺序
function compose(……funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (……args) => a(b(……args))) }
使用方法:
const operate = compose(div2, mul3, add1, add1) operate(0) // 相当于 div2(mul3(add1(add1(0)))) operate(2) // 相当于 div2(mul3(add1(add1(2))))
export const curry = fn => (……args1) => args1.length === fn.length ? fn(……args1) : (……args2) => { const args = [……args1, ……args2] return args.length >= fn.length ? fn(……args) : curry(fn)(……args) }
- JavaScript offers many choices for building objects, including prototypal inheritance, constructor functions, and classes.
- The phrase prototype inheritance is an oxymoron because the idea of a shared linked prototype object is contradictory to the class inheritance model, in which instances gain copies of the inherited data.
- Constructor functions have been the standard mechanisms used to mimic the idea of classes in JavaScript.
- Classes smooth over the details of the prototype configuration for newcomers or developers coming from other class-based languages and have become the preferred choice of JavaScript developers.
- The class syntax can blur your understanding of JavaScript』s prototype inheritance mechanism. Classes are useful, but remember that JavaScript is different from other class-based languages you may have seen or used.
关于本文
文章标题 | 《The Joy of Javascript》- 1 - Object/Function |
发布日期 | 2022-06-18 |
文章分类 | Reading |
相关标签 | #Reading #The Joy of Javascript |
留言板
PLACE_HOLDER
PLACE_HOLDER
PLACE_HOLDER
PLACE_HOLDER
PLACE_HOLDER