linux基本命令

查看版本、内核

查看系统版本lsp_release -a
查看内核版本uname -a

磁盘命令

查看磁盘空间 df -Th 以G为单位

linux目录&文件权限

进入根目录 cd /
文件目录及权限查看 ls -la

Snipaste_2021-04-20_15-10-14

  • 第一列代码文件的各种quanxian r-read w-write x-执行 r-remove
  • home目录相当于个人目录
  • etc 存放软件配置文件
  • sys 系统目录
  • usr 存放着系统的可执行文件
  • var 日志文件

CPU/内存/进程

查看运行中的进程top 可以按M看详细内存

Snipaste_2021-04-20_15-18-17

文档相关

  • 进入文件夹 cd 路径
  • pwd 显示当前文件夹
  • ls 查看文件下所有文件夹及文件
  • mkdir 新建文件夹
  • touch 新建文件
  • vi 文件名 使用vim编辑器打开文件
    • 输入 i 进入编辑-insert–模式
    • 按esc,然后输入:wq 保存退出
      • :q! 不对其进行保存
  • echo ‘1111’ >> test.txt 向文件内添加内容 1111
    • >单箭头是覆盖内容 >> 是添加内容
  • rm demo.txt 删除文件 提示是否删除 输入Y回车确认删除
  • rm -r demo/ 删除目录 提示是否删除 输入Y回车确认删除
  • rm -rf 强制删除 尽量不要使用

下载/解压/安装命令

以nodejs作为例子

  • 下载资源 wget https://cdn.npm.taobao.org/dist/node/v14.16.1/node-v14.16.1.tar.gz
    • 下载完成后,用ls查看
  • 解压资源,tar格式
    • tar zxvf node-v14.16.1.tar.gz
      • z 是.gz结尾的压缩文件
      • x 解压
      • v 显示所有解压过程
      • f 使用归档的名字
  • 压缩文件 tar zcvf 文件名 文件目录

查看进程

  • 查看进程 ps -ef | grep docker grep 相当于搜索,限定docker程序
    • root 后面跟着的数值是进程的id,杀死进程可以用 kill -9 id - 9表示强制终止

查看服务的命令

  • 查看服务状态

    • 例如查看ssh的运行状态,可以看到客户端通过ssh连接到服务器的记录 service sshd status
  • 停止服务

    • 例如停止ssh service sshd stop
  • 重启服务

    • 例如重启ssh service sshd restart

还有一个systemtcl命令

简单记录学习极客时间-数据与结构之美-排序相关的知识记录

tips

排序动画演示 可用手机app

时间复杂度 On2

冒泡排序

冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否 满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在 的位置,重复 n 次,就完成了 n 个数据的排序工作。

冒泡排序最不容易出错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const arr = [4,5,2,3,8,7,6,1,10]
function bubble(arr) {
const l = arr.length
for (let i = 0; i < l - 1; i++) {
// 没有交换发生 其实代表数组已经是有序的了
let flag = true;
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
const temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
// 有交换发生
flag = false;
}
}
if (flag) {
break;
}
}
}
bubble(arr)
console.log(arr);

插入排序

首先,我们将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有 一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排 序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程, 直到未排序区间中元素为空,算法结束。

插入排序写起来比较费劲,要注意的地方比较多

我自己的写的

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

教程写的,已经换成JavaScript,并加了注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function insert2(arr) {
const l = arr.length;
for (let i = 1; i < l; ++i) {
// 缓存当前处理的元素,减少交换次数
let value = arr[i];
// 对比前方排序区,查找插入的位置
let j = i - 1;
for ( ; j >= 0; --j) {
if (arr[j] > value) {
// 相当于把arr[j]向右移一位
// 第一次被覆盖掉的元素是当前处理元素,已在顶部缓存
// 第一次以后被覆盖的元素,实际上已经保存在右侧了
arr[j + 1] = arr[j]; // 数据移动
} else {
// 当前元素已经满足条件,不需要交换
break;
}
}
// 只在最后交换 j+1
arr[j + 1] = value; // 插入数据
}
}

选择排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function select(arr) {
const l = arr.length
let min, minIndex
for (let i = 0; i < l - 1; i++) {
// 起始位置
min = arr[i]
minIndex = i
for (let j = i+1; j < l; j++) {
if (arr[j] < min) {
min = arr[j]
minIndex = j
}
}
const temp = arr[i]
arr[i] = min
arr[minIndex] = temp
}
}
// select(arr)
console.log(arr);

时间复杂度 O nlogn

归并排序(稳定)

自己的代码,未优化的版本,找了好久的错误

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
// 归并排序
function merginInto(arr, l, r) {
if (l === r) {
return arr
}
const m = (l + r) >> 1 // 相当于除以2 取整
const left = arr.slice(l, m + 1)
const right = arr.slice(m + 1, r + 1)
return sortOrderArr(
left.length <= 1 ? left: merginInto(
// 忘了把边界改成新数组的了
left, 0, m
),
right.length <= 1 ? right : merginInto(
// 忘了把边界改成新数组的了
right, 0, r - (m + 1)
)
)
}
// 将两个有序数组合并
function sortOrderArr(arr1, arr2) {
const res = []
let i1 = i2 = 0, l1 = arr1.length, l2 = arr2.length
while (i1<l1 || i2<l2) {
if (i1 === l1) {
res.push(arr2[i2++])
} else if(i2===l2) {
res.push(arr1[i1++])
}else if (arr1[i1] <= arr2[i2]) {
res.push(arr1[i1++])
}else(
res.push(arr2[i2++])
)
}
return res
}

比较大众的写法,这个类似于二叉树的后续遍历

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
function mergeInto(arr, l, r) {
if (l >= r) {
return arr
}
const m = (l + r) >> 1;
// 原地排序 不需要返回
mergeInto(arr, l, m)
mergeInto(arr, m + 1, r)
// 两个已经拍好序的数组
mergeArr(arr, l, m, r)
return arr
}
function mergeArr(arr, l, m, r) {
const res = []
let i1 = l, i2 = m + 1, l1 = m+1, l2 = r+1
while (i1<l1 || i2<l2) {
if (i1 === l1) {
res.push(arr[i2++])
} else if(i2===l2) {
res.push(arr[i1++])
}else if (arr[i1] <= arr[i2]) {
res.push(arr[i1++])
}else(
res.push(arr[i2++])
)
}
for (let i = l,j=0; i < r+1; i++,j++) {
arr[i] = res[j]
}
}

快速排序(不稳定)

把比参考位置小的数移到左侧,大的移到右侧,如果不额外占用空间的话,比较难写

空间复杂度 O n 比较容易写出来

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
function fastSort(arr, l, r) {
if (l >= r) {
return arr
}
const p = fastSortHandle(arr, l, r)
fastSort(arr, l, p - 1)
fastSort(arr, p + 1, r)
return arr
}
// 快排辅助函数 分区 排序
function fastSortHandle(arr, l, r) {
// 占用了 On的空间
const res = []
//随机会好一点
const rdm = Math.floor(Math.random()*(r-l+1)+l)
const ref = arr[rdm]
let li = l, ri = r
for (let i = l; i < r + 1; i++) {
if (i === rdm) {
continue
}
if (arr[i] <= ref) {
res[li] = arr[i]
li++
} else {
res[ri] = arr[i]
ri--
}
}
res[ri] = ref
for (let i = l; i < r+1; i++) {
arr[i] = res[i]
}
return ri
}

O1空间复杂度左右归类的算法解释

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
function fastSortHandle(arr, l, r) {
// const rdm = Math.floor(Math.random()*(r-l+1)+l)
const rdm = 1
const ref = arr[rdm]
let j = l
for (let i = l; i < r + 1; i++) {
// 跳过
if (i === rdm) {
continue;
}
// 跳过
if (j === rdm) {
j++
}
if (arr[i] <= ref) {
[arr[i], arr[j]] = [arr[j], arr[i]]
j++
}
}
if (rdm < j) {
j--
}
[arr[rdm], arr[j]] = [arr[j], arr[rdm]]
return j
}

当选择一个随机数N,下标INDEX作为参照点的话,程序运行到关键点的情况应该如下图所示

左侧是小于等于N的数,右侧是大于N的数,但是N可能在左侧或者右侧

image-20211112105742642

最好的情况就是 把N的位置放进 j-1 和 j之间,但是这样时间复杂度较高,采取交换替代

如果N在左侧就和J-1交换,N在右侧就和J置换,如果不需要随机选择位置的话,N取最右侧元素,就不需要判断了

1
2
3
4
5
6
if (rdm < j) {
j--
}

[arr[rdm], arr[j]] = [arr[j], arr[rdm]]
return j

更好的随机数左右归类

力扣官方采取先用随机数与最后一位交换位置在进行常规方法,方法容易理解,也不容易出错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int partition(vector<int>& nums, int l, int r) {
int pivot = nums[r];
int i = l - 1;
for (int j = l; j <= r - 1; ++j) {
if (nums[j] <= pivot) {
i = i + 1;
swap(nums[i], nums[j]);
}
}
swap(nums[i + 1], nums[r]);
return i + 1;
}
int randomized_partition(vector<int>& nums, int l, int r) {
int i = rand() % (r - l + 1) + l; // 随机选一个作为我们的主元
swap(nums[r], nums[i]);
return partition(nums, l, r);
}

最后总结出JavaScript版本的快速排序

image-20211112112025117

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
function fastSort(arr, l, r) {
if (l >= r) {
return arr
}
const p = fastSortHandle(arr, l, r)
// -1 +1 可别忘 会死循环的
fastSort(arr, l, p - 1)
fastSort(arr, p + 1, r)
return arr
}
function fastSortHandle(arr, l, r) {
const rdm = Math.floor(Math.random() * (r - l + 1) + l)
// 与最后元素交换
swap(arr, rdm, r)

const ref = arr[r]
let j = l
for (let i = l; i < r; i++) {
if (arr[i] <= ref) {
swap(arr, i, j)
j++
}
}
swap(arr, r, j)
return j
}
function swap(arr, p, q) {
const temp = arr[p]
arr[p] = arr[q]
arr[q] = temp
}

归并排序和快速排序的区别与联系

image-20211112112920927

快排和归并用的都是分治思想,递推公式和递归代码也非常相似,那它们的区别在哪里呢?

image-20211112113107064可以发现,归并排序的处理过程是由下到上的,先处理子问题,然后再合并。

而快排正好相反,它的处理过程是由上到下的,先分区,然后再处理子问题。

归并排序虽然是稳定的、时 间复杂度为 O(nlogn) 的排序算法,但是它是非原地排序算法。我们前面讲过,归并之所以 是非原地排序算法,主要原因是合并函数无法在原地执行。快速排序通过设计巧妙的原地分区函数,可以实现原地排序,解决了归并排序占用太多内存的问题。

websocket–阮一峰

基础概念

websocket是一种网络传输协议,可在单个TCP连接上进行全双工通信,位于OSI模型的应用层。

特点

  • tcp连接,与http协议兼容
  • 双向通信,主动推送
  • 无同源限制,协议标识符是ws(加密wss)

应用场景

  • 聊天、消息、点赞
  • 直播评论(弹幕)
  • 游戏、协同编辑、基于位置的应用

ws常用前端库

  • ws(实现原生协议,特点:通用、性能强,定制性强)推荐
  • socket.io(向下兼容协议,特点:适配性强,性能一般)

基础示例 ws

web端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const ws = new WebSocket("ws://127.0.0.1:3000")
ws.onopen = () => {
// 向服务端发送数据
ws.send("hello server")
// 接收服务端发送的请求
}
ws.onmessage = (event) => {
console.log(event);
}

绑定多种时间,可以使用addEventListener
ws.addEventListener("message", function(event) {
var data = event.data;
// 处理数据
});

server端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const WebSocket = require('ws');
const wss = new WebSocket.Server({
port: 3000
})

wss.on("connection", (ws) => { // ws在这
ws.on("message", (res) => {
console.log("提供3000服务的");
console.log(res);
})
ws.send("来自客户端")
})

也可以
const ws = new WebSocket("ws://127.0.0.1:3000") // ws在这

ws.on("open", () => {
ws.send("hello web")
ws.on("message", (res) => {
console.log(res);
})
})

Snipaste_2021-04-23_10-08-28

koa中文网 有比较详细的介绍
详细可运行的配置在 个人git项目

和文章略有出入(很多插件都有更新的版本)

koa常用的中间件

  • kao-router 路由相关 接口必备
  • koa-combine-routers 当路由文件较多时,合并路由,只需要一个app.use就可以搞定
  • koa-body 解析前端发送的数据,支持
    • can handle requests such as:
      • multipart/form-data
      • application/x-www-urlencoded
      • application/json
      • application/json-patch+json
      • application/vnd.api+json
      • application/csp-report
      • text/xml
    • option for patch to Koa or Node, or either
    • file uploads
    • body, fields and files size limiting
  • Koa-json 美化json
  • @koa-cors 跨域请求
  • koa-views 渲染页面
  • koa-helmet 前端安全 设置安全头
  • koa-send 静态文件服务中间件 可以实现下载功能,也是koa-static的依赖
  • Koa-static 静态资源服务器
  • koa-compose 整合中间件 ompose[midleware, ….] 替代带多次 use()

使用webpack 开发 koa 项目

  • 首先需要安装webpack webpack-cli npm install webpack webpack-cli -D

  • 安装几个webpack plugin

    • clean-webpack-plugin 清理dist目录
    • webpack-node-externals 对node_modules文件做排除处理(貌似是为了nodejs准备的)
  • 安装babel相关的plugin或loader

    • @babel/core
    • @babel/node
    • @babel/preset-env
    • babel-loader
  • 书写webpack.config.js

    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
    const path = require("path")
    const nodeExcternals = require("webpack-node-externals")
    const { CleanWebpackPlugin } = require("clean-webpack-plugin")
    const webpackconfig = {
    target: "node",
    mode: "development",
    entry: {
    server: path.join(__dirname, "./src/index.js")
    },
    output: {
    filename: "[name].bundle.js",
    path: path.join(__dirname, "./dist")
    },
    devtool: {
    "eval-source-map"
    },
    modules: {
    rules: [
    {
    test: /\.(js|jsx)$/,
    use: {
    loader: "babel-loader"
    },
    exclude: [path.join(__dirname, "/node-modules")]
    }
    ]
    },
    externals: [nodeExcternals()],
    plugins: [
    new CleanWebpackPlugin(),
    new webpack.DefinePlugin({
    'process.env': {
    NODE_ENV: JOSN.stringfy(process.env.NODE_ENV) (就算是字符串还是需要)类似于“‘production’”
    }
    })
    ],
    node: {
    console: true,
    global: true,
    process: true,
    __filename: true,
    __dirname: true,
    Buffer: true,
    setImmediate: true,
    path: true ?webapck 5 好像没了
    }
    }
  • 新建 .babelrc

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    presets: [
    [
    "@babel/preset-env",
    {
    targets: {
    node: "current"
    }
    }
    ]
    ]
    }
  • 打包项目 npx webpack

  • 启动项目 npx babel-node ./src/index.js

    • 如果需要热更新,安装nodemon

    • 然后 npx nodemon --exec babel-node ./src/index.js

      1
      2
      --watch 监听哪些文件的变化,当变化的时候自动重启
      --exec 配置运行命令
  • 拆分webpack配置 对应开发环境及生产环境

    • 需要webpack-merge插件进行webpack配置合并
    • cross-env 设置环境变量,保证跨平台正确的环境变量
    • terser-webpack-plugin 压缩代码
  • 建立三个webapck.config.env.js文件

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
 base
const path = require("path")
const nodeExcternals = require("webpack-node-externals")
const webpack = require('webpack');
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const webpackconfig = {
target: "node",
mode: "development",
entry: {
server: path.join(__dirname, "./src/index.js")
},
output: {
filename: "[name].bundle.js",
path: path.join(__dirname, "./dist")
},
modules: {
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: "babel-loader"
},
exclude: [path.join(__dirname, "/node-modules")]
}
]
},
externals: [nodeExcternals()],
plugins: [
new CleanWebpackPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JOSN.stringfy(process.env.NODE_ENV) // (就算是字符串还是需要)类似于“‘production’”
}
})
],
node: {
console: true,
global: true,
process: true,
__filename: true,
__dirname: true,
Buffer: true,
setImmediate: true,
path: true // ?webapck 5 好像没了
}
}
dev
const webpackMerge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.config.base');
const webpackConfig = webpackMerge(baseWebpackConfig, {
mode: 'development',
devtool: "eval-source-map",
stats: {
children: false // 关闭日志消息
}
})

module.exports = webpackConfig;
prod
const webpackMerge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.config.base');
const TerserPlugin = require('terser-webpack-plugin');

const webpackConfig = webpackMerge(baseWebpackConfig, {
mode: 'production',
stats: {
children: false, // 关闭日志消息
warnings: false
},
optimization: {
minimizer: [new TerserPlugin({
terserOptions: {
compress: {
warnings: false,
drop_console: false, //注释掉console
dead_code: true,
drop_debugger: true //注释掉debugger
},
output: { // 最新的貌似没这属性
comments: false,
beautify: false
},
mangle: true // 混淆
},
parallel: true, // 并行化执行
sourceMap: false // 使用源映射将错误消息位置映射到模块(这会减慢编译速度)
})],
splitChunks: { // 视频里配的不太好,仅做示例吧
cacheGroups: {
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 3,
enforce: true
}
}
}
},
})

module.exports = webpackConfig;

作废 可以以计算机基础下的git文章进行拓展,这篇文章作废掉

学习网址

这可能是学习git操作最好的网址了 ——— learngit

tips

一个本地仓库可以对应多个远端仓库
git remote add origin xxx.git // 将本地仓库与远端建立联系 origin是仓库别名 可以自定义
git remote add origin1 xxx.git // 将本地仓库与另一个远端建立联系
如果都使用origin push将一起推送至两个仓库
如果增加-u指令 则使用 git push 默认推送到设置的仓库,不使用默认则是 git push origin master

git图示

image-20210426132629619

git命令简单介绍,后期会补全

git add

可将该文件添加到暂存区。

1
git add [file1] [file2] ...

添加指定目录到暂存区,包括子目录:

1
git add [dir]

添加当前目录下的所有文件到暂存区:

1
git add .

git add -u <==> git add –update
提交所有被删除和修改的文件到数据暂存区

git add .
提交所有修改的和新建的数据暂存区

git add -A <==>git add –all
提交所有被删除、被替换、被修改和新增的文件到数据暂存区

撤销git add

撤销上次add

1
git checkout .  撤销add操作,不可恢复

单独撤销某个文件 对应vscode中的取消暂存文件

1
git reset a.txt

上面的这个命令是一个简写,实际上reset命令的完整写法如下:

1
git reset --mixed HEAD a.txt

其中,mixed是一个模式(mode)参数,如果reset省略这个选项的话默认是mixed模式;HEAD指定了一个历史提交的 hash 值;a.txt指定了一个或者多个文件。

该命令的自然语言描述是:不改变work dir中的任何数据,将stage区域中的a.txt文件还原成HEAD指向的commit history中的样子。就相当于把对a.txt的修改从stage区撤销,但依然保存在work dir中,变为unstage的状态。

描述的不咋滴

git commit

将暂存区内容添加到仓库中

1
git commit -m '一些描述'

再简单提一些常见场景, 比如说commit完之后,突然发现一些错别字需要修改,又不想为改几个错别字而新开一个commithistory区,那么就可以使用下面这个命令:

1
git commit --amend

这样就是把错别字的修改和之前的那个commit中的修改合并,作为一个commit提交到history区。

工作区命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+ git clone 从远端仓库拉取代码
- git init 初始化
- git remote 远端建立连接
- git pull 拉取远端代码,并与本地合并
git log 查看提交记录 按空格持续加载 Q退出
git log 可以显示所有提交过的版本信息,不包括已经被删除的 commit 记录和 reset 的操作
git reflog 可以查看**本地所有**的所有操作记录(包括分支、包括已经被删除的 commit 记录和 reset 的操作),适合找回
git config
git config --global --list 查看全局的git配置
git config --global user.name xxxx 设置全局user name
git config --global user.email xx@xx 设置全局user.email
1、仓库级别 local 【优先级最高】
2、用户级别 global【优先级次之】
3、系统级别 system【优先级最低】
git status 工作区 暂存区 远端是否需要拉取等信息
git restore 指令使得在工作空间但是不在暂存区的文件撤销更改
git restore --staged 的作用是将暂存区(add)的文件从暂存区撤出,但不会撤销修改
git commit -m "msg" 暂存区提交到本地仓库
git push

git push 命令用于从将本地的分支版本上传到远程并合并。

命令格式如下:远程分支如果不存在,则会创建一个分支

1
git push <远程主机名> <本地分支名>:<远程分支名>

如果本地分支名与远程分支名相同,则可以省略冒号:

1
git push <远程主机名> <本地分支名>

如果当前分支与多个主机存在追踪关系,则可以使用 -u 参数指定一个默认主机,这样后面就可以不加任何参数使用git push

1
git push -u origin master 

当遇到这种情况就是不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机,这时需要 -all 选项

1
git push --all origin 

实例演示

以下命令将本地的 master 分支推送到 origin 主机的 master 分支。

1
git push origin master

相等于:

1
git push origin master:master

如果本地版本与远程版本有差异,但又要强制推送可以使用 –force 参数:

1
git push --force origin master

删除主机的分支可以使用 –delete 参数,以下命令表示删除 origin 主机的 master 分支:

1
git push origin --delete master

略本地分支名,也可以删除指定的远程分支

1
git push origin :master
Git fetch

简单的来说 git pull = git fetch + git merge

git fetch 命令:

1
git fetch <远程主机名> //这个命令将某个远程主机的更新全部取回本地

如果只想取回特定分支的更新,可以指定分支名:

1
git fetch <远程主机名> <分支名> //注意之间有空格

最常见的命令如取回origin 主机的master 分支:

1
git fetch origin master

取回更新后,会返回一个FETCH_HEAD ,指的是某个branch在服务器上的最新状态,我们可以在本地通过它查看刚取回的更新信息:

1
git log -p FETCH_HEAD

img

可以看到返回的信息包括更新的文件名,更新的作者和时间,以及更新的代码(19行红色[删除]和绿色[新增]部分)。我们可以通过这些信息来判断是否产生冲突,以确定是否将更新merge到当前分支。

代码合并示例

1
2
3
4
5
6
7
8
9
10
11
## 在本地新建一个temp分支,并将远程origin仓库的master分支代码下载到本地temp分支;
git fetch origin master:temp

## 比较本地代码与刚刚从远程下载下来的代码的区别;
git diff temp

## 合并temp分支到本地的master分支;
git merge temp

## 如果不想保留temp分支,删除;
git branch -D temp

stage暂存区命令

1
git stash 贮藏 详细看下边
git stash

stash命令可用于临时保存和回复修改,可跨分支

*注:在未add之前才能执行stash!!!!*

  • git stash [save message]
    保存,save为可选项,message为本次保存的注释
  • git stash list
    所有保存的记录列表
  • git stash pop stash@{num}
    恢复,num是可选项,通过git stash list可查看具体值。只能恢复一次
  • git stash apply stash@{num}
    恢复,num是可选项,通过git stash list可查看具体值。可回复多次
  • git stash drop stash@{num}
    删除某个保存,num是可选项,通过git stash list可查看具体值
  • git stash clear
    删除所有保存

master命令

1
git reset --hard commitID 回退版本 相当于删除中间的版本
Git diff命令的四种用法

git diff 比较「暂存区」与「工作区」之间的差异。

git diff commitID 比较「给定提交 ID」与「工作区」的差异。

git diff --cached commitID 比较「暂存区」与「给定提交 ID」的差异

git diff commit1 commit2 比较指定的两次提交 「commit1」与 「commit2」的差异

选项 --stat 显示有多少行发生变化,简洁的展示差异

git reset

git reset 通过把分支记录回退几个提交记录来实现撤销改动

git reset HEAD~1 回退到上次提交(撤销commit),但是文件修改后的内容还是存在于工作区的

远程仓库做出的修改并没有回退哦,要记住

此时通过git log和 git reflog对比就能看出两条命令的明显区别了

image-20210427154024280

image-20210427154043396

Git Revert

虽然在你的本地分支中使用 git reset 很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的!revert 之后就可以把你的更改推送到远程仓库与别人分享啦

git revert HEAD相当于相当于把上次提交的内容还原覆盖到工作区中,提交记录会增加一个,而不是减少,详细见下一节的示意

reset 和 revert对比

初始状态,切记HEAD~1和HEAD 不能混用,有意想不到的结果

image-20210427154540480

git reset HEAD~1

image-20210427154611065

git revert HEAD

image-20210427154652039

分支命令

git branch

一般用于分支的操作,比如创建分支,查看分支等等,

  git branch 不带参数:列出本地已经存在的分支,并且在当前分支的前面用”*”标记

  git branch -r 查看远程版本库分支列表

  git branch -a 查看所有分支列表,包括本地和远程

  git branch dev 创建名为dev的分支,创建分支时需要是最新的环境,创建分支但依然停留在当前分支

  git branch -d dev 删除dev分支,如果在分支中有一些未merge的提交,那么会删除分支失败

git branch -D dev:强制删除dev分支

git branch -vv 可以查看本地分支对应的远程分支

  git branch -m oldName newName 给分支重命名

git branch -f main HEAD~3 强制修改分支位置 向上移动3个位置

git branch -f main commitID 强制修改分支位置到指定commit

Git checkout

git checkout master 将分支切换到master

 git checkout -b master 如果分支存在则只切换分支,若不存在则创建并切换到master分支

在这里只介绍分支相关的命令

Git 合并分支的两种方式

image-20210426180103988

image-20210426180149291

我们在切换分支,和新建分支的时候,有没有想过,这些操作操作背后的工作原理是怎样的呢?最大的功臣就是.git目录下的HEAD引用,下面有对HEAD的详细解释

Git rebase

第二种合并分支的方法是 git rebase。Rebase 实际上就是取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去。

Rebase 的优势就是可以创造更线性的提交历史,这听上去有些难以理解。如果只允许使用 Rebase 的话,代码库的提交历史将会变得异常清晰。

这个用图示比较清晰

image-20210426201823042
image-20210426201909176

image-20210426201938207

Git flow

两种常见的模型

image-20210426183300352

image-20210426183643468

git HEAD

可以把HEAD理解成一个指针,HEAD指针通常会指向一个分支,如下图所示

image-20210427143641740

HEAD–>master–>commitID,通常情况下,HEAD会一直跟随着当前分支,并指向分支,而分支则指向当前最新的commit

还有一种 detached HEAD的状态,使用git checkout C3即可以把HEAD指针指向C3

还有一种相对引用的方式,git checkout main^*3 或者 git checkout main^^^ 或者 git checkout main~3 或者使用 git checkout HEAD^^^,这几种方法都是向父节点方向移动

image-20210427144448741

如果此时进行commit,git的返回信息会提示我们,You are in ‘detached HEAD’ state.(你现在处于’分离头’状态)。然后会从C3节点出现一个新的节点,HEAD会指向新的节点,可以简单的理解为匿名分支(图和上边不一样,仅做示意)

image-20210427145825615

我们现在有两个选择,如下:

  • 丢弃这个匿名分支

    • 直接检出到任何一个别的分支,就相当于放弃了这些提交
  • 保留这个匿名分支

    • 创建一个名为newtest的分支来保存这些提交
      • git branch newtest c7 这种方式HEAD并未指向newtest,而是指向了C7,仍是指针分离状态
      • git checkout -b newtest HEAD指向了newtest,newtest指向了C7
  • 与现存分支,参考分支命令章节

git checkout

git checkout命令用于切换分支或恢复工作树文件,也可以指定HEAD指针的位置

除了分支命令中介绍的使用方法,还可以进行HEAD操作,详细见上一小节

解决方案最好去官网查看

简短的记录一下步骤

  • 申请阿里云服务器

  • 创建秘钥对,然后绑定到实例

  • 运行长命令

    • ssh -i {ff6b954c664adf1ba473194b3b620b7d .pem ~/.ssh/alien-linux.pem} {root}@{39.106.100.189}
    • 应该会出现权限问题
  • 运行命令,获取权限 chmod 400 .pem文件的完整路径 chmod 400 ~/.ssh/alien-linux.pem

  • 配置config文件 vim ~/.ssh/config

    1
    2
    3
    4
    5
    Host Aliyun #自定义别名
    HostName 39.106.100.189 #ssh服务器公网IP
    Port 22 #ssh服务器端口,默认22
    User root #ssh服务器用户名,默认root
    IdentityFile ~/.ssh/alien-linux.pem #秘钥文件路径
  • 这样差不多就ok了

在阿里云安装docker

docker-install

1
2
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

然后 需要安装 docker-compose 查找linux安装方法,mac docker客户端集成了docker-compose
compose

1
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

然后需要给脚本执行权限

1
sudo chmod +x /usr/local/bin/docker-compose

安装mongo

下载mongo

docker pull mongo

如果失败的话,可以配置中国镜像, 编写/etc/docker/daemon.json

1
2
3
4
5
6
{
"registry-mirrors": ["https://<my-docker-mirror-host>"]
}
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}

然后重启docker systemctl restart docker.service

运行mongo

docker run -d --name some-mongo -p 10050:27017 mongo

  • -d 表示后台运行
  • -p 定义端口 10050:27017 容器端口27017映射到宿主机10050
  • –name 命名
  • mongo 可以接:tag 比如 mongo:4 版本4

检查docker下边运行的镜像 docker ps,如下图所示

Snipaste_2021-04-20_10-22-34

关闭防火墙

简单粗暴法

ubuntu:service ufw stop

Centos service firewalld stop

B格高的方法,放行特定端口

ubuntu: ufw allow Port端口号

centos:firewall-cmd --zone=public --add-port=10050/tcp --permanent –permanent 永久生肖

服务器提示 FirewallD is not running 未开启防火墙,先把防火墙开启(不知道有无必要),systemctl start firewalld

然后继续执行上一条命令,输出success,然后运行firewall-cmd --reload,重启生效

现在验证是否可以访问,通过robo 3T 或者 Navicat连接测试,需要修改阿里云安全组策略,要不然无法连接

安装nodejs

1.wget https://nodejs.org/dist/v6.10.3/node-v6.10.3-linux-x64.tar.xz //下载最新的稳定版 v6.10.3 到本地

(最新 https://nodejs.org/dist/v14.15.1/node-v14.15.1-linux-x64.tar.xz

2.tar xvJf node-v6.10.3-linux-x64.tar.xz //下载完成后, 将其解压

3.mv node-v6.10.3-linux-x64 /usr/local/node-v6 //将解压的 Node.js 目录移动到 /usr/local 目录下

4.ln -s /usr/local/node-v6/bin/node /bin/node //配置 node 软链接到 /bin 目录

5.ln -s /usr/local/node-v6/bin/npm /bin/npm //下载 node 的压缩包中已经包含了 npm , 我们只需要将其软链接到 bin 目录下即可

6.echo ‘export PATH=/usr/local/node-v6/bin:$PATH’ >> /etc/profile //将/usr/local/node−v6/bin目录添加到PATH′>>/etc/profile//将/usr/local/node−v6/bin目录添加到PATH 环境变量中可以方便地使用通过 npm 全局安装的第三方工具

7.source /etc/profile 有时候需要更新

第六、七步可能不需要,试一下npm -v 能不能ok,如果不可以的话,在运行第六步

docker的基本命令

一、基本命令

docker version查看docker版本
docker info查看docker详细信息
docker –help查看docker命令

二、镜像命令

docker images查看docker镜像

img

PEPOSITORY:镜像的仓库源
TAG:镜像的标签
IMAGE ID:镜像ID
CREATED:镜像创建时间
SIZE:镜像大小
 同一个仓库源可以有多个TAG,表示这个仓库源的不同版本,我们使用REPOSITORY:TAG来定义不同的镜像。如果不指定一个镜像的版本标签,例如只使用tomcat,docker将默认使用tomcat:latest镜像
docker images -a列出本地所有的镜像
docker images -p只显示镜像ID
docker images –digests显示镜像的摘要信息
docker images –no-trunc显示完整的镜像信息

img

docker search tomcat从Docker Hub上查找tomcat镜像

img

STARS:关注度
docker search -s 30 tomcat从Docker Hub上查找关注度大于30的tomcat镜像
docker pull tomcat从Docker Hub上下载tomcat镜像。等价于:docker pull tomcat:latest
docker commit -m “提交的描述信息” -a “作者” 容器ID 要创建的目标镜像名称:[标签名]提交容器使之成为一个新的镜像。
如:docker commit -m “新的tomcat” -a “lizq” f9e29e8455a5 mytomcat:1.2
docker rmi hello-world从Docker中删除hello-world镜像
docker rmi -f hello-world从Docker中强制删除hello-world镜像
docker rmi -f hello-world nginx从Docker中强制删除hello-world镜像和nginx镜像
docker rmi -f $(docker images -p)通过docker images -p查询到的镜像ID来删除所有镜像

三、容器命令

docker run [OPTIONS] IMAGE根据镜像新建并启动容器。IMAGE是镜像ID或镜像名称
OPTIONS说明:
 –name=“容器新名字”:为容器指定一个名称
 -d:后台运行容器,并返回容器ID,也即启动守护式容器
 -i:以交互模式运行容器,通常与-t同时使用
 -t:为容器重新分配一个伪输入终端,通常与-i同时使用
 -P:随机端口映射
 -p:指定端口映射,有以下四种格式:
  ip:hostPort:containerPort
  ip::containerPort
  hostPort:containerPort
  containerPort
docker ps列出当前所有正在运行的容器
docker ps -a列出所有的容器
docker ps -l列出最近创建的容器
docker ps -n 3列出最近创建的3个容器
docker ps -q只显示容器ID
docker ps –no-trunc显示当前所有正在运行的容器完整信息
exit退出并停止容器
Ctrl+p+q只退出容器,不停止容器
docker start 容器ID或容器名称启动容器
docker restart 容器ID或容器名称重新启动容器
docker stop容器ID或容器名称停止容器
docker kill 容器ID或容器名称强制停止容器
docker rm 容器ID或容器名称删除容器
docker rm -f 容器ID或容器名称强制删除容器
docker rm -f $(docker ps -a -q)删除多个容器
docker logs -f -t –since –tail 容器ID或容器名称查看容器日志
如:docker logs -f -t –since=”2018-09-10” –tail=10 f9e29e8455a5
 -f : 查看实时日志
 -t : 查看日志产生的日期
 –since : 此参数指定了输出日志开始日期,即只输出指定日期之后的日志
 –tail=10 : 查看最后的10条日志
docker top 容器ID或容器名称查看容器内运行的进程
docker inspect 容器ID或容器名称查看容器内部细节
docker attach 容器ID进到容器内
docker exec 容器ID进到容器内
docker cp 容器ID:容器内的文件路径 宿主机路径从容器内拷贝文件到宿主机.
如:docker cp f9e29e8455a5:/tmp/yum.log /root

svg-captcha

图形验证码插件

nodemailer

邮件服务依赖包

moment.js

date操作库

接口文档管理

showDoc

总结一下方便记忆

  • in 操作符 所有属性都可以访问到,下面就不提及in操作符了
  • 继承属性
    • 只有for in 可以访问
  • 不可枚举属性
    • 只有Object(Reflect).getOwnPropertyXXXX静态方法可以访问到
方法 不可枚举属性 继承属性 symbol属性 自身属性
in
for…in
JSON.stringfy()
Object.assign()
… 扩展运算符
Object.keys()、Object.values()、Object.entries()
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols(obj)
Reflect.ownKeys(obj)
Object.getOwnPropertyDescriptor(s)
Object.hasOwn(s)
Object.prototype.hasOwnProperty(s)

下面是验证代码,首先创建一个测试对象,拥有

  • self自身属性
  • parent继承属性
  • sym symbol属性
  • noEnumerable 不可枚举属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const parent = {
parent: 'parent'
}
const sym = Symbol("sym")
// 很重要的一个点
// 在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中
const child = {
self: 'self',
[sym]: "Symbol"
}
Object.setPrototypeOf(child, parent)

Object.defineProperty(child, 'noEnumerable', {
enumerable: false,
value: 0
})

测试代码如下,在nodejs 14+环境下运行

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
console.log("\n for ... in \n");

for (const key in child) {
console.log(key);
}

console.log("\n in \n");

console.log('parent' in child);
console.log('self' in child);
console.log('noEnumerable' in child);
console.log(sym in child);

console.log("\n Object.keys \n");

for (const key of Object.keys(child)) {
console.log(key);

}

console.log("\n Object.getOwnPropertyNames \n");

console.log(Object.getOwnPropertyNames(child));

console.log("\n Object.getOwnPropertySymbols \n");

console.log(Object.getOwnPropertySymbols(child));

console.log("\n Reflect.ownKeys \n");

console.log(Reflect.ownKeys(child));

console.log("\n JSON.stringify \n");

console.log(JSON.stringify(child));
console.log(JSON.parse(JSON.stringify(child)));

console.log("\n Object.assign \n");

console.log(Object.assign(child));
console.log(Object.assign({}, child));

console.log("\n 扩展运算符 ... \n");

console.log({ ...child });

console.log("\n Object.getOwnPropertyDescriptors() \n");

console.log(Object.getOwnPropertyDescriptors(child));

运行结果如下

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
 for ... in 

self
parent

in

true
true
true
true

Object.keys

self

Object.getOwnPropertyNames

[ 'self', 'noEnumerable' ]

Object.getOwnPropertySymbols

[ Symbol(sym) ]

Reflect.ownKeys

[ 'self', 'noEnumerable', Symbol(sym) ]

JSON.stringify

{"self":"self"}
{ self: 'self' }

Object.assign

{ self: 'self', [Symbol(sym)]: 'Symbol' }
{ self: 'self', [Symbol(sym)]: 'Symbol' }

扩展运算符 ...

{ self: 'self', [Symbol(sym)]: 'Symbol' }

Object.getOwnPropertyDescriptors()

{
self: {
value: 'self',
writable: true,
enumerable: true,
configurable: true
},
noEnumerable: { value: 0, writable: false, enumerable: false, configurable: false },
[Symbol(sym)]: {
value: 'Symbol',
writable: true,
enumerable: true,
configurable: true
}
}

对象的详细方法见js高程第四版第八章

对象ES6之后方法的简单总结

本章介绍 Object 对象的新增方法。

Object.is()

用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致

不同之处只有两个:一是+0不等于-0,二是NaN等于自身

1
2
3
4
5
+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

Object.assign()

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

1
2
3
4
5
6
7
const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:, c:3}

注意点

Object.assign拷贝的属性是有限制的,

  • 浅拷贝

  • 只拷贝源对象的自身属性(不拷贝继承属性)

  • 不拷贝不可枚举的属性enumerable: false

  • Object.assign只能进行值的复制,无法复制取值函数,将求值后再复制

  • 数组的处理

    Object.assign([1, 2, 3], [4, 5])
    // [4, 5, 3]

属性名为 Symbol 值的属性,也会被Object.assign拷贝。

完整的复制对象

1
2
3
4
5
6
7
8
9
10
11
12
// 写法一 不能复制不可枚举 不能复制取值函数 
const clone2 = Object.assign(
Object.create(Object.getPrototypeOf(obj)),
obj
);

// 写法二 完美复制
const clone3 = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)

Object.getOwnPropertyDescriptors()

ES5 的Object.getOwnPropertyDescriptor(obj, prop)方法会返回某个对象属性的描述对象(descriptor)。ES2017 引入了Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。

描述对象MDN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const obj = {
foo: 123,
get bar() { return 'abc' }
};

Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }

原型链方法

JavaScript 语言的对象继承是通过原型链实现的。ES6 提供了更多原型对象的操作方法。

Object.setPrototypeOf()

Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的原型对象(prototype),返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法

1
2
3
4
5
// ==格式==
Object.setPrototypeOf(object, prototype)

// 用法
const o = Object.setPrototypeOf({}, null);

该方法等同于下面的函数。

1
2
3
4
function setPrototypeOf(obj, proto) {
obj.__proto__ = proto;
return obj;
}

Object.getPrototypeOf()

该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。

1
Object.getPrototypeOf(obj);

遍历方法

Object.keys() Object.values Object.entries

都是不可遍历 对象自身的(不含继承的)所有可遍历(enumerable)属性的键名

ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名

1
2
3
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]

ES2017 引入了跟Object.keys配套的Object.valuesObject.entries,作为遍历一个对象的补充手段,供for...of循环使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };

for (let key of keys(obj)) {
console.log(key); // 'a', 'b', 'c'
}

for (let value of values(obj)) {
console.log(value); // 1, 2, 3
}

for (let [key, value] of entries(obj)) {
console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}

Object.fromEntries()

Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象

1
2
3
4
5
Object.fromEntries([
['foo', 'bar'],
['baz', 42]
])
// { foo: "bar", baz: 42 }

该方法的主要目的,是将键值对的数据结构还原为对象,因此特别适合将 Map 结构转为对象。

1
2
3
4
5
6
7
8
9
// 例一
const entries = new Map([
['foo', 'bar'],
['baz', 42]
]);

Object.fromEntries(entries)
// { foo: "bar", baz: 42 }

Object.create()

  • 语法
    Object.create(proto, [propertiesObject])
  • 参数
    • proto : 必须。表示新建对象的原型对象,即该参数会被赋值到目标对象(即新对象,或说是最后返回的对象)的原型上。该参数可以是null, 对象, 函数的prototype属性 (创建空的对象时需传null , 否则会抛出TypeError异常)。
    • propertiesObject : 可选。 如果没有指定为 undefined,则是要添加到新创建对象的不可枚举(默认)属性(即其自身的属性,而不是原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。
  • 返回值
    在指定原型对象上添加新属性后的对象

框架偏爱

很多框架源码作者使用它来初始化一个新的对象,难道是最佳实践?
原因有二

  • 通过Object.create(null)创建出来的对象,没有任何属性,显示No properties。我们可以将其当成一个干净的 map 来使用,自主定义 toString,hasOwnProperty等方法,并且不必担心将原型链上的同名方法被覆盖。
  • {…}创建的对象,使用for in遍历对象的时候,会遍历原型链上的属性,带来性能上的损耗。使用Object.create(null)则不必再对其进行遍历了。
    两种方式的比较

案例说明

1)创建对象的方式不同

new Object() 通过构造函数来创建对象, 添加的属性是在自身实例下。
Object.create() es6创建对象的另一种方式,可以理解为继承一个对象, 添加的属性是在原型下。

1
2
3
4
5
6
7
8
9
10
11
12
13
// new Object() 方式创建
var a = { rep : 'apple' }
var b = new Object(a)
console.log(b) // {rep: "apple"}
console.log(b.__proto__) // {}
console.log(b.rep) // {rep: "apple"}

// Object.create() 方式创建
var a = { rep: 'apple' }
var b = Object.create(a)
console.log(b) // {}
console.log(b.__proto__) // {rep: "apple"}
console.log(b.rep) // {rep: "apple"}

Object.create()方法创建的对象时,属性是在原型下面的,也可以直接访问 b.rep // {rep: “apple”} ,
此时这个值不是吧b自身的,是它通过原型链proto来访问到b的值。

2)创建对象属性的性质不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 创建一个以另一个空对象为原型,且拥有一个属性p的对象
o = Object.create({}, { p: { value: 42 } })

// 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:
o.p = 24
o.p
//42

o.q = 12
for (var prop in o) {
console.log(prop)
}
//"q"

delete o.p
//false

Object.create() 用第二个参数来创建非空对象的属性描述符默认是为false的,而构造函数或字面量方法创建的对象属性的描述符默认为true。看下图解析:

propertiesObject

3)创建空对象时不同

propertiesObject

当用构造函数或对象字面量方法创建空对象时,对象时有原型属性的,即有_proto_;
当用Object.create()方法创建空对象时,对象是没有原型属性的。

1
2
Object.create()   //Object prototype may only be an Object or null
Object.create('') //Object prototype may only be an Object or null

4)proto 属性

JavaScript 的对象继承是通过原型链实现的。ES6 提供了更多原型对象的操作方法。
__proto__属性,用来读取或设置当前对象的prototype对象。目前只有浏览器环境必须部署有这个属性,其他运行环境不一定要部署,因此不建议使用这个属性,而是使用下面这些来 **Object.setPrototypeOf()(写操作,它是 ES6 正式推荐的设置原型对象的方法)、Object.getPrototypeOf()(读操作)、Object.create()**(生成操作)代替。

应用案例,原型属性的继承
1
2
3
4
5
6
7
8
9
10
var triangle = {a: 1, b: 2, c: 3};

function ColoredTriangle() {
this.color = 'red';
}

//ColoredTriangle.prototype = triangle; //ColoredTriangle.prototype.constructor === ColoredTriangle// false
Object.assign(ColoredTriangle.prototype, triangle) //ColoredTriangle.prototype.constructor === ColoredTriangle// true

var c = new ColoredTriangle();

打印出 实例c 看看结构是怎样的

propertiesObject

其中 color 属性在实例上,而其他的原型上。
现在来拷贝一个 实例 c2

1
2
3
var c2 = Object.assign({},c)
console.log(c2.color); //red
console.log(c2.a); //undefined

因为 Object.assign 是不能拷贝到继承或原型上的方法的。所以 实例c2 没有 a 这个属性。那要怎么要才能拷贝到原型上的方法呢?

  • 第一种方法
1
2
3
4
5
6
7
var originProto = Object.getPrototypeOf(c);
var originProto2 = Object.create(originProto);
var c2 = Object.assign(originProto2, c);
//var c2 = Object.assign(Object.create(Object.getPrototypeOf(c)), c)

console.log(c2.color); // red
console.log(c2.a); // 1

这样就实现了原型属性的拷贝。
Object.getPrototypeOf(c) 既 originProto 得到的是原型上的 //{a: 1, b: 2, c: 3};
Object.create(originProto) 既 originProto2 既是创建了一个 {a: 1, b: 2, c: 3} 在原型上的新对象;
Object.assign(originProto2, c) 在源对象originProto2 上合并对象 c (只能合并实例属性);

  • 第二种方法(推荐)
1
2
3
4
5
var c = new ColoredTriangle();
var c2 = Object.create(Object.getPrototypeOf(c), Object.getOwnPropertyDescriptors(c));

console.log(c2.color); // red
console.log(c2.a); // 1

可以把Object.create()的参数理解为:第一个参数是放在新对象的原型上的,第二个参数是放在新对象的实例上的。
Object.getPrototypeOf() 得到的是 c 对象的原型,然后作为第一个参数,所以会在新对象c2 的原型上。
Object.getOwnPropertyDescriptors() 得到是 c 对象自身的可枚举属性,作为第二个参数,放在 c2 的实例上。

为什么说推荐这个方法呢?因为Object.assign() 方法不能正确拷贝 get ,set 属性。

统计数组或字符串重复数字数量的时候使用哈希表

  • 这种方法性能好,而且容易理解,且不易出错
  • 不能用来统计连续出现的次数
1
2
3
4
5
6
7
8
9
10
11
// 相同牌出现次数Map
let timeMap = new Map();

// 遍历牌
deck.forEach(num => {
// 统计每张牌出现的次数
timeMap.set(num, timeMap.has(num) ? timeMap.get(num) + 1 : 1);
});

// Map.prototype.values()返回的是一个新的Iterator对象,所以可以使用扩展运算符(...)来构造成数组
let timeAry = [...timeMap.values()];

相关题目 卡片分组 leetcode 914

  • 计算连续出现次数的方法(自己写的,很垃圾,看见好的会更新)
1
2
3
4
5
6
7
8
9
10
11
12
"1122233334444"
var arr = s.split("");
var arrSame = [];
var length = 1
for (var i = 0; i < arr.length; i++) {
if(arr[i]!==arr[i+1]||(!arr[i+1])){
arrSame.push(length)
length = 1
}else{
length++
}
}

相关题目 计数二进制子串 leetcode 696

辗转相除法 欧几里得算法

1997 / 615 = 3 (余 152)
615 / 152 = 4(余7)
152 / 7 = 21(余5)
7 / 5 = 1 (余2)
5 / 2 = 2 (余1)
2 / 1 = 2 (余0)

1
2
3
4
5
6
7
function ojld(chushu, yushu) {
if(chushu % yushu === 0){
return yushu
}else{
return ojld(yushu, chushu % yushu)
}
}

单调栈的讲解与应用

0%