vue-router导航守卫

官方文档

  • 导航 表示路由正在发生改变

  • 守卫方法的三个参数: to: Route(目标的路由) ; from: Route(离开的路由) ; next: Function() (是否执行目标路由)

    • next(): 执行下一个钩子
    • next(false): 中断当前导航。如果url改变重置到from
    • next('/') / next({path:'/'}): 跳转到不同地址
    • next(error): 导航终止&将错误传递给router.onError()

      全局前置守卫

      router.beforeEach

1
2
3
4
5
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
// ...
})

路由独享的守卫

在路由配置的守卫 beforeEnter , (对于进入该路由进行控制)

1
2
3
4
5
6
7
8
9
10
11
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})

组件内部守卫

对出入该组件路由进行控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},

beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}

完整生命周期

  1. 导航被触发。
  2. 失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter。
  8. 调用全局beforeResolve 守卫。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

纽约时间比加州时间早三个小时

纽约时间比加州时间早三个小时,
New York is 3 hours ahead of California,

但加州时间并没有变慢。
but it does not make California slow.

有人22岁就毕业了,
Someone graduated at the age of 22,

但等了五年才找到稳定的工作!
but waited 5 years before securing a good job!

有人25岁就当上CEO,
Someone became a CEO at 25,

却在50岁去世。
and died at 50.

也有人迟到50岁才当上CEO,
While another became a CEO at 50,

然后活到90岁。
and lived to 90 years.

有人单身,
Someone is still single,

同时也有人已婚。
while someone else got married.

欧巴马55岁就退休,
Obama retires at 55,

川普70岁才开始当总统。
but Trump starts at 70.

世上每个人本来就有自己的发展时区。
Absolutely everyone in this world works based on their Time Zone.

身边有些人看似走在你前面,
People around you might seem to go ahead of you,

也有人看似走在你后面。
some might seem to be behind you.

但其实每个人在自己的时区有自己的步程。
But everyone is running their own RACE, in their own TIME.

不用嫉妒或嘲笑他们。
Don’t envy them or mock them.

他们都在自己的时区里,你也是!
They are in their TIME ZONE, and you are in yours!

生命就是等待正确的行动时机。
Life is about waiting for the right moment to act.

所以,放轻松。
So, RELAX.

你没有落后。
You’re not LATE.

你没有领先。
You’re not EARLY.

在你自己的时区里,一切安排都准时。
You are very much ON TIME, and in your TIME ZONE.

webpack

在网页中会引用哪些常见的静态资源?

  • JS
    • .js .jsx .coffee .ts(TypeScript 类 C# 语言)
  • CSS
    • .css .less .sass .scss
  • Images
    • .jpg .png .gif .bmp .svg
  • 字体文件(Fonts)
    • .svg .ttf .eot .woff .woff2
  • 模板文件
    • .ejs .jade .vue

网页中引入的静态资源多了以后有什么问题

  1. 网页加载速度慢, 因为 我们要发起很多的二次请求;
  2. 要处理错综复杂的依赖关系

如何解决上述两个问题

  1. 合并、压缩、精灵图、图片的Base64编码
  2. 可以使用之前学过的requireJS、也可以使用webpack可以解决各个包之间的复杂依赖关系

webpack概念

webpack 是一个用于 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个 依赖图,此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle

  1. 文件引入依赖进行分类构成依赖图
  2. 不同模块分别编译代码块(chunk)
  3. 将各类代码块打包 合并
  4. 生成并输出静态资源(bundle.js)

webpack核心概念

webpack基础配置

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
/* 
webpack.config.js webpack的配置文件
告诉webpack要干什么(运行webpack指令时、会加载里面的配置)
*/

// 用来拼接绝对路径的方法
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
// 入口文件 默认值是 ./src/index.js
entry: './src/index.js',

// 输出路径 默认值是 ./dist/main.js
output: {
// 输出文件名
filename: 'built.js', // 'js/built.js'

// 输出路径 [ __dirname: node.js的变量、代表当前文件目录绝对路径 ]
path: resolve(__dirname, 'build')
},

// loader的配置
// 下载 -> 使用
module: {
rules: [
// 详细的loader配置
// test 配置哪些文件 ;use 使用哪些loader,多个用 [,,] 执行顺序从右到左

// 1. css-loader : 将css文件编译成commonjs模块加载到js中
// 2. style-loader : 创建style标签,将js中的样式资源插入进去,添加到head中生效
{
test: /\.css$/, use: ['style-loader', 'css-loader']
}
]
},

// plugins的配置
// 下载 -> 引入 -> 使用
plugins: [
// 详细的plugins配置
// 默认再输出文件夹内创建一个 index.html 文件,自动打包输出的所有资源(js/css)
// new HtmlWebpackPlugin(),

// 需要有dom结构的html
new HtmlWebpackPlugin({
// 将./src/index.html 的内容复制一遍 再引入打包的资源 再输出文件夹内输出
template: './src/index.html'
}),
],

// mode模式配置
mode: 'development', // mode:'production'

// devServer-开发服务器 自动编译
// 启动制令 npx webpack-dev-server 仅在内存中编译打包 不会输出
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true, // 压缩
port: 8080,
open: true, // 自动打开浏览器
}

}

Flutter组件封装

Flutter组件封装

封装控件的子项目

通过来定义子项目,再通过List泛型来声明变量以供使用

以BottomNavigationBar(底部导航栏)为例

基本用法

1
2
3
4
5
6
7
8
9
10
bottomNavigationBar: BottomNavigationBar(
...
items: [
BottomNavigationBarItem(title: Text('首页'), icon: Icon(Icons.home)),
BottomNavigationBarItem(title: Text('书籍'), icon: Icon(Icons.book)),
BottomNavigationBarItem(
title: Text('我的'), icon: Icon(Icons.perm_identity)),
],
)

封装

  1. BottomNavigationBarItem的内容封装成类,使用的时候需要传入label、iconPath、activeIconPath三个参数
    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
    class NavigationIconView {
    // item
    final BottomNavigationBarItem item;
    // title
    final String label;
    // icon path
    final IconData iconPath;
    // actived icon path
    final IconData activeIconPath;

    NavigationIconView({@required this.label, @required this.iconPath, @required this.activeIconPath})
    : item = BottomNavigationBarItem(
    title: Text(
    label,
    style: TextStyle(color: Colors.black),
    ),
    icon: Icon(
    iconPath,
    ),
    activeIcon: Icon(
    activeIconPath,
    size: 30,
    ),
    );
    }

  1. 在页面中声明变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 限定类型,使用 <> 泛型来指定 List 中保存的数据类型为NavigationIconView
    List<NavigationIconView> _navigationIconViews;

    ...

    // 页面初始化的时候对数据进行赋值,按要求传入参数
    _navigationIconViews = [
    NavigationIconView(label: '资讯', iconPath: Icons.new_releases, activeIconPath: Icons.new_releases),
    NavigationIconView(label: '动态', iconPath: Icons.palette, activeIconPath: Icons.palette),
    NavigationIconView(label: '发现', iconPath: Icons.find_replace, activeIconPath: Icons.find_replace),
    NavigationIconView(label: '我的', iconPath: Icons.person_pin, activeIconPath: Icons.person_pin)
    ];
  2. 在BottomNavigationBar控件中使用

    1
    2
    3
    4
    5
    bottomNavigationBar: BottomNavigationBar(
    ...
    // view.item => BottomNavigationBarItem(...)
    items: _navigationIconViews.map((view) => view.item).toList(),
    ),

封装一个控件

  1. 以Drawer(抽屉)为例,创建一个widget,并写入参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class MyDrawer extends StatelessWidget {
    final String headImagePath;
    final List meanuTitle;
    final List meanuIcons;
    const MyDrawer({Key key, @required this.headImagePath, @required this.meanuTitle, @required this.meanuIcons})
    : assert(headImagePath != null),
    assert(meanuTitle != null),
    assert(meanuIcons != null),
    super(key: key);

    @override
    Widget build(BuildContext context) {
    return Drawer(...)
    }
  2. 在页面中使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ...
    Widget build(BuildContext context) {
    return Scaffold(
    ...
    drawer: MyDrawer(
    headImagePath: 'assets/image/xxx.jpg',
    meanuIcons: _meanuIcons,
    meanuTitle: _meanuTitle,
    ),
    )
    }

2020年度总结

关键词:顺利、平淡

这一年实现了人生路上一个重要的转变
从学生转换成了打工人 hhh(苦笑
现在回想起来2020好像没做什么事情 过的糊里糊涂的

因为疫情原因在家呆了差不多半年、毕业答辩也是线上草草结束
还混了个优秀实习生...
挺不真实的、就这?我大学四年就完了?

七月中旬去了公司培训,体检身体状况还是不行,好在医生很仁慈
放了我一马,让我能顺利过关、顺利入职
公司给的专利 kpi 也‘一帆风顺’的通过了
12月29完成了转正答辩,70%的几率能混的个A。

好像也没什么值得说的了
从入职工作以来还挺适应的
实现财务自由的感觉真的挺好
对比其他同学无止尽的加班 我觉得我这份工作还行
虽然工资不算高 对我来说足够用了 还有富裕的可以存着
工作的强度和难度都能接受
周边的同事也不错 经常‘施舍’一些家乡特产给我
和同期进来的小伙伴玩的比较好 没实习的时候孤独
因为和项目组不在一个地方
省去了很多无用的会议 没人看着管着 美滋滋
四川这个地方也不错 周边还有好多想去的地方 也在慢慢筹备着

这样的生活已经很满足了 至少关键性的事情都是顺利进行的
虽然日子过得很平淡、但我也很享受这种平淡的生活
希望2021也能平安顺意。

路由传值

路由传值

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
// main.dart
import 'package:demo/pages/Form.dart';
import 'package:demo/pages/Search.dart';
import 'package:flutter/material.dart';
import 'pages/tabs/Tabs.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Tabs(),
routes: {
'/form': (context,{arguments}) => FormPage(arguments: {'id': 'form title'}),
'/search': (context) => SearchPage(),
},
);
}
}


// Form.dart
import 'package:flutter/material.dart';

class FormPage extends StatefulWidget {
final Map arguments;
FormPage({Key key, this.arguments}) : super(key: key);

@override
_FormPageState createState() => _FormPageState();
}

class _FormPageState extends State<FormPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Text('back'),
onPressed: () {
Navigator.of(context).pop();
},
),
appBar: AppBar(
title: Text(widget.arguments['id']),
),
body: Text('表单'),
);
}
}

请求参数问题

一个不好检查的bug

基本信息描述

1
2
一个弹框表单的编辑功能,编辑接口请求的时候
会对列表进行分类,以数组的方式存储在指定属性中

参数处理逻辑没问题,这就是错误不好找的原因

bug描述

编辑的时候参数列表数据 有!时!候! 会重复

原因

1
2
3
4
5
6
7

将接口参数放入页面的全局变量中存储
每次要发送请求之前对参数进行处理 然后进行接口请求

错误原因
① 每次编辑弹框开启的时候变量没有重置
② 请求失败的时候,变量没有重置,再次请求之前还是会处理一次数据 然后放进变量中导致重复

Dart基础语法

数据类型

变量声明

支持类型推断,可以不必指定类型

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
 /*
未初始化变量声明
dynamic、var、Object

数据类型
int / double / String / bool / List / Maps
*/
var name = 'doreen' // 再次赋值的时候类型需要一致
String name = 'sherry'

// 模板字符串 也可以是 """ ... """
String str = '''
hello,
thank you
'''
print( name +" "+ str )
// => print("$name $str")

int age = 13
bool flag = true / false (只能是这两个值)
var arr = ['aa'] // => var arr = new List() arr.add('aa')
// new List(<string>) 指定list的类型

// Maps类型和Object类似
var person = {
"name":"doreen",
"age":20
}
print(person['age']) // 不能通过person.age

var person = new Map() // new 可以省略
person["name"] = "sherry"

// const 定义之后不能被修改的变量
const PI = 3.14159

// final 基本与const一致
final PI = 3.14159

// 区别: final是惰性初始化,使用的时候才会被赋值 (需要通过方法 赋值给常量的时候只能用final)
final a = new DateTime.now() // √
const a = new DateTime.now() // ×

类型判断

is 关键词判断

1
if(str is String){ ... }

List常用属性和方法

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
属性:
.length 长度
.reversed 翻转(返回的字符串)
.isEmpty 是否为空
.isNotEmpty 是否不为空

方法:
.add() 增加一个
.addAll([]) 增加多个
.indexOf() 查找 传入具体值
.remove() 删除 传入具体值
.removeAt() 删除 传入具体索引
.fillRange(start,end,value) 修改(替换)
.insert(index,value) 指定位置插入
.insertAll(index,list) 指定位置插入List
.toList() 其他类型转换成List
.join() List转换成字符串
.split() 字符串转换成List
.forEach() 遍历
.map()
.where() 筛选符合条件的集合
.any() 有一个满足条件返回true
.every() 所有满足条件 返回true

myList.forEach((value){
print(value)
})
myList.map((value){
return value*2
})
myList.map((value){
return value>2
})
myList.any((value){
return value>2
})

Maps常用属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
属性:
.keys 获取所有key值
.values 获取所有value值
.isEmpty
.isNotEmpty

方法:
.remove(key) 删除指定key的数据
.addAll({...}) 合并映射 给映射内增加属性
.containsValue() 查看映射内的值 返回true/false
.forEach()
.map()
.where()
.any()
.every()

流程控制语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if(age>=18){
print('成年人');
}else{
print('未成年人');
}


for(var item in flyObject){
print(item);
}


for(int i=0;i<10;i++){
print(i);
}

while(i>10){
i-=1;
}

// switch case ,break,continue

函数

返回类型 方法名称 (参数,…){ 方法体 return 返回值; }

1
2
3
4
5
6
7
8
9
int fibonacci( int n ){
if( n==0 || n==1 ) return n;
return fibonacci(n-1) - fibonacci(n-2)
// 返回值类型为 数字
}

void fun(){
// 没有返回值 / 不指定返回值类型
}

可选参数

1
2
3
4
5
6
7
8
// 可选参数用 [] 表示,默认参数直接用赋值语句
String printUserInfo(String username,[String sex='女',int age]){
if(age!=null){
return '$username ----- $age ----- $sex';
}else{
return '$username';
}
}

可选命名参数

1
2
3
4
5
6
7
8
9
10
11

// 命名参数用 {} 表示,默认参数直接用赋值语句
String printUserInfo(String username,{ int age,String sex='女' }){
if(age!=null){
return '$username ----- $age ----- $sex';
}else{
return '$username';
}
}
// 通过 key: value 的方式填写参数
printUserInfo('doreen',age:20)

如果是List或者Map作为默认参数,赋值必须要用const
void add({List list = const [1,2,3] })

匿名函数

无参匿名

var fun = () => print(‘aaa’);

有参匿名

var fun = (String name) => print(name);

导入

通过import关键字来访问其他库中的内容

1
import 'demo.dart'

类(Class)

构造函数可以写多个
默认构造函数—实例化的时候调用 (一个)
命名构造函数—通过类调用 (多个)

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
/*
如果想要写成私有属性/方法
就需要把类单独抽离成一个文件 引入使用
并且在定义属性/方法的时候在名字前面加上 _
eg:
String _name;
void _getName(){}
*/
class People {
String name;
int age;

void getInfo(){
// print('$name---$age') =>
print('${this.name}---${this.age}')
}

void setInfo(int age) {
this.age = age;
}

// 默认构造函数,带有可以直接为成员变量赋值的语法糖
People(this.name,this.age)
/*
=>
People(name, age) {
this.name = name;
this.age = age;
}

// 赋默认值
People():name='doreen',age=10{}
*/

// 命名构造函数
People.now(){
print('我是命名构造函数')
}

}

void main(){
var p = new People('doreen', 24);
// People p = new People('jerry',9)

p.getInfo(); // doreen---24
p.setInfo(18);
p.getInfo(); // doreen---18
People.now(); // 我是命名构造函数
}

getter

直接通过访问属性的方式访问方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Rect {
num height;
num width;
Rect(this.height,this.width);

// 类似于计算属性
get area {
return this.height*this.width;
}

getArea(){
return this.height*this.width;
}
}

void main() {
Rect r = new Rect(10,9)
print('面积:${r.area}'); //get
print('面积:${r.getArea()}'); //方法
}

setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 class Rect {
num height;
num width;
Rect(this.height,this.width);

set areaHeight(value) {
this.height = value;
}

setHeight(value){
this.height = value;
}
}

void main() {
Rect r = new Rect(10,9)

r.areaHeight = 5;
print(r.height); // 5
r.setHeight(9);
print(r.height); // 9
}

static 静态成员

使用static关键字来实现类级别的变量和函数
静态方法不能访问非静态成员,非静态方法可以访问静态成员
实例化对象不能访问、只能通过类直接访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class People {
String name = 'doreen';
static int age = 10;
static void show() {
print(age); // 访问静态属性
// print(this.name); × 不能访问非静态属性
}
void printInfo() {
show(); // 不需要this
}
}

void main() {
var p = new People();
// p.show(); ×
print(People.age); // 10
People.show(); // 10
}

对象的操作符

? 条件运算符
as 类型转换
is 类型判断
.. 级联操作
?? 是否为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
People p;
p?.printInfo(); // 如果p为空就不执行

var p1;
p1 = '';
p1 = new Person();
(p1 as Person).printInfo();

print( p is People) // 判断对象是否属于xx类

var p = new Person()
p..name='doreen' // 直接换行不需要分号
..age=9
..printInfo();

/*
=>
p.name = 'doreen';
p.age = 9;
p.printInfo();
*/

bool flag;
flag ??= false; // flag为空则赋值false否则给原本值

继承

子类使用extends关键词来继承父类
子类会继承父类里面可见的属性和方法 但不会继承构造函数
子类能复写父类的方法 getter和setter

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
class Person {
String name='doreen';
num age=8;
void printInfo(){
print('${this.name}----${this.age}')
}
Person(this.name,this.age)
}

class Women extends Person {
String sex;
// 实例化属性时 赋值给父类
Women(String name, num age,this.sex) : super(name, age);

// override表示重写父类方法 建议写上
@override
void printInfo() {
// super.printInfo(); // print('${this.name}----${this.age}')
print('${this.name}--${this.sex}--${this.age}');
}

// 字类方法调用父类方法
void run(){
super.printInfo();
print('hhh')
}

}

抽象类

dart抽象类主要用于定义标准,字类可以继承抽象类,也可以实现抽象类接口

  1. 抽象类通过abstract关键字来定义
  2. dart中的抽象方法不能用abstract声明,dart中没有实体方法我们称为抽象方法
  3. 如果子类继承抽象类必须得实现里面的抽象方法
  4. 如果把抽象类当作接口实现的话必须得实现抽象类里面定义的所有属性和方法
  5. 抽象类不能被实例化,只有继承它的子类

extends抽象类和implements的区别:

  • 如果要复用抽象类型里面的方法,并且要用抽象方法约束子类的话我们就用extends继承抽象类
  • 如果只是把抽象类当作标准的话我们就用implements实现抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
abstract class Person {
eat(); // 父类定义抽象方法

// 公共(普通)方法
void printInfo(){
print('抽象类的普通方法')
}
}

class Women extends Person {
// 子类必须继承抽象方法,并实现
@override
eat() {
print('handle eat');
}
}

void main() {
// var a = new Person() × 抽象类无法直接被实例化
var doreen = new Women();
doreen.eat();
}

多态

允许将子类类型的指针赋值给父类类型的指针,同一个函数调用会有不同的执行效果
子类的实例赋值给父类的引用
多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现

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
abstract class Person {
eat(); // 抽象方法
}

class Women extends Person {
@override
eat() {
print('women eat');
}

run() {
print('run');
}
}

class Men extends Person {
@override
eat() {
print('men eat');
}
}

void main() {
Person doreen = new Women(); // 子类的实例赋值给父类的引用
Person jerry = new Men();
doreen.eat();
// doreen.run(); ×
jerry.eat();
}

接口

dart的接口没有interface关键字定义接口,而是普通类或抽象类都可以作为接口被实现,同样使用implements关键字进行实现
如果实现的类是普通类,会将普通类和抽象中的属性的方法全部需要覆写一遍
一般都是使用抽象类来实现接口

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
abstract class Db {
// 当作接口(约定、规范)
String uri;
add(String data);
delete();
}

// 父类型里面定义的子类必须按照父类的规范去实现
// 子类可以自己扩展属性或方法
class MySQL implements Db {
@override
String uri;

@override
add(String data) {
print('MySQL的add方法' + data);
}

@override
delete() {
print('MySQL的delete方法');
}
}

class Mongodb implements Db {
@override
String uri;

@override
add(String data) {
print('mongodb的add方法' + data);
}

@override
delete() {
print('mongodb的delete方法');
}
}

多个接口的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
abstract class A {
printA();
}

abstract class B {
printB();
}

// 获取A,B类的全部属性和方法
class C implements A, B {
@override
printA() {
print('A接口');
}

@override
printB() {
print('B接口');
}
}

mixins

mixins的使用条件:

  1. 作为mixins的类只能继承自Object,不能继承其他类
  2. 作为mixins的类不能有构造函数
  3. 一个类可以mixins多个mixins类
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
// class C extends A,B {}  × 可以获取多个接口但是不能实现多继承

// class A extends Aa {} × 作为mixins的类只能继承自Object,不能继承其他类
class A {
String name='a';
// A(this.name) × 作为mixins的类不能有构造函数
void printA() {
print('A');
}
}

class B {
void printB() {
print('B');
}
}

// 通过with取代extends 相当于继承了A和B
// A和B的顺序 后者会覆盖前者的同样的属性和方法
class C with A, B {}

// Person内可以有构造函数,并继承了三者的属性和方法
class C extends Person with A,B {}

void main() {
C c = new C();
c.printA();
c.printB();
}

泛型

解决类、接口、方法的复用性、以及不特定数据类型的支持(类型校验)

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
// T可以用任意字母代替 ,表示泛型(任意类型)
// T---① getData<T>(T----② value)
// ① 对返回的参数进行校验
// ② 对传入的参数进行校验

T getData<T>(T value) {
return value;
}
void main(){
// 在<>内指定参数类型
getData<String>('hello');
// getData<String>(123); ×

}

// 在类中使用泛型
class A<T>{
...
}
new A<String>()


// 在接口中使用泛型
abstract class Cache<T> {
getByKey(String key);
void setByKey(String key, T value);
}

// =>
abstract class ObjectCache {
getByKey(String key);
void setByKey(String key, Object value);
}
abstract class StringCache {
getByKey(String key);
void setByKey(String key, String value);
}

JavaScript数据结构和算法

数据结构

存储和组织数据的一种方式

数组(Array)

通常情况下用于存储一系列同一种数据类型的值

创建和初始化

  • new Array('a','b')
  • ['a','b']

常用操作

添加元素
  • 数组的末尾添加元素 arr.push(item)
  • 数组的首位插入一个元素 arr.unshift(item)
  • 指定位置插入元素 arr.splice(index,0,item)
删除元素
  • 数组的末尾删除一个元素 arr.pop()
  • 数组的首位删除一个元素 arr.shift()
  • 删除指定位置的元素 arr.splice(start,number)
1
2
3
let arr = [1,2,3,4]
arr.splice(1,2) // 起始位置,删除的个数
arr // [1,4]
修改元素
  • 修改指定索引的元素 arr.splice(index,1,item)
  • 修改指定索引的多个元素 arr.splice(index,number,item)
1
2
3
let arr = [1,2,3,4,5]
arr.splice(1,2,'qq','bb')
arr // [1,'qq','bb',4,5]

栈(Stack)

数组是一个线性结构,并且可以在数组的任意位置插入和删除元素。但是有时候,我们为了实现某些功能,必须对这种任意性加以限制。栈和队列就是比较常见的受限的线性结构。

栈(stack)是一种运算受限的线性表:

  • LIFO(last in first out)表示就是后进入的元素,第一个弹出栈空间
  • 其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底
  • 向一个栈插入新元素又称作进栈(入栈/压栈),它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素
  • 从一个栈删除元素又称作出栈(退栈),它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素


先进后出,后进先出

程序中的栈结构

  • 函数调用: A(B(C(D()))): 即 A 函数中调用 B,B 调用 C,C 调用 D;在 A 执行的过程中会将 A 压入栈,随后 B 执行时 B 也被压入栈,函数 C 和 D 执行时也会被压入栈。所以当前栈的顺序为:A->B->C->D(栈顶);函数 D 执行完之后,会弹出栈被释放,弹出栈的顺序为 D->C->B->A

  • 递归: 为什么没有停止条件的递归会造成栈溢出?比如函数 A 为递归函数,不断地调用自己(因为函数还没有执行完,不会把函数弹出栈),不停地把相同的函数 A 压入栈,最后造成栈溢出(Queue Overfloat)

栈结构实现

常用操作
  • push(item) 添加一个新元素到栈顶
  • pop() 移除栈顶元素(出栈)
  • peek() 返回栈顶的元素,不对栈做任何修改(该方法不会移除栈顶的元素,仅仅返回它)
  • isEmpty() 判断栈内是否有元素
  • size() 返回栈内元素个数
  • toString() 将栈结构的内容通过字符串的形式返回
JavaScript实现栈
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
class Stock {
constructor() {
this.items = []
}
// 进栈
push(item) {
this.items.push(item)
}

// 出栈,并返回该元素
pop() {
return this.items.pop()
}

// 返回当前栈顶元素(不改变栈)
peek() {
return this.items[this.items.length - 1]
}

// 判断栈是否为空
isEmpty() {
return this.items.length ? false : true
}

// 获取栈元素的个数
size() {
return this.items.length
}

// 以字符串的形式返回栈内元素
toString() {
let result = ''
this.items.forEach((item) => {
result += item + ''
})
return result
}
}

let stock = new Stock()
stock.push(1)
stock.push(1)
stock.push(2)
console.log(stock.pop()) // 2
console.log(stock.items) // [1,1]
console.log(stock.peek()) // 1
console.log(stock.isEmpty()) // false
console.log(stock.size()) // 2
console.log(stock.toString()) // 1 1

简单应用

栈结构实现十进制转二进制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function dec2bin(number) {
let stock = new Stock()
while (number) {
stock.push(number % 2)
number = Math.floor(number / 2)
}
let str = ''
while(!stock.isEmpty()){
str += stock.pop()
}
return str
}
console.log(dec2bin(10)) // 1010
console.log((10).toString(2)) //1010

队列(Queue)

是一种运算受限的线性表,特点:先进先出。(FIFO:First In First Out)

常用操作

  • enqueue(element) 向队尾添加一个/多个新的项
  • dequeue() 移除队列的第一项,并返回移除的元素
  • front() 返回队列中的第一个元素(最先被添加的,也是最早被移除的元素。不改变队列)
  • isEmpty() 是否有元素
  • size() 返回队列元素个数
  • toString() 将队列内容转换成字符串的形式

JavaScript实现队列

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
class Queue {
constructor() {
this.items = []
}
enqueue(element) {
this.items.push(element)
}
dequeue() {
return this.items.shift()
}
front() {
return this.items[0]
}
isEmpty() {
return !Boolean(this.items.length)
}
size() {
return this.items.length
}
toString() {
let str = ''
this.items.forEach(item => {
str += item + ' '
})
return str
}
}

let queue = new Queue
queue.enqueue(1)
queue.enqueue(2)
queue.enqueue(3)
console.log(queue.dequeue()) // 1
console.log(queue.front()) // 2
console.log(queue.isEmpty()) // false
console.log(queue.size()) // 2
console.log(queue.toString()) // 2 3

简单实现

击鼓传花游戏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function game(array, number) {
let queue = new Queue()
array.forEach(item => {
// 进入队列
queue.enqueue(item)
})

while (queue.size() > 1) {
for (let i = 0; i < number - 1; i++) {
// 不是number项的进入队尾
queue.enqueue(queue.dequeue())
}
// 删除number项
queue.dequeue()
}

// 获取留到最后一个的值
const end = queue.front()
return array.indexOf(end) // 返回值所在的索引
}

let arr = ['doreen', 'jerry', 'sherry', 'tom', 'amy', 'gaga']
console.log(arr[game(arr, 2)]) // amy

优先队列

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
class QueueElement {
// 元素和他的优先级
constructor(element, priority) {
this.element = element
this.priority = priority
}
}

class PriorityQueue extends Queue {
constructor() {
super()
}

enqueue(element, priority) {
const queueElement = new QueueElement(element, priority)
if (this.isEmpty()) {
this.items.push(queueElement)
} else {
let add = false
for (let i = 0; i < this.items.length; i++) {
if (this.items[i].priority > queueElement.priority) {
this.items.splice(i, 0, queueElement)
add = true; break
}
}
if (!add) {
this.items.push(queueElement)
}
}
}

toString() {
let str = ''
this.items.forEach(item => {
str += item.element + '-' + item.priority + ' '
})
return str

}


}

let queue = new PriorityQueue()
queue.enqueue(1, 1)
queue.enqueue(2, 0)
console.log(queue.toString()) // 2--0 1-1

链表(Linked List)

链表和数组

数组缺点
  • 数组的创建需要申请一段连续的内存空间(一整块内存),并且大小是固定的,当前数组不能满足容量需求时,需要扩容。 (一般情况下是申请一个更大的数组,比如 2 倍,然后将原数组中的元素复制过去)

  • 在数组开头或中间位置插入数据的成本很高,需要进行大量元素的位移。

链表

链表中的元素在内存中不必是连续的空间。链表的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(有些语言称为指针)组成。

单向链表

优点
  • 内存空间不必是连续的,可以充分利用计算机的内存,实现灵活的内存动态管理。
  • 链表在插入和删除数据时,时间复杂度可以达到 O(1),相对数组效率高很多。
    缺点
  • 访问任何一个位置的元素时,需要从头开始访问。(无法跳过第一个元素访问任何一个元素)
  • 无法通过下标值直接访问元素,需要从头开始一个个访问,直到找到对应的元素。
  • 虽然可以轻松地到达下一个节点,但是回到前一个节点是很难的。
常用操作
  • append(element) 链尾添加新项
  • insert(position,element) 在指定位置添加新项
  • get(position) 获取对应位置的元素
  • indexOf(element) 返回该元素在链表的索引,没有则为-1
  • update(position,element) 修改指定位置的元素
  • removeAt(position) 从链表的特定位置移除一项
  • remove(element) 从链表中移除一项
  • isEmpty()
  • size()
  • toString()
JavaScript实现单向链表
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
class Node {
constructor(data) {
this.data = data
this.next = null
}
}

class LinkedList {
constructor() {
this.length = 0
this.head = null
}
apped(data) {
let node = new Node(data)
if (!this.length) {
this.head = node
} else {
let nextNode = this.head
while (nextNode.next !== null) {
nextNode = nextNode.next
}
nextNode.next = node
}
this.length++
}

insert(position, element) {
if (this.length >= position && position >= 0) {

let newNode = new Node(element)
let currentNode = this.head
let nextNode;
if (position === 0) {
this.head = newNode
this.head.next = currentNode
} else {
while (position > 1) {
currentNode = currentNode.next
position--
}
nextNode = currentNode.next
currentNode.next = newNode
newNode.next = nextNode
this.length++
}
return this.head
} else {
return false
}
}

get(position) {
if (position < 0 || position >= this.length) return false
if (position === 0) return this.head.data
let currentNode = this.head
while (position > 0) {
currentNode = currentNode.next
position--
}
return currentNode.data
}

indexOf(element) {
let index = 0
let currentNode = this.head
while (currentNode) {
if (currentNode.data === element) {
return index
}
currentNode = currentNode.next
index++
}

return -1
}

update(position, element) {
if (position >= this.length || position < 0) return false
let currentNode = this.head
let newNode = new Node(element)

while (position > 0) {
currentNode = currentNode.next
position--
}

currentNode.data = newNode.data
return currentNode
}

removeAt(position) {

if (position >= this.length || position < 0) return false
let currentNode = this.head

if (position === 0) {
this.head = this.head.next
} else {
let previousNode;

while (position > 0) {
previousNode = currentNode
currentNode = currentNode.next
position--

}
previousNode.next = currentNode.next
}

this.length--
return currentNode
}

remove(element) {
this.removeAt(this.indexOf(element))
}

isEmpty() {
return this.length === 0
}

size() {
return this.length
}

toString() {
let str = ''
let currentNode = this.head
while (currentNode) {
str += currentNode.data + ' '
currentNode = currentNode.next
}
return str
}
}
let link = new LinkedList()
link.apped(2)
link.apped(5)
link.insert(2, 4) // 2 -> 5 -> 4
console.log(link.get(1)) // 5
console.log(link.indexOf(5)) // 1
console.log(link.removeAt(1)) // 删掉了:5 链表:2 -> 4
link.remove(2)
console.log(link.toString()) // 4

双向链表

  • 既可以从头遍历到尾,也可以从尾遍历到头。
  • 链表相连的过程是双向的。实现原理是一个节点既有向前连接的引用,也有一个向后连接的引用。
  • 双向链表可以有效的解决单向链表存在的问题。
  • 双向链表缺点:
    • 每次在插入或删除某个节点时,都需要处理四个引用,而不是两个,实现起来会困难些。
    • 相对于单向链表,所占内存空间更大一些。
      双向链表结构
      常用操作
  • append(element) 向链表尾部追加一个新元素。
  • insert(position, element) 向链表的指定位置插入一个新元素。
  • get(position) 获取指定位置的元素。
  • indexOf(element) 返回元素在链表中的索引。如果链表中没有该元素就返回 -1。
  • update(position, element) 修改指定位置上的元素。
  • removeAt(position) 从链表中的删除指定位置的元素。
  • remove(element) 从链表删除指定的元素。
  • isEmpty() 如果链表中不包含任何元素,返回 trun,如果链表长度大于 0 则返回 false。
  • size() 返回链表包含的元素个数,与数组的 length 属性类似。
  • forwardString() 返回正向遍历节点字符串形式。
  • backwordString() 返回反向遍历的节点的字符串形式。
JavaScript实现双向链表
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
class Node {
constructor(data) {
this.data = data
this.next = null
this.prev = null
}
}

class LinkedList {
constructor() {
this.length = 0
this.head = null
this.tail = null
}
append(element) {
let newNode = new Node(element)

if (this.length === 0) {
this.head = newNode
this.tail = newNode
} else {
this.tail.next = newNode
newNode.prev = this.tail
this.tail = newNode
}
this.length++
}

insert(position, element) {

if (position < 0 || position > this.length) return false
let newNode = new Node(element)
let currentNode = this.head
let previousNode;
if (position === 0) {
if (this.head === null) {
this.head = newNode
this.tail = newNode
} else {
newNode.next = currentNode
currentNode.prev = newNode
this.head = newNode
}

} else if (position === this.length) {
newNode.prev = this.tail
this.tail.next = newNode
this.tail = newNode

} else {
while (position > 0) {
previousNode = currentNode
currentNode = currentNode.next
position--
}
newNode.prev = previousNode
newNode.next = currentNode
previousNode.next = newNode
currentNode.prev = newNode

}
this.length++
return currentNode
}

get(position) {
if (position < 0 || position >= this.length) return false
if (position === 0) {
return this.head.data
}
if (position === this.length - 1) {
return this.tail.data
}
let currentNode = this.head
while (position > 0) {
currentNode = currentNode.next
position--
}
return currentNode.data

}

indexOf(element) {
let currentNode = this.head
let index = 0
if (!this.length) return -1

while (currentNode) {
if (currentNode.data === element) {
return index
}
currentNode = currentNode.next
index++
}
return -1
}

update(position, element) {
if (position < 0 || position >= this.length) return false
let currentNode = this.head
if (position === 0) {
this.head.data = element
}
while (position > 0) {
currentNode = currentNode.next
position--
}
currentNode.data = element
return currentNode
}

removeAt(position) {
if (position < 0 || position >= this.length) return false
let currentNode = this.head
let prevNode;
if (position === 0) {
if (this.length === 1) {
this.head = null
this.tail = null
} else {
this.head = this.head.next
this.head.prev = null
}
} else if (position === this.length - 1) {
this.tail = this.tail.prev
this.tail.next = null
} else {
while (position > 0) {
prevNode = currentNode
currentNode = currentNode.next
position--
}
prevNode.next = currentNode.next
currentNode.next.prev = prevNode
}
this.length--

}

remove(element) {
return this.removeAt(this.indexOf(element))
}
isEmpty() {
return this.length === 0
}
size() {
return this.length
}

forwardString() {
let str = ''
let currentNode = this.head
while (currentNode) {
str += currentNode.data + ' '
currentNode = currentNode.next
}
return str
}
backwordString() {
let str = ''
let currentNode = this.tail
while (currentNode) {
str += currentNode.data + ' '
currentNode = currentNode.prev
}
return str
}
}

let link = new LinkedList()
link.append(1)
link.append(2)
link.insert(2, 3)
// 1 2 3
link.update(2, 5) // 1 2 5
console.log(link.forwardString()) // 1 2 5
console.log(link.backwordString()) // 5 2 1

集合

集合比较常见的实现方式是哈希表,这里使用 JavaScriptObject 进行封装

集合特点

  • 集合通常是由一组无序的不能重复的元素构成
  • 数学中常指的集合中的元素是可以重复的,但是计算机中集合的元素不能重复
  • 集合是特殊的数组
    • 特殊之处在于里面的元素没有顺序,也不能重复
    • 没有顺序意味着不能通过下标值进行访问,不能重复意味着相同的对象在集合中只会存在一份

常用操作

  • add(value)
  • remove(value)
  • has(value)
  • clear() 移除所有项
  • size()
  • values() 返回一个包含集合中所有值的数组

集合之间的操作

  • 并集:对于给定的两个集合,返回一个包含两个集合中所有元素的新集合。
  • 交集:对于给定的两个集合,返回一个包含两个集合中共有元素的新集合。
  • 差集:对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合。
  • 子集:验证一个给定集合是否是另一个集合的子集。

JavaScript实现集合

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
71
72
73
74
75
76
77
class Set {
constructor() {
this.items = {}
}
has(value) {
return this.items.hasOwnProperty(value)
}
add(value) {
if (this.has(value)) return false
this.items[value] = value
return true
}
remove(value) {
if (this.has(value)) return false
delete this.items[value]
}
clear() {
this.items = {}
}
size() {
return Object.keys(this.items).length
}
values() {
return Object.values(this.items)
}

// 集合间的操作

// union() 求两个集合的并集
union(other) {
let unionSet = new Set()
for (let value of this.values()) {
unionSet.add(value)
}
for (let value of other.values()) {
unionSet.add(value)
}
return unionSet
}

// intersection() 求两个集合的交集
intersection(other) {
let intersectionSet = new Set()
for (let value of this.values()) {
if (other.has(value)) {
intersectionSet.add(value)
}
}
return intersectionSet
}

// difference() 差集
difference(other) {
let differenceSet = new Set()
for (let value of this.values()) {
if (!other.has(value)) {
differenceSet.add(value)
}
}
return differenceSet
}

// subset() 子集
subset(other) {
for(let value of this.values()){
if(!other.has(value)){
return false
}
}
return true
}
}
let set = new Set()
set.add('abc')
set.add('abc')
set.add('123')
console.log(set) // {'abc':'abc','123':'123'}

字典

特点

  • 字典存储的是键值对,主要特点是一一对应
  • key是不能重复且无序的,而Value可以重复

常用操作

  • set(key,value) 向字典中添加新元素
  • remove(key) 通过使用键值来移除对应数据
  • has(key)
  • get(key)
  • clear()
  • size()
  • keys()
  • values()

JavaScript代码实现

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
class Map {
constructor() {
this.items = {}
}

has(key) {
return this.items.hasOwnProperty(key)
}

set(key, value) {
this.items[key] = value
}

remove(key) {
if (this.has(key)) delete this.items[key]
}

get(key) {
if (this.has(key)) return this.items[key]
return undefined
}

clear() {
this.items = {}
}

size() {
return Object.keys(this.items).length
}

keys() {
return Object.keys(this.items)
}

values() {
return Object.values(this.items)
}
}

散列[哈希]表(Hash)

哈希表的结构就是数组,但它神奇之处在于对下标值的一种变换,这种变换我们可以称之为哈希函数,通过哈希函数可以获取 HashCode

树(Tree)

堆(Heap)

算法

解决问题的方法/步骤逻辑

排序算法

冒泡排序

  • 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  • 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  • 针对所有的元素重复以上的步骤,除了最后一个。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

1
2
3
4
5
6
7
8
9
10
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
let m = arr[j]
let n = arr[j + 1]
if (n < m) [arr[j + 1], arr[j]] = [m, n]
}
}
return arr
}

快速排序

对冒泡排序的改进

设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,
然后将所有比它小的数都放到它左边,所有比它大的数都放到它右边,这个过程称为一趟快速排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function quickSort(arr) {
let array = arr
function sort(array) {
let left = [], center, right = []
if (array.length <= 1) return array
center = array[0]
for (let i = 1; i < array.length; i++) {
if (array[i] > center) {
right.push(array[i])
} else {
left.push(array[i])
}
}
return [...sort(left), center, ...sort(right)]
}
return sort(array)
}

选择排序

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

1
2
3
4
5
6
7
8
9
10
11
12
13
function searchSort(arr) {
for (let i = 0; i < arr.length; i++) {
let m = i
for (let j = i + 1; j < arr.length; j++) {
if (arr[m] > arr[j]) {
m = j // 找到最小值所在的位置
}
}
// 位置置换
[arr[m], arr[i]] = [arr[i], arr[m]]
}
return arr
}

插入排序

在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序

1
2
3
4
5
6
7
8
9
10
11
12
13
function insertSort(arr) {
var j;
for (var i = 1; i < arr.length; i++) {
var temp = arr[i]
j = i - 1
while (temp < arr[j] && j >= 0) {
arr[j + 1] = arr[j]
j--
}
arr[j + 1] = temp
}
return arr;
}

代码段

filter多条件过滤

前端实现过滤 过滤条件为空的字段需要删除

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
let condition = {
supplierCode: '123',
materialCode: '333'
}
let tableData = [
{
supplierCode: '123',
materialCode: '333'
},
{
supplierCode: '123456666',
materialCode: '333'
}
]

let queryTableData = []

function getQueryList() {
let tempFilter = {};
for (let key in condition) {
if (condition[key]) {
tempFilter[key] = condition[key];
}
}
queryTableData = tableData.filter((item) => {
let flag = false;
for (let i in tempFilter) {
if (tempFilter[i]) {
if (item[i] === tempFilter[i]) {
flag = true;
} else {
flag = false;
break;
}
}
}
if (flag) {
return item;
}
});
}

  • Copyrights © 2019-2023 John Doe
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信