ubuntu 18.04 vscode root运行配置

在开发上一直想用root权限进行开发,避免遇到一些恶心的东西,正好之前的虚拟机坏了,这次重新弄了一个root登录的ubuntu虚拟机,但是在安装vscode遇到了vscode必须设置–user-data-dir,导致syncsetting插件不能正常执行的问题,于是忙乎了一阵,差不多解决了问题,这篇文章记录一下

修改启动文件

通过which命令查看vscdoe的code命令在哪里

which code

可以看到输出了/usr/bin/code,这个地址,然后在/usr/bin目录使用

ls -al | grep code

可以看到输出了lrwxrwxrwx 1 root root 24 11月 16 14:58 code -> /usr/share/code/bin/code/usr/bin/code软链接指向了/usr/share/code/bin/code

进入这个启动文件

vim /usr/share/code/bin/code

就可以看到下面的内容了

第一段就是在判断root权限下,需要输入--user-data-dir才能继续正常运行

这里把这段屏蔽掉,让他不判断,因为后面会添加默认的参数

文件最后可以看出是在传递参数,这里在最后加伤我们的参数就可以了~~~

图片里的路径可以自己设置哦,到现在在命令行里面调用code命令已经可以正常使用了

修改桌面快捷方式

除了命令行以外,还需要修改桌面图标的内容,因为桌面图标的启动路径不在/usr/share/code/bin/code而是直接指向/usr/share/code/code运行文件,所以我们只需要修改一下指向就行了

修改/usr/share/applications/code.desktop/usr/share/applications/code-url-handler.desktop文件中的Exec属性的路径就行了

这样就可以在root身份下愉快的和vscode玩耍啦~~~

END

2018-11-16 完成

2018-11-16 立项

CSS 绘制扇形

CSS绘制扇形的基础可以从一个圆形出发,一个正方形用border-radius: 50%;很简单就能构造出来。

1
2
3
4
5
6
7
8
9
<style>
.circle{
width: 50px;
height: 50px;
background: red;
border-radius: 50%;
}
</style>
<div class="circle"></div>

结合4边border就可以很简单的构建出90deg,180deg,270deg的扇形了。

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
<style>
[class|="sector"]{
width: 0;
height: 0;
border: 25px solid red;
border-radius: 50%;
}

.sector-90{
border-top-color: transparent;
border-bottom-color: transparent;
border-left-color: transparent;
}

.sector-180{
border-bottom-color: transparent;
border-left-color: transparent;
}

.sector-270{
border-bottom-color: transparent;
}
</style>
<div class="sector-90"></div>
<div class="sector-180"></div>
<div class="sector-270"></div>

任意角度的扇形

CSS现在有了clip-path这个属性,可以控制裁减,所以任意角度的扇形都可以弄出来啦~~~

1
2
3
4
5
6
7
8
9
10
<style>
.clip-sector-45{
height: 50px;
width: 50px;
background: red;
clip-path: path('M 25 25 L 50 25 L 50 0 Z');
border-radius: 50%;
}
</style>
<div class="clip-sector-45"></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="clip-sector-jsdeg"></div>
<style>
#clip-sector-jsdeg{
height: 50px;
width: 50px;
background: red;
border-radius: 50%;
}
</style>
<script>
let startTime = Date.now()
function render(){
let deg = 360 * (Date.now() - startTime % (3 * 1000)) / (3 * 1000)
let x = 25 + 25 * Math.cos(deg / 180 * Math.PI)
let y = 25 - 25 * Math.sin(deg / 180 * Math.PI)
let dom = document.getElementById("clip-sector-jsdeg")
dom.style.clipPath = `path('M 25 25 L 50 25 A 25 25 0 1 0 ${x} ${y} Z')`
requestAnimationFrame(render)
}
requestAnimationFrame(render)
</script>

最终的结果

END

2020-09-29 完成

2020-09-29 立项

事件循环 Event loop

JS由于是单线程的,为了避免等待I/O的处理阻塞后续事务的执行,具体的运行时往往对I/O操作通过异步操作的形式实现,例如发出请求,读取文件等等。

虽然也会有提供同步执行的形式,但是会阻塞其他事务的执行。

对于这些异步操作,就需要一个事件循环的模型来处理。类似处理完一个事件,然后等待下一个事件,一直这样循环下去。

1
2
3
while(wait_something){
do_something
}

浏览器和Nodejs是现在JS十分常见的两个运行环境,它们的事件循环各自有各自的定义。

浏览器中的事件循环

浏览器中的事件循环需要一个任务队列(task queue)和一个微任务队列(microtask queue)

每次循环先从任务队列读取一个任务并执行,然后循环处理微任务队列的任务直到微任务队列为空,然后执行浏览器渲染相关的操作(所以如果任务耗时太多就会导致浏览器界面卡顿)。

具体的事件循环如图:

详细流程可以参考HTML Event Loops 标准

操作进入的队列

异步操作 放入队列
setTimeout 任务队列
setInterval 任务队列
事件回调 任务队列
Promise.then/catch/finally 微任务队列

Nodejs中的事件循环

Nodejs中的事件循环与浏览器中的事件循环不同,将事件循环主要分为了6个阶段:timers、pending callbacks、idle prepare、poll、check和close callbacks

每个阶段都是一个FIFO的队列

timers阶段处理setTimeoutsetInterval的计时结束的任务,这些任务是以结束时间构建了一个最小堆,每次检测判断堆顶的任务是否满足要求。

pending callbacks阶段处理执行延迟到下一个循环迭代的I/O回调

idle, prepare阶段处理了nodejs自己的一些任务

poll(轮询)阶段检索新的I/O回调,执行它的处理

check阶段处理setImmediate的回调,但是在这之前会再次判断timers阶段是否有完成的任务

close callbacks阶段处理close事件的回调(poll阶段不会处理)

此外每个回调事件处理完后会处理process.nextTick的队列,每处理一个nextTick回调,会清空微任务队列。

详细可以参考 Node.js 事件循环 Nodejs 源码事件循环 Nodejs 任务处理

参考资料

HTML标准 Event Loops

Node.js 事件循环

Nodejs 源码事件循环

Nodejs 任务处理

END

2020-09-24 立项

2020-09-23 立项

域名系统(DNS)简介

域名系统(Domain Name System, DNS)实现了域名到IP地址转换的功能,可以帮助人们方便的方便互联网系统。

全世界域名的管理由互联网名称与数字地址分配机构(The Internet Corporation for Assigned Names and Numbers, ICANN)进行。ICANN原本是美国商务部下的一个非营利组织,但在2016年不再属于商务部,成为独立机构。它负责管理全世界的域名系统的运作,主要工作就是规定顶级域名(Top Level Domain, TLD)

TLD是指域名的最后一部分,例如网站chenxiyuan.fun的最后一部分fun就是顶级域名,ICANN可以规定哪些字符串可以作为顶级域名。顶级域名可以分为一般性域名(gTLD)如.com.org.net和国别顶级域名(ccTLD)如.cn.io.cc。现有的顶级域名有上千种,列表可参考NameBeta

上千的顶级域名下面还会有成千上万的二级域名,单纯靠ICANN很难管理,因此这些顶级域名下的具体管理政策都会交给一些委托商来进行,委托商通过管理顶级域名获利。

根域名和DNS服务器

ICANN对所有顶级域名进行管理,因此存在一个最高级的域——根域名,有时候一个域名后面会跟一个.,这个点就是根域名。ICANN需要告诉外界各个顶级域名去找哪个委托商处理,因此ICANN需要搭建服务器提供根域名查询服务,提供根域名列表,列出各个根域名对应的委托商的服务器,这个列表叫DNS根区(DNS root zone)(点击根区文件查看)。。

保存根区文件的服务器就叫根域名服务器,由于早期网络传输限制,一个DNS查询结果最多可以容纳13个地址,因此就规定最多13个根域名服务器\[a-m\].root-servers.net。这13台根域名服务器由12个组织独立运营,为保证根节点服务器的稳定,都会采用分布式的形式部署,因此根域名服务器不是只有13台。全世界的根域名服务器可以在https://root-servers.org/查看。一般根区文件变化很少,因此本地计算机、本地DNS服务器都会对根区文件缓存,所以根域名服务器的压力并不是很大。

各个顶级域名委托商运行的服务器叫顶级域名服务器,顶级域名服务器为其所属的顶级域名提供解析服务,它可以直接提供解析结果,也可能指示前往下一级域名的域名服务。例如xxx.yyy.zzzyyy.zzz可能会有自己的服务器管理自己的域名,因此zzz顶级域名服务器会给查询者提供yyy.zzz的服务器地址,让查询者访问yyy.zzz的服务器去查询。这些子域名的服务器由子域名的持有者进行控制,叫权限域名服务器。

此外还有本地DNS服务器,本地DNS服务器为域名提供缓存服务器,计算机本地所填写的DNS服务器都是本地DNS服务器如114.114.114.1148.8.8.8等等。当接收到查询本地DNS服务器会先查询自己的缓存,如果有缓存则直接返回,如果没有就根据查询域名的层级,从根域名一步步向下查找获得目标域名对应的地址。

DNS查询

当计算机需要查询一个域名的DNS时,会先查询自身的DNS缓存,如果没有命中则向本地DNS服务器查询。如果本地DNS服务器也没有,那么计算机就会向本地DNS服务器一样从根域名开始查起,如果还没有则DNS查询失败。

参考资料

ICANN

NameBeta——TLD列表

阮一峰——根域名的知识

END

2020-08-15 完成

2020-08-14 立项

ubuntu initramfs修复

正happy的敲着代码突然停电了T T,今天上来发现虚拟机ubuntu系统进入了initramfs,找了半天终于找到了退出办法

解决方法

在initramfs后面输入 fsck.ext4 -y <出错的盘符,例如/dev/sda1> 可修复一下

END

2019-09-10 完成

2019-09-10 立项

懒加载利器-IntersectionObserver简介

在开发当中,某些情况下需要监控元素是否进入了指定位置,再进行数据加载操作(例如图片的懒加载、效果的初入播放等等),传统的方式主要以监听scroll事件为主,但是这样的方式对性能有一定的影响,现在出了一个草案提出了IntersectionObserver对象,这个对象可以帮助获取两个对象之间相交时的事件,而不需要通过计时、监听事件的方式进行处理

IntersectionObserver

简单例子

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>IntersectionObserver</title>
</head>
<body>
<div style="height: 500px;width: 200px;overflow:scroll">
<div style="position: relative; height: 2000px">
<div id="red" style="position: absolute; top: 1000px; height: 200px; width: 100%; background: red;"></div>
</div>
</div>
<script>
let intersectionObserver = new IntersectionObserver(entries => {
let entry = entries[0]
if(entry.isIntersecting === true) {
console.log("red in")
} else {
console.log("red out")
}
})
intersectionObserver.observe(document.getElementById("red"))
</script>
</body>
</html>

构造函数 IntersectionObserver(callback[, options])

IntersectionObserver的构造函数含有两个参数:callbackoptions

callback: (entries, observer) => void

第一个参数为触发的回调函数,entriesIntersectionObserverEntry对象数组,observer是触发的IntersectionObserver观察者实例

回调的触发是收集的这段时间内触发阈值变化的内容,一个观察对象在这一次内跨过多个阈值只能产生一个entry,多个观察对象跨越阈值则会产生多个entry

options

配置主要包含三个属性:rootrootMarginthreshold

其中root设定的为监听对象的具体祖先元素(必须是观察对象的祖先元素,否则无效),这个默认为顶级文档的视图窗口

rootMargin可以用于扩展或搜索root的判断区域,例如"-30px -30px -30px -30px"表示判断的边框向内收缩30px

threshold表示的触发的阈值,这个可以是一个数值,也可以是一个数值数组,但是值必须在0~1,触发时获取的比例不一定是触发的阈值

这三个选项也是IntersectionObserver的属性,但是是只读的

实例函数

在创建了IntersectionObserver对象后,它有observer(targetElement)unobserver(targetElement)disconnect(),其中observer(targetElement)unobserver(targetElement)用于绑定和解绑监听对象,disconnect()用于结束对象的监听行为

IntersectionObserverEntry

IntersectionObserverEntry对象,描述了目标元素与其根元素容器在某一特定过渡时刻的交叉状态,属性如下

属性名称 类型 描述
boundingClientRect DOMRectReadOnly 目标元素的边界信息
rootBounds DOMRectReadOnly 观察者的根元素的边界信息
intersectionRect DOMRectReadOnly 根和目标元素的相交区域的边界信息
intersectionRatio number intersectionRect与boundingClientRect的比例值
isIntersecting boolean 根和目标元素是否相交
target Element 触发这个Entry的被观察元素
time DOMHighResTimeStamp IntersectionObserver的时间原点到交叉被触发的时间的时间戳

兼容性

总结

IntersectionObserver主要用于监听子元素与祖先元素视图之间的交叉情况,从而实现对元素展现情况的捕获

注意点

1.intersectionRatio为0不代表不想交,是否相交需要依靠isIntersecting进行判断

参考资料

MDN_IntersectionObserver

END

2019-07-12 完成

2019-01-17 立项

[错误记录]js严格模式下的报错

JS严格模式下的奇葩问题

经历

最近项目中PDFJS有些浏览器不能渲染了,一开始排查以为是PDFJS在webpack打包下的问题,一直没有找到解决原因,后来突然发现了是在JS严格模式下,元素的style属性是不可修改的!!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
"use strict";
var div = document.createElement("div")
div.style = "width: 100%"
div.innerHTML="success"
document.body.appendChild(div)
</script>
</body>
</html>

这段代码会在IE和一些老的safira浏览器报错,原因是严格模式下元素的style属性不能修改!!!!,需要用element.style.xxxx = yyyy的模式修改才行

ENd

2019-03-15 完成

2019-03-15 立项

数据结构与算法系列(9)--排序算法总结

前面讲了大多数常见的排序算法,这里对这些算法总结一下

算法属性列表

排序算法 最优时间复杂度 平均时间复杂度 最差时间复杂度 空间复杂度 是否稳定 是否为原地排序
选择排序 Ω(n2) Θ(n2) O(n2) O(1) N Y
插入排序 Ω(n) Θ(n2) O(n2) O(1) Y Y
冒泡排序 Ω(n) Θ(n2) O(n2) O(1) Y Y
希尔排序 Ω(n) Θ(n1.3) O(n2) O(1) N Y
归并排序 Ω(nlogn) Θ(nlogn) O(nlogn) O(n) Y N
快速排序 Ω(nlogn) Θ(nlogn) O(n2) O(logn) N Y
堆排序 Ω(nlogn) Θ(nlogn) O(nlogn) O(1) N Y
计数排序 Ω(n+k) Θ(n+k) O(n+k) O(k) Y N
基数排序 Ω(n*q) Θ(n*q) O(n*q) O(n+k) Y N
桶排序 Ω(n+k) Θ(n+k) O(n+k) O(n+k) Y N

快速排序算法总结

  1. 快排是目前最快的通用排序算法

    原因:快排的内循环指令少,而且大多采用顺序取值可以充分利用缓存

  2. 利用稳定性排序算法可以在保持原来的属性A的有序性下,实现属性B的有序,这时候B的顺序优先

排序算法解决的问题归约

  1. 找出重复元素
  2. 排名
  3. 优先队列
  4. 中位数与顺序统计

排序算法应用

  1. 商业计算
  2. 信息搜索
  3. 运筹学
  4. 事件驱动模拟
  5. 数值计算
  6. 组合搜索

END

2019-03-12 完成

2019-03-05 立项

数据结构与算法系列(8)--非比较排序

前面的排序都是基于比较进行的排序,而基于比较进行排序的效率最快也只是nlogn,而在某些情况下可以不需要比较就进行排序,从而达到一个极好的效率,这里主要介绍计数排序、基数排序、以及桶排序

计数排序

计数排序主要针对正整数数组,通过查询数组中的最大值和最小值,来设置存储数据的计数数组的大小,然后遍历将整数n对应的arr[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
function CountSort(arr){
let min = Infinity
let max = 0
let len = arr.length
for (let i = 0; i < len; i++){
if (arr[i] < min) min = arr[i]
if (arr[i] > max) max = arr[i]
}

let countarr = new Array(max - min + 1).fill(0)

for (let i = 0; i < len; i++){
countarr[arr[i] - min]++
}

let p = 0
for (let i = 0; i <= max - min; i++){
for (let j = 0; j < countarr[i]; j++){
arr[p] = i + min
p++
}
}

return arr
}

算法属性

最优时间复杂度 平均时间复杂度 最差时间复杂度 空间复杂度 稳定性
Ω(n+k) Θ(n+k) O(n+k) O(k) 稳定

这里的k就是待排序数组里面的最大值减去最小值,计数排序在面对最小值和最大值之差的数值远大于原数组数据量的时候,效率反而会很差

基数排序

为了解决计数排序的缺陷,通过基数进行排序的算法也就出来了,它类似于计数排序,但是是基于基数来进行的

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 BaseSort(arr){
let len = arr.length

let max = 0
for (let i = 0; i < len; i++){
if (arr[i] > max) max = arr[i]
}

let x = 1
while (true){
let baseArr = new Array(10).fill(0).map(_ => new Array())

for (let i = 0; i < len; i++){
let j = parseInt(arr[i] / x) % 10
baseArr[j].push(arr[i])
}

let p = 0
baseArr.forEach(base => {
base.forEach(data => {
arr[p] = data
p++
})
})

x = x * 10
if (x > max) break
}

return arr
}

算法属性

最优时间复杂度 平均时间复杂度 最差时间复杂度 空间复杂度 稳定性
Ω(n*q) Θ(n*q) O(n*q) O(n+k) 稳定

基数排序中k是桶的个数,在这里就是基数排序时,数值的进制,而q表示的最大的位数。在网上大都都是将qk认为是同一值,但是从算法可以明显看出来,空间复杂度和时间复杂度,依靠的内容不同

桶排序

桶排序也是对计数排序的优化,计数排序吧每个数值都展开了,导致会消耗大量的空间,而桶排序这是将这些数值划分为一个个的区间段

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
function BucketSort(arr, bukectSize = 10){
let min = Infinity
let max = 0
let len = arr.length
for (let i = 0; i < len; i++){
if (arr[i] < min) min = arr[i]
if (arr[i] > max) max = arr[i]
}

let bucketNum = parseInt((max - min) / bukectSize) + 1
let buckets = new Array(bucketNum).fill(0).map(_ => new Array())

for (let i = 0; i < len; i++){
let b = parseInt((arr[i] - min) / bukectSize)
let bucket = buckets[b]
let len = bucket.length
bucket[len] = arr[i]
for (let j = len - 1; j >= 0; j--){
if (bucket[j] < arr[i]){
bucket[j + 1] = bucket[j]
} else {
bucket[j + 1] = arr[i]
break
}
}
}

let p = 0
for (let i = 0; i < buckets.length; i++){
for (let j = 0; j < buckets[i].length; j++){
arr[p] = buckets[i][j]
p++
}
}

return arr

算法属性

最优时间复杂度 平均时间复杂度 最差时间复杂度 空间复杂度 稳定性
Ω(n+k) Θ(n+k) O(n+k) O(n+k) 稳定

小结

这些非比较排序主要依靠了整数可以直接映射到存储空间的特性,只有在部分情况下才会有用,而最常用到的依然还是比较排序

END

2019-03-08 完成

2019-03-06 立项

数据结构与算法系列(7)--堆排序

这一篇文章主要介绍二叉堆的概念,以及堆排序算法

二叉堆

二叉堆其实就是由一个数组数据表示出来的一个完全二叉树,树的内容在后序讲述,二叉堆需要满足的仅仅是父节点的值大于两个子节点

为了方便计算,第一个元素默认为在1号位置,当然这个数据可以进行转换

堆排序

堆排序的核心在于将数组构建成一个有序的堆,然后通过提取堆的顶部(最大值)–维护的循环来对堆进行排序

构造有序堆

构造有序堆只需要将元素一个个遍历(一个个加入到堆当中),然后进行上浮即可,也就是元素与自己的父元素进行比较,然后如果大于父元素就进行交换,并重复操作直到不满足或者到顶

下沉

在堆构造完成后,就能直接获取最大元素,在获取最大元素后只要将其与堆的末尾元素交换,然后末尾的位置不纳入堆的范围即可,同时使用下沉操作,来将置换上来的小值向下交换,维持堆的循序结构

堆排序实现

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
function StackSort(arr){
//上浮
function swim(arr, p){
while (p > 1 && arr[p] > arr[parseInt(p / 2)]){
[arr[p], arr[parseInt(p / 2)]] = [arr[parseInt(p / 2)], arr[p]]
p = parseInt(p / 2)
}
}

//下沉--将第一个元素下沉
function sink(arr, i, len){
while (2 * i <= len){
let j = 2 * i
if (j < len && arr[j] < arr[j + 1]) j++
if (arr[i] < arr[j]){
[arr[i], arr[j]] = [arr[j], arr[i]]
i = j
} else {
break
}
}
}

let length = arr.length
for (let i = length - 1; i >= 0; i--){
arr[i + 1] = arr[i]
}

for (let i = 0; i < length; i++){
swim(arr, i + 1)
}

for (let i = length; i > 1; i--){
[arr[1], arr[i]] = [arr[i], arr[1]]
sink(arr, 1, i - 1)
}

for (let i = 0; i < length; i++){
arr[i] = arr[i + 1]
}
arr.length = length

return arr
}

除此之外,堆的初始化还可以通过下沉来完成

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 StackSort2(arr){
//下沉--将第一个元素下沉
function sink(arr, i, len){
while (2 * i <= len){
let j = 2 * i
if (j < len && arr[j] < arr[j + 1]) j++
if (arr[i] < arr[j]){
[arr[i], arr[j]] = [arr[j], arr[i]]
i = j
} else {
break
}
}
}

let length = arr.length
for (let i = length - 1; i >= 0; i--){
arr[i + 1] = arr[i]
}

for (let i = parseInt(length / 2); i >= 1; i--){
sink(arr, i, length)
}

for (let i = length; i > 1; i--){
[arr[1], arr[i]] = [arr[i], arr[1]]
sink(arr, 1, i - 1)
}

for (let i = 0; i < length; i++){
arr[i] = arr[i + 1]
}
arr.length = length

return arr
}

算法属性

最优时间复杂度 平均时间复杂度 最差时间复杂度 空间复杂度 稳定性
Ω(nlogn) Θ(nlogn) O(nlogn) O(1) 不稳定

END

2019-03-05 完成

2019-03-04 立项