highlight: a11y-dark
最初发布于 szhshp的第三边境研究所, 转载请注明
关于此书 - 从此我给别人面试可以出究极难度的题目了 😏
请支持正版书籍 Amazon 购买链接
早先在 Github 看到人提起这本书, 我简单翻了一下目录, 发现有一些内容还挺有意思, 里面有很多近几年的新方法, 正好补充一些之前开发未涉及的部分.
本以为两三周可以阅读完毕, 没想到啃这本书真是困难, 引用了数学分析和函数式的概念, 一些部分看得我一脸懵逼, 额外阅读了大量的 Funtional Programming 的文章才得以吃掉这本书.
从此我给别人面试可以出究极难度的题目了 😏
相关文章
一本书里面内容较多, 因此分成了多篇 Post, 可以从此处看到相关文章:
Object
通过使用 mixin 实现对象继承
Mixin 模式的优势:
- 支持多继承
- 可以对现有的对象添加独立的方法, 这些方法可以在多个对象里面复用
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 */
OLOO(Objects-linked-to-other-objects) 模式对象扩展
/* 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
Class 模式对象扩展
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);
Function
函数式编程和 OOP 的比较 | FP vs OOP | Differences between FP and OOP
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 中的方法. |
Functional Programming Example | 函数式编程示例
一个命令式编程的例子:
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
来进行从右向左的执行顺序
compose 方法实现
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))))
curry 方法的实现
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)
}
Summary
- 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.