Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
hojas committed Jan 10, 2024
1 parent e43a939 commit 4c20804
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 7 deletions.
13 changes: 9 additions & 4 deletions .vitepress/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,19 @@ export const nav: DefaultTheme.NavItem[] = [
// link: 'https://xiaolincoding.com/',
link: '/network',
},
// {
// text: '算法',
// link: '/algorithm',
// link: 'https://www.programmercarl.com/',
// },
{
text: '设计模式',
link: '/design-pattern',
},
{
text: '学习资源',
link: '/resource',
},
// {
// text: '算法',
// link: 'https://www.programmercarl.com/',
// },
{
text: '关于我',
link: '/about',
Expand Down
17 changes: 17 additions & 0 deletions .vitepress/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,21 @@ export const sidebar: DefaultTheme.Sidebar = {
],
},
],
'/algorithm': [
{
text: '算法',
items: [
{ text: '排序算法', link: '/algorithm/sorting' },
],
},
],
'/design-pattern': [
{
text: '设计模式',
items: [
{ text: '编程原则', link: '/design-pattern/principles' },
{ text: '常用设计模式', link: '/design-pattern/design-patterns' },
],
},
],
}
7 changes: 7 additions & 0 deletions src/docs/algorithm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: 算法
---

# 算法

常见算法的 JavaScript 实现。
146 changes: 146 additions & 0 deletions src/docs/algorithm/sorting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
title: 排序算法
---

# 排序算法

排序算法(sorting algorithm)用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用,因为有序数据通常能够被更高效地查找、分析和处理。

## 选择排序

选择排序(selection sort)的工作原理非常简单:开启一个循环,每轮从未排序区间选择最小的元素,将其放到已排序区间的末尾。

算法流程:

1. 初始状态下,所有元素未排序,即未排序(索引)区间为 [0, n-1]
2. 选取区间 [0, n-1] 中的最小元素,将其与索引 0 处的元素交换。完成后,数组前 1 个元素已排序。
3. 选取区间 [1, n-1] 中的最小元素,将其与索引 1 处的元素交换。完成后,数组前 2 个元素已排序。
4. 以此类推。经过 n-1 轮选择与交换后,数组前 n-1 个元素已排序。
5. 仅剩的一个元素必定是最大元素,无须排序,因此数组排序完成。

```js
/**
* 选择排序
* @param {number[]} nums
*/
function selectionSort(nums) {
const n = nums.length
// 外循环:未排序区间为 [i, n-1]
for (let i = 0; i < n - 1; i++) {
// 内循环:找到未排序区间内的最小元素
let k = i
for (let j = i + 1; j < n; j++) {
if (nums[j] < nums[k])
k = j // 记录最小元素的索引
}
// 将该最小元素与未排序区间的首个元素交换
[nums[i], nums[k]] = [nums[k], nums[i]]
}
}
```

## 冒泡排序

冒泡排序(bubble sort)通过连续地比较与交换相邻元素实现排序。这个过程就像气泡从底部升到顶部一样,因此得名冒泡排序。

冒泡过程可以利用元素交换操作来模拟:从数组最左端开始向右遍历,依次比较相邻元素大小,如果“左元素 > 右元素”就交换二者。遍历完成后,最大的元素会被移动到数组的最右端。

算法流程:

1. 首先,对 n 个元素执行“冒泡”,将数组的最大元素交换至正确位置
2. 接下来,对剩余 n-1 个元素执行“冒泡”,将第二大元素交换至正确位置
3. 以此类推,经过 n-1 轮“冒泡”后,前 n-1 大的元素都被交换至正确位置
4. 仅剩的一个元素必定是最小元素,无须排序,因此数组排序完成

```js
/**
* 冒泡排序
* @param {number[]} nums
*/
function bubbleSort(nums) {
const n = nums.length
// 外循环:未排序区间为 [0, n-1]
for (let i = 0; i < n - 1; i++) {
// 内循环:从左向右依次比较相邻元素
for (let j = 0; j < n - 1 - i; j++) {
// 如果“左元素 > 右元素”,则交换二者
if (nums[j] > nums[j + 1])
[nums[j], nums[j + 1]] = [nums[j + 1], nums[j]]
}
}
}
```

## 插入排序

插入排序(insertion sort)是一种简单的排序算法,它的工作原理与手动整理一副牌的过程非常相似。

具体来说,我们在未排序区间选择一个基准元素,将该元素与其左侧已排序区间的元素逐一比较大小,并将该元素插入到正确的位置。

算法流程:

1. 初始状态下,数组的第 1 个元素已完成排序
2. 选取数组的第 2 个元素作为 base ,将其插入到正确位置后,数组的前 2 个元素已排序
3. 选取第 3 个元素作为 base ,将其插入到正确位置后,数组的前 3 个元素已排序
4. 以此类推,在最后一轮中,选取最后一个元素作为 base ,将其插入到正确位置后,所有元素均已排序

```js
/* 插入排序 */
function insertionSort(nums) {
// 外循环:已排序元素数量为 1, 2, ..., n
for (let i = 1; i < nums.length; i++) {
const base = nums[i]
let j = i - 1
// 内循环:将 base 插入到已排序部分的正确位置
while (j >= 0 && nums[j] > base) {
nums[j + 1] = nums[j] // 将 nums[j] 向右移动一位
j--
}
nums[j + 1] = base // 将 base 赋值到正确位置
}
}
```

## 快速排序

快速排序(quick sort)是一种基于分治策略的排序算法,运行高效,应用广泛。

快速排序的核心操作是“哨兵划分”,其目标是:选择数组中的某个元素作为“基准数”,将所有小于基准数的元素移到其左侧,而大于基准数的元素移到其右侧。具体来说,哨兵划分的流程如图 11-8 所示。

算法流程:

1. 首先,对原数组执行一次“哨兵划分”,得到未排序的左子数组和右子数组
2. 然后,对左子数组和右子数组分别递归执行“哨兵划分”
3. 持续递归,直至子数组长度为 1 时终止,从而完成整个数组的排序

```js
function quickSort(nums, left, right) {
// 子数组长度为 1 时终止递归
if (left >= right)
return
// 哨兵划分
const pivot = partition(nums, left, right)
// 递归左子数组、右子数组
this.quickSort(nums, left, pivot - 1)
this.quickSort(nums, pivot + 1, right)
}

function partition(arr, left, right) { // 分区操作
const pivot = left // 设定基准值(pivot)
let index = pivot + 1
for (let i = index; i <= right; i++) {
if (arr[i] < arr[pivot]) {
swap(arr, i, index)
index++
}
}
swap(arr, pivot, index - 1)
return index - 1
}

function swap(arr, i, j) {
const temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
```
7 changes: 7 additions & 0 deletions src/docs/design-pattern.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: 设计模式
---

# 设计模式

更新中...
5 changes: 5 additions & 0 deletions src/docs/design-pattern/design-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: 常用设计模式
---

# 常用设计模式
37 changes: 37 additions & 0 deletions src/docs/design-pattern/principles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: SOLID 编程原则
---

# SOLID 编程原则

## 什么是 SOLID 原则

SOLID 是面向对象编程和设计(OOP & OOD)中的五个基本原则,它们能够帮助我们设计出更加健壮、可维护、可扩展的软件。

SOLID 是由 Robert C. Martin 在 2000 年代初提出的,它们是:

- **S**ingle Responsibility Principle(单一职责原则)
- **O**pen-Closed Principle(开闭原则)
- **L**iskov Substitution Principle(里氏替换原则)
- **I**nterface Segregation Principle(接口隔离原则)
- **D**ependency Inversion Principle(依赖倒置原则)

## 单一职责原则

一个 class 只做一件事情。一个函数只做一件事情。一个组件只做一件事情。

## 开闭原则

软件实体(类、模块、函数、组件等)应该对扩展开放,对修改关闭。

## 里氏替换原则

子类可以扩展父类的功能,但不能改变父类原有的功能。

## 接口隔离原则

不应该强迫客户端依赖它们不需要的接口。

## 依赖倒置原则

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
2 changes: 1 addition & 1 deletion src/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ hero:
link: https://github.com/hojas/frontend-guide
features:
- title: 前端框架
details: 框架实现原理...
details: Vue 实现原理...
- title: 前端工程化
details: 项目构建、代码质量、测试...
- title: 浏览器原理
Expand Down
14 changes: 12 additions & 2 deletions src/docs/vue/effect.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ export function onScopeDispose(fn: () => void) {
// 记录当前活跃的对象
export let activeEffect: ReactiveEffect | undefined

/**
* 创建一个 ReactiveEffect
* 用于收集依赖和触发依赖
*/
export class ReactiveEffect<T = any> {
// 是否为激活状态
active = true
Expand Down Expand Up @@ -325,9 +329,12 @@ export interface ReactiveEffectRunner<T = any> {

/**
* Registers the given function to track reactive updates.
* 注册给定的函数以跟踪响应式更新。
*
* The given function will be run once immediately. Every time any reactive
* property that's accessed within it gets updated, the function will run again.
* The given function will be run once immediately.
* 给定的函数将立即运行一次。每次任何响应式
* Every time any reactive property that's accessed within it gets updated, the function will run again.
* 在它内部访问的任何响应式属性被更新时,该函数将再次运行。
*
* @param fn - The function that will track reactive updates.
* @param options - Allows to control the effect's behaviour.
Expand All @@ -337,13 +344,16 @@ export function effect<T = any>(
fn: () => T,
options?: ReactiveEffectOptions,
): ReactiveEffectRunner {
// 如果 fn 是一个 ReactiveEffectRunner 则取出 effect 中的 fn
if ((fn as ReactiveEffectRunner).effect instanceof ReactiveEffect)
fn = (fn as ReactiveEffectRunner).effect.fn

// 创建一个 ReactiveEffect
const _effect = new ReactiveEffect(fn, NOOP, () => {
if (_effect.dirty)
_effect.run()
})
// 如果 options 存在则将 options 中的属性赋值给 _effect
if (options) {
extend(_effect, options)
if (options.scope)
Expand Down

0 comments on commit 4c20804

Please sign in to comment.