Live Note

Remain optimistic

一个简陋的 VDOM

希望以后会慢慢完善吧…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
let data = {
tag: "div",
children: [
{
tag: "span",
children: [
{
tag: "#text",
content: "hello world",
},
],
},
{
tag: "p",
children: [
{
tag: "#text",
content: "This is a paragraph!",
},
{
tag: "h1",
children: [
{
tag: "#text",
content: "This is a H1 tag",
},
],
},
{
tag: "h6",
children: [
{
tag: "#text",
content: "and I'm h6",
},
],
},
],
},
],
}

class VDOM {
constructor(data) {
this.tag = data.tag
this.children = data.children
this.content = data.content
}
static render(data) {
let _this = new VDOM(data)
if (_this.tag === "#text") {
return document.createTextNode(_this.content)
}
let node = document.createElement(_this.tag)
_this.children.map((child) => node.appendChild(VDOM.render(child)))
return node
}
}

let diff = (root, oldV, newV, current = 0) => {
if (!oldV) root.appendChild(newV)
else if (!newV) root.removeChild(root.childNodes[current])
else if (newV.tag !== oldV.tag || newV.content !== oldV.content)
root.replaceChild(new VDOM(newV).render(), root.childNodes[current])
// 递归判断
else
for (let i = 0; i < newV.children.length || i < oldV.children.length; i++)
diff(root.childNodes[i], oldV.children[i], newV.children[i], i)
}
document.querySelector("#root").appendChild(VDOM.render(data))

结构为:

1
2
3
4
5
6
7
8
9
10
<div id="root">
<div>
<span>hello world</span>
<p>
This is a paragraph!
<h1>This is a H1 tag</h1>
<h6>and I'm h6</h6>
</p>
</div>
</div>

Container

Let’s create a normal container first.

1
2
3
4
5
6
7
8
9
10
11
class Container {
constructor(x) {
this._value = x
}
// use the of to create the container
static of(x) {
return new Container(x)
}
}
Container.of(2) // Container { _value: 2 }
Container.of({ name: "jack" }) // Container { _value: { name: 'jack' } }

But we should not directly manipulate the data in the container. We need a function to do this.

1
2
3
4
5
6
// Container.prototype.map :: (a -> b) -> Container a -> Container b
Container.prototype.map = function (f) {
return Container.of(f(this._value))
}
let six = Container.of(2).map((x) => x * 3) // Container { _value: 6 }
six.map((x) => x.toString()).map((x) => "number: " + x + "!") // Container { _value: 'number: 6!' }

After passing the values in the Container to the map function, we can let them manipulate it; after the operation is completed, in order to prevent accidents, put them back into the Container they belong to. The result is that we can call the map continuously, running any function we want to run.
And the Functor is a container type that implements the map function and adheres to certain rules.

Maybe

In Haskell, the Maybe type is defined as follow:

1
data Maybe a = Just a | Nothing

Maybe will check if its value is empty before calling the passed function. So let’s create a simple one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Maybe {
constructor(x) {
this._value = x
}
static of(x) {
return new Maybe(x)
}
isNothing() {
return this._value === null || this._value === undefined
}
// map :: (a -> b) -> Maybe a -> Maybe b
map(f) {
return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this._value))
}
}
Maybe.of("hello world").map(match(/o/gi)) // Maybe { _value: [ 'o', 'o' ] }
Maybe.of({ name: "jack" })
.map($.prop("age"))
.map((x) => x + 10) // Maybe { _value: null }
Read more »

Function Composition

In computer science, function composition is an act or mechanism to combine simple functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole. -- Wikipedia

Example:

1
2
3
4
5
6
var compose = (f, g) => (x) => f(g(x))
var head = (x) => x[0]
var reverse = reduce((a, x) => [x].concat(a), [])

var last = compose(head, reverse)
last(["first", "second", "last"]) // 'last'

In the definition of compose, g will be executed before f, thus creating a right-to-left data stream. This is much more readable than nesting a bunch of function calls.

Associativity

Like many other functional programming concepts, associativity is derived from math.It is an expression in which the order of evaluation does not affect the end result provided the sequence of the operands does not get changed. -- JOSEPH REX

Example:

1
2
2 + (3 + 4) === 2 + 3 + 4 // true
2 * (3 * 4) === 2 * 3 * 4 // true

Because of the grouping of calls to compose is not important, so the result is the same. This also gives us the ability to write a variadic compose.
Like this:

1
2
3
4
5
6
7
8
9
10
var last = compose(head, reverse)
last(["first", "second", "last"]) // 'last'

var upperLast = compose(head, reverse, toUppercase)
upperLast(["first", "second", "last"]) // 'LAST'

var mediaUrl = _.compose(_.prop("m"), _.prop("media"))
// var images = _.compose(_.map(img), _.map(mediaUrl), _.prop('items'));
// use the associativity
var images = _.compose(_.map(_.compose(img, mediaUrl)), _.prop("items"))

There is no standard answer on how to composition, just make it more reusable.

Read more »

What is currying

Currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument. -- Wikipedia
It mean this:

1
2
3
4
5
let add = (x) => (y) => x + y
let addOne = add(1)
let addTwo = add(2)
addOne(11) // 12
addTwo(11) // 13

In order to define functions more easily, we need the loadsh, it will help us to currying the function.

make some using thing

1
2
3
4
5
6
7
8
9
10
11
12
13
var _ = require("loadsh").curry

var match = _((regex, str) => str.match(regex))
var replace = _((regex, replacement, str) => str.replace(regex, replacement))
var filter = _((f, array) => array.filter(f))
match(/\s+/g, "test Message") // [' ']
match(/\s+/g)("test Message") // [' ']

var hasSpace = match(/\s+/g)
hasSpace("testMessage") // null
filter(hasSpace, ["testMessage1", "test Message2", "test Message 3"]) // ['test Message2', 'test Message 3']
var noA = replace(/[Aa]+/g, "*")
noA("aaaabbbAAAc") // '*bbb*c'
Read more »

Function

function has special rules:

  1. It must work for every possible input value
  2. And it has only one relationship for each input value

Although each input will only have one output, but for different inputs may have the same output.

Pure function

Given all these, pure functions have a big set of advantages. They are easier to read and understand, as they do one thing. There is no need to look outside the function code and check for variables. There is no need to think how changing the value of the variable will affect other functions. No mutations in other functions will affect the result of the pure function for a specific input.
Pure functions are easier to test, as all dependencies are in the function definition and they do one thing.
For Example:

1
2
3
4
5
6
7
8
9
10
var arr = [1, 2, 3]

// Pure
arr.slice(0, 2) // [1, 2]
arr.slice(0, 2) // [1, 2]
arr.slice(2, 3) // [3]

// Impure
arr.splice(0, 2) // [1, 2]
arr.splice(0, 2) // [3]

Another Example:

1
2
3
4
5
6
7
8
9
10
11
// Impure
var sign = 3

// The return value depends on the system status
var isBigger = (num) => num > 3

// Pure
var isBigger = (num) => {
var sign = 3
return num > sign
}

Functional Programming

经过这几天的了解,越觉得 FP 十分有趣
给个例子:

1
2
3
4
5
6
7
8
// a simple function
function add(a, b) {
return a + b
}
/// the same as
let add = function (a, b) {
return a + b
}

在 ES6 中,存在着箭头函数。所以上面的函数可以写成这个形式:

1
let add = (a, b) => a + b
Read more »

  • background: 背景的可爱萌妹子, 当然可以自定义背景
  • beautify: 代码格式化
  • Dracula Official: 颜色主题
  • Haskell Syntax Highlighting: Haskell 语法高亮
  • HTML Snippets: HTML 自动补全
  • HTML CSS Support
  • JavaScript (ES6) code snippets: JS 自动补全
  • Markdown PDF: 神器, 但是默认会装一个 Chromium
  • npm Intellisense: 自动导入模块
  • Path Intellisense: 自动补全文件名
  • Quokka.js: 方便 debug
  • Vetur
  • vscode-icons: vscode 文件图标
  • Vue 2 Snippets
  • yddict(npm): 查词, 非常方便, 安装:sudo npm i yddict -g, 用法: yd hello
  • http-server(npm)

邻接表

存储方式:表头存放节点,相邻节点存放于之后的链表中。
** 使用 Map 模拟 **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Graph {
constructor() {
this.point = []
this.map = new Map()
}

addPoint(point) {
this.point.push(point)
this.map.set(point, [])
}

// 无向
addEdge(pointA, pointB) {
this.map.get(pointA).push(pointB)
this.map.get(pointB).push(pointA)
}

print() {
for (let item of this.point) {
console.log(item + " -> " + this.map.get(item).join(","))
}
}
}

Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var graph = new Graph()
var topArr = ["A", "B", "C", "D", "E", "F", "G", "H", "I"]
for (let item of topArr) {
graph.addPoint(item)
}

graph.addEdge("A", "B")
graph.addEdge("A", "D")
graph.addEdge("A", "E")
graph.addEdge("A", "H")
graph.addEdge("F", "G")
graph.addEdge("I", "B")
graph.addEdge("I", "C")

graph.print()

Binary Search Tree

  • 若左子树不空,则左子树上所有结点的值均小于它的根结点的值。
  • 若右子树不空,则右子树上所有结点的值均大于它的根结点的值。
  • 左、右子树也分别为二叉排序树。
  • 没有键值相等的节点。

实现

Node:

1
2
3
4
5
6
7
8
let print = (key) => console.log(key)
class Node {
constructor(key) {
this.key = key
this.left = null
this.right = null
}
}
Read more »