目录
– 代码技巧
– 轻松的逐个完成多个异步
– 顺序执行异步(简易)
– 数组
– 基本函数
– 高阶函数
– 循环
– 转换为数组
– 字符串
– Audio
– AudioContext
– 声明
– 方法
– AnalyserNode
– 声明
– 方法
– AudioBuffer 和 buffer
– 声明
– 例子
– 异步处理
– promise
– 构造promise
– resolve和reject
– then和catch
– JS引擎的执行顺序
– 同步任务和异步任务
– 宏任务和微任务
– 总结
– websocket
– readyState含义
– 客户端
– 建立连接 接收消息
– 发送消息
– 服务端
– 依赖
– 搭建
– 发送消息
– 实/常/有 用的函数
– 时间戳
– JSON
– cookie
– 杂
– 完全深度复制
– 获取最底层的document文档对象
– 直接无提示关闭页面
– 刷新页面
– 消除所有定时器
– 地毯式搜索所有子iframe中的所有元素
– 获取指定名称的 cookie 值
– 获取URL中指定名称的值
– 睡眠函数
– 简易的md转html
– 数组快速去重
– 参考来源
代码技巧
轻松的逐个完成多个异步
let promises = [];
for(...){
promises.push(
(()=>{
// 顺序处理的初始化代码
return new Promise((resolve, reject) => {
new Promise((res, rej) => {
ctx.decodeAudioData(avbuffer, res, rej);
})
.then((buffer) => {
this.buffers[i] = buffer;
resolve();
})
.catch(reject);
});
})();
await Promise.all(promises);
)
}
解读:
1. 创建一个promises
数组, 用于存放所有Promise
2. 在循环中使用立即执行函数, 定制每个独立的Promise
的同时也无需担心变量污染!
3. 立即执行函数的返回值是一个Promise
, 在外侧用promises
来存放
4. 要返回的Promise
中当然也可以再使用异步函数!(要注意大小Promise
的回调函数名称不能冲突)
5. 最后使用await Promise.all(promises);
有序完成所有的Promise
!
顺序执行异步(简易)
let i = 0;
let aaa = [...]
const g = async () => {
if (i >= aaa.length) {
return;
}
await f(aaa[i]);
i++;
g();
}
await g();
数组
基本函数
array.push()
– 作用: 在数组的末尾添加一个元素
– 返回: 新数组的长度
> var array = [1,9,2,4,"a",10]
> array.push(true);
< 7
> array
< [1, 9, 2, 4, 'a', 10, true]
array.pop()
– 作用: 删除最后一个元素
– 返回: ta的值
– 注意: 不要求参数(输了也没用)
> var array = [1,9,2,4,"a",10]
> array.pop();
< 10
> array
< [1, 9, 2, 4, 'a']
array.shift();
– 作用: 弹出数组的第一个元素
– 返回: 第一个元素
– 备注: 会改变原先的数组
> var array = [1,9,2,4,"a",10]
> array.shift();
< 1
> array
< [9, 2, 4, 'a', 10]
array.unshift(val);
– 作用: 在第一个元素前插入被传入的值
– 返回: 新数组的长度
– 备注: 原先的每个元素的序号+1
> var array = [1,9,2,4,"a",10]
> array.unshift("zhy");
< 7
> array
< ['zhy', 1, 9, 2, 4, 'a', 10]
array.splice(n1,n2)
– 作用: 从数组中按序号剪切出一段新数组, 同时也会改变原数组
– 返回: 剪切出来的新数组
– 参数: 参数n1是指从array[n1]这一元素开始, n2是指新数组的长度
– 注意: n2即便很大也不会使新数组的长度变得很大, 只会一直剪切到原数组的最后一个元素为止
> var array = [1,9,2,4,"a",10]
> array.splice(1,3)
< [9, 2, 4]
> array
< [1, 'a', 10]
> array.splice(1,9)
< ['a', 10]
> array
< [1]
array.slice(n1,n2);
– 作用: 从数组中按序号复制出一个新数组
– 返回: 新数组
– 参数: n1是指开始的序号, array[n1]这个元素不取; n2是指结束的序号, array[n2]这个元素取
– 注意: 1. 是复制, 原数组无变化, 要与splice区分
1. 遵循左开右闭的原则
> var array = [1,9,2,4,"a",10]
> array.slice(3,5)
< [4, 'a']
> array
< [1, 9, 2, 4, 'a', 10]
array.join(str)
– 作用: 将数组的每个元素之间用str连接起来
– 返回: 一个字符串
– 参数: 一个可以被转为string的东西(数字,string之类)
– 备注: 原数组不变化
> var array = [1,9,2,4,"a",10];
> array.join("|")
< '1|9|2|4|a|10'
arry = array1.concat(array2, array3)
– 作用: 将多个数组连接起来
– 返回: 组合而成的新数组
– 参数: 多个数组对象(按顺序)
– 注意: 原数组不变化, 一定要获取返回值
> var array1 = [1,2,3];
> var array2 = [4,5,6];
> var array3 = [7,8,9];
> var arrayA = array1.concat(array2,array3);
> arrayA
< [1, 2, 3, 4, 5, 6, 7, 8, 9]
> array3.concat(array1,array2);
< [7, 8, 9, 1, 2, 3, 4, 5, 6]
高阶函数
array.map(function)
– 作用: 将一个函数作用在数组的每个元素上, 并用其返回值组成新的数组后输出
– 返回: 加工后产生的新数组
– 参数: 仅回调函数自身, 而回调函数会接收3个参数:callback(currentValue, index, array)
– 注意: 原数组不发生改变
> var aaa = [1, 3, 5];
> function double(x) {
return x * 2;
}
> aaa.map(double);
< [2, 6, 10]
> aaa;
< [1, 3, 5]
array.reduce(function)
– 作用: 函数要求两个变量, 函数先作用于前两个元素, 并把返回值和数组的下一个元素再次作为参数继续运算
– 返回: 最终的一个结果, 视function的返回值而定
– 参数: function要求两个参数
– 备注: 其效果展开来看如下:[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
> var arr = [1, 3, 5, 7, 9];
> arr.reduce(function (x, y) {
return x + y;
});
< 25
array.filter(function(element, index, self){})
– 作用: 把Array
的某些元素过滤掉,然后返回剩下的元素组成的集合
– 返回: 一个筛选后的数组
– 参数: 一个回调函数, 应当返回一个布尔值, 其参数有三个:
+element
: 表示元素
+index
: 元素位置
+self
: 数组本身
> var arr = ['A', 'B', 'C'];
> var r = arr.filter(function (element, index, self) {
console.log(element); // 依次打印'A', 'B', 'C'
console.log(index); // 依次打印0, 1, 2
console.log(self); // self就是变量arr
return true;
});
< 'A'
< 0
< arr
< 'B'
< 1
< arr
< 'C'
< 2
< arr
循环
for(a in aaa)
– 作用: 循环得到一个对象的所有属性的简洁写法
– 参数: 这里的a
是key
或者说是property
– 注意: 这里得到的a
是string
的形式, 千万别写aaa.a
!!!
– 备注: 最好是对一个object
使用
> let aaa = {
"name": "zhyDaDa",
"hobby": "programming",
"age": 3
}
> for(a in aaa){
console.log(a);
console.log(aaa[a]);
}
< name
< zhyDaDa
< hobby
< programming
< age
< 3
for(let b of bbb)
– 作用: 循环得到数组中每一个值的简洁写法
– 参数:b
是一个value而非index
– 备注: 最好是对一个array
使用
> var bbb = [1,'b',2,4,"zhyDaDa",10];
> for(let b of bbb){
console.log(b);
}
< 1
< b
< 2
< 4
< zhyDaDa
< 10
转换为数组
Array.prototype.slice.call(HTMLCollection)
– 作用: 将类数组对象转换为数组(常用于页面元素数组的转化)
– 返回: 返回值为数组
– 参数: 一个类数组对象
> var aaa = $(".aaa");
< undefined
> aaa
< [a.aaa, b.aaa ...]
> Array.prototype.slice.call(aaa)
< [a.aaa, b.aaa ...]
字符串
parseInt(string, radix)
– 作用: 可解析一个字符串,并返回一个整数
– 返回: 整数
– 参数: +
+string
: 要被解析的字符串。
+radix
: 可选, 表示要解析的数字的基数; 该值介于 2 ~ 36 之间
– 备注:
+ 如果 string 以 “0x” 开头,parseInt() 会把 string 的其余部分解析为十六进制的整数。
+ 如果 string 以 0 开头,那么 ECMAScript v3 允许 parseInt() 的一个实现把其后的字符解析为八进制或十六进制的数字。
+ 如果 string 以 1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数。
> parseInt("10.33")
< 10
> parseInt("10",8)
< 8
> parseInt("0x10")
< 16
Number(object)
– 作用: 把不同的对象转换为数字
– 返回: 数字以及NaN
– 参数: 一个JS对象
– 备注: 如果没有提供参数, 则返回0
> Number(true)
< 1
> Number(false)
< 0
> Number("432")
< 432
> Number("4 3 2")
< NaN
Audio
所有工作都在AudioContext中实现
这个对象包含有很多个节点(nodes), 各司其职
一般的工作流程如下:
1. 创建一个AudioContext
2. 在AudioContext中,创建声源节点——例如<audio>
标签,振动发声器 (oscillator),音频流(stream)
3. 创建特效节点——例如混响,双二阶滤波,声相控制,音频振幅压缩
4. 选择音频的终点——例如系统的扬声器
5. 连接声源和特效,以及特效和终点(和电吉他接线一个原理)。
AudioContext
声明
var audioCtx = new AudioContext;
跨浏览器的通用方法:
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();
方法
audioCtx.close();
– 作用: 释放占用的系统资源, 停止处理音频
– 返回: promise
– 备注: 可以接then处理后续动作
audioCtx.close().then(function() { ... });
audioCtx.suspend();
audioCtx.resume();
– 作用: 暂停和继续
– 返回: promise
– 备注: 如果返回失败, 就说明audioCtx已经被关闭
AnalyserNode
声明
var analyser = audioCtx.createAnalyser();
方法
analyser.getFloatFrequencyData(array);
analyser.getByteFrequencyData(array);
– 作用: 获取频率数据, 并以数组形式储存, 前者是32位浮点, 后者是Uint8Array(无符号字节数组)
– 返回: 数组
– 备注: 此数组表示的频率范围为 0 ~ 22050 Hz,每个元素表示对应频率上的信号分量强度,单位为分贝
AudioBuffer 和 buffer
缓存区(buffer)包含以下数据:不间断的 IEEE754 32 位线性 PCM,从 -1 到 1 的范围额定,就是说,32 位的浮点缓存区的每个样本在 -1.0 到 1.0 之间。
声明
var audioBuffer = new AudioBuffer(context[, options]);
– 作用: 创建一个新的 AudioBuffer 对象
– 返回: 一个新的 AudioBuffer 对象实例
– 参数:
+ context: audioCtx
+ option可选参数
– length: buffer 中采样帧的长度。
– numberOfChannels: buffer 的通道数。默认值为 1.
– sampleRate: buffer 的采样率 (Hz). 默认值为构造此对象时使用的 context 的采样率。AudioContext.createBuffer(Number numOfChannels, Number length, Number sampleRate);
– 作用: 建一个空白的 AudioBuffer 对象
– 返回: 一个 AudioBuffer
– 参数:
+ numOfChannels
一个定义了 buffer 中包含的声频通道数量的整数。 一个标准的实现必须包含至少 32 个声频通道。
-
length
一个代表 buffer 中的样本帧数的整数。 -
sampleRate
线性音频样本的采样率,即每一秒包含的关键帧的个数。实现过程中必须支持 22050 ~ 96000 的采样率。
var source = audioCtx.createBufferSource()
– 作用: 建立一个一个 AudioBufferSourceNode 节点
– 参数: 参数的说明
– 注意:
一个 AudioBufferSourceNode 只能被播放一次,也就是说,每次调用 start() (en-US) 之后,如果还想再播放一遍同样的声音,那么就需要再创建一个 AudioBufferSourceNode。
例子
// 创建一个双声道的缓冲
var myArrayBuffer = audioCtx.createBuffer(2, frameCount, audioCtx.sampleRate)
// 提取其中一个声道的缓冲, 可以以数组的方式修改它
var nowBuffering = myArrayBuffer.getChannelData(0);
for (var i = 0; i < frameCount; i++) {
// audio needs to be in [-1.0; 1.0]
nowBuffering[i] = Math.random() * 2 - 1;
}
// 要想播放缓冲, 就要有一个资源节点
var source = audioCtx.createBufferSource();
// 把刚才处理好的缓冲交过去
source.buffer = myArrayBuffer;
// 最后把这个资源绑定到最终的节点上去
source.connect(audioCtx.destination);
// 在最终的节点上实现播放动作
source.start();
异步处理
promise
promise是一个 构造函数, 传入一个要运行的函数M
作为参数, 返回一个 promise 实例
在M运行过程中, 会调用两个函数(resolve
和reject
)表示完成与否
紧接着promise实例后有then
和catch
这两个回调函数, 他们会根据promise的返回传递参数, 调用相应函数
promise有三种状态:
1. Fulfilled: has-resolved, 表示成功解决,这时会调用 onFulfilled.
2. Rejected: has-rejected, 表示解决失败,此时会调用 onRejected.
3. Pending: unresolve, 表示待解决,既不是resolve也不是reject的状态。也就是promise对象刚被创建后的初始化状态.
resolve
和reject
用来切换状态
then
和catch
用来感知状态
构造promise
let M = new Promise((resolve, reject) => {
if(done)
resolve(data.done); //处理成功
if(!done)
reject(data.fail); //处理成功
});
这里Promise构造器接受一个函数(一般是 无名函数 ), 这个函数有两个参数
参数是两个函数名, 一般是resolve
和reject
无名函数的内部就是要运行的函数了
promise 实例构造完成就开始运行
下一轮Event Loop就无效了
原因很简单: 状态显示的不是 Pending, 说明已经完成了
resolve和reject
当调用resolve
时, 其参数是要传出去的数据, 其效果是把所在的这个 promise 实例 的状态改成 fulfilled
当调用reject
时, 其参数是错误信息, 其效果是把所在的这个 promise 实例 的状态改成 rejected
注意:
+ 这两个函数不能类比 return! 函数还会继续走完
+ 这两个函数只干两件事:
1. 修改状态
2. 传递数据
+ 修改状态只能改未完成的状态, 也就是说, resolve
和reject
哪个在前面哪个生效
then和catch
他们能感知他们对应的 promise 实例 的状态
他们的参数是回调函数的函数名
M.then(f)
或是M.catch(f)
当M转变成Fulfilled状态时,then就被送入 “微任务”
当 “宏任务” 完成后, then将M
的resolve
传递出的数据作为参数传递给回调函数
catch同理
注意: then
和catch
本身也是 promise 实例
JS引擎的执行顺序
明确: JS 执行机制是单线程
同步任务和异步任务
JS有一个Event Loop的概念, 可以理解为手头正要做的事
JS的主线程就会按着上面登记的任务一个个完成
如果任务出了问题, JS就报错, 停止, 产生卡死现象
任务有一个简单的分类:
+ 同步任务
+ 异步任务
同步进入主线程,异步进入Event Table
这个Event Table可以理解为任务规划表
上面登记的内容是一些函数和ta对应的出场条件
当满足条件时, 这个函数会作为一项任务推入 Event Queue
Event Queue可以理解为待办清单
主进程空闲后, 就把Event Queue全部放到主进程中
打个比喻(纯粹是假想的, 只是用于理解记忆):
主进程:["做作业()", "准备开始吃中饭()"]
Event Table:[["4点", "买菜()"], ["5点", "烧菜()"], ["6点", "吃晚饭()"]]
Event Queue:[["吃饭前", "打开手机()"], ["吃饭时", "看下饭视频()"]]
宏任务和微任务
主进程更细分来看, 其实还能划分成宏任务和微任务
+ macro-task(宏任务):包括整体代码script,setTimeout,setInterval
+ micro-task(微任务):Promise,process.nextTic
总结
按顺序排下来
1. 主进程
2. 宏任务
3. 微任务
4. 装载Event Queue到主进程
5. 回到第2步
websocket
readyState含义
WebSocket
对象的 readyState
属性有以下四种可能的值,每个值对应不同的含义:
1. CONNECTING
(值为 0):表示 WebSocket
正在建立连接。这是初始状态,当 WebSocket
对象被创建后,会立即进入此状态。
2. OPEN
(值为 1):表示 WebSocket
连接已经建立并且可以进行通信。一旦连接成功建立,readyState
将变为 OPEN
。
3. CLOSING
(值为 2):表示 WebSocket
正在关闭连接。当调用 WebSocket
对象的 close()
方法时,会触发关闭过程,此时 readyState
将变为 CLOSING
。
4. CLOSED
(值为 3):表示 WebSocket
连接已经关闭或无法建立。当 WebSocket
连接关闭后,或者连接无法建立时,readyState
将变为 CLOSED
。
注意: 这里的状态是只读的
客户端
建立连接 接收消息
function setServer(serverAddress) {
serverAddress = serverAddress || "ws://127.0.0.1:432";
if (serverAddress.indexOf("ws") < 0) serverAddress = "ws://" + serverAddress + ":432";
window.socket = new WebSocket(serverAddress);
window.socket_file = new WebSocket(serverAddress + "0");
socket.onopen = function() {
console.log("成功连接到服务器");
};
socket.onmessage = function(e) {
let data = JSON.parse(e.data);
};
socket.onclose = function() {
console.log("连接关闭");
};
}
发送消息
function sendMsg(obj) {
let msg = JSON.stringify(obj);
socket.send(msg);
}
服务端
依赖
const WebSocketServer = require('ws').Server;
在nmp
中运行, 其package.json
中应当有以下内容:
{
"dependencies": {
"ws": "^8.15.1"
},
"scripts": {
"server": "node server.js"
},
"name": "main",
"version": "1.0.0",
"main": "server.js",
"type": "commonjs",
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}
commonjs
这个很关键, 因为ws
库用es6
规则写的, 要用commonjs
引入才可以
否则无法使用require
这个方法来引入库
搭建
const wss = new WebSocketServer({
host: '0.0.0.0',
port: 4320
});
wss.on('connection', function(ws) {
console.log(`client ${ws._socket.remoteAddress} connected`);
ws.on('message', function(message) {
let data = JSON.parse(message);
})
ws.on('close', function() {
console.log(`client ${ws._socket.remoteAddress} disconnected`);
})
})
注意: 这里的
ws
是一个WebSocket
对象, 代表连接的的单个客户端
因此要监听ws
的事件就要在其上绑定函数
若要稍后要针对每个个体单独处理, 要记得存储其索引
发送消息
let data = JSON.stringify(obj);
ws.send(data);
实/常/有 用的函数
时间戳
Date.now()
new Date().getTime()
(new Date()).valueOf()
Number(new Date())
– 作用: 获得当前的时间戳
– 返回: 时间戳(数字形式)
> Date.now()
< 1666958673590
> new Date().getTime()
< 1666958656425
new Date(time)
– 作用: 将时间戳转化为字符串形式
– 返回: 标准格式的字符串形式的时间表示
– 参数:time
是数字形式的时间戳
– 注意: 这里的字符串会按照现在的环境变化, 下面给出两个版本
> new Date(1666958673590)
< Fri Oct 28 2022 20:04:33 GMT+0800 (中国标准时间)
> new Date(1645099205343)
< Thu Feb 17 2022 20:00:05 GMT+0800 (CST)
生成 ‘yyyy/MM/dd 上(下)午 hh:mm:ss’ 格式
function getLocalTime(n){
return new Date(parseInt(n)).toLocaleString().replace(/:\d{1,2}$/,'');
}
getLocalTime(1645099205343) //"2022/2/17 下午8:00"
当前系统区域设置格式(toLocaleDateString和toLocaleTimeString)
(new Date()).toLocaleDateString() + " " + (new Date()).toLocaleTimeString()
//'2022/2/18 下午6:51:09'
格林威治标准时间(toGMTString)
(new Date()).toGMTString()
//"Fri, 18 Feb 2022 10:53:01 GMT"
JSON
JSON.parse(“json_string”)
– 作用: 将一个JSON字符串解析成JavaScript对象
– 返回: 一个对象, 可以直接用
– 参数: json字符串
– 备注: 前后端交互
> var json_info = '{"a":1,"b":"zhyDaDa","c":null}'
> var js_obj = JSON.parse(json_info)
> js_obj
< {a: 1, b: 'zhyDaDa', c: null}
JSON.stringify(value, replacer, space)
– 作用: 将JavaScript值转换成JSON对象
– 返回: 字符串, json的格式
– 参数:
+ value: 一个对象, 可以是数组
+ replacer: 将对象的值传入, 得到的返回值写入json
+ space: 用几个空格缩进
– 备注: 前后端交互
> var js_obj = {a: 1, b: 'zhyDaDa', "c": null}
> var json_string = JSON.stringify(js_obj)
> json_string
< '{"a":1,"b":"zhyDaDa","c":null}'
cookie
设置cookie
– 参数: iDay是有效天数
function setCookie(name,value,iDay){
var oDate = new Date();
oDate.setDate(oDate.getDate() + iDay);
document.cookie = name + "=" + value + ";expires=" + oDate;
}
读取cookie
– 注意: 该方法简单地认为cookie中只有一个“=”,即key=value,如有更多需求可以在此基础上完善:
function getCookie(name){
//例如cookie是"username=abc; password=123"
var arr = document.cookie.split('; ');//用“;”和空格来划分cookie
for(var i = 0 ;i < arr.length ; i++){
var arr2 = arr[i].split("=");
if(arr2[0] == name){
return arr2[1];
}
}
return "";//整个遍历完没找到,就返回空值
}
删除cookie
– 备注: 原理就是覆盖, 然后时间设置成昨天
function removeCookie(name){
setCookie(name, "1", -1)//第二个value值随便设个值,第三个值设为-1表示:昨天就过期了,赶紧删除
}
杂
完全深度复制
- 作用: 完全的复制一个数组或者对象, 并且两者独立互不影响
- 返回: 克隆品
- 参数: 要复制的对象, 以及是否是数组
- 备注: 完美解决存储位置公用的问题
var deepCopy = function (source,isArray) {
var result = {};
if(isArray) var result = [];
for(var key in source) {
if(Object.prototype.toString.call(source[key]) === '[object Object]') {
result[key] = deepCopy(source[key])
} if(Object.prototype.toString.call(source[key]) === '[object Array]') {
result[key] = deepCopy(source[key],1)
} else {
result[key] = source[key]
}
}
return result;
}
获取最底层的document文档对象
- 作用: 该函数会返回 document 为最底层的 iframe 父页面的文档对象,从而避免了在多层嵌套 iframe 中访问元素时出现的问题
- 备注: 并不改变现在的document, 因为document是只读的
/**
* 获取最顶层的文档对象。
*
* @returns {Document} 最顶层的文档对象。
*/
function getBaseDocument() {
var currentWindow = window;
while (currentWindow !== currentWindow.parent) {
currentWindow = currentWindow.parent;
}
return currentWindow.document;
}
直接无提示关闭页面
- 作用: 同上, 用js实现
- 备注: 无提示
let a = document.createElement("a");
a.href = "javascript:window.opener=null;window.open('','_self');window.close();";
a.click();
刷新页面
- 作用: 如题
location.reload();
消除所有定时器
- 作用: 如题
let end = setInterval(function () { }, 10000);
for (let i = 1; i <= end; i++) {
clearInterval(i);
}
地毯式搜索所有子iframe中的所有元素
- 注意: 按需要改变选择器的规则
function searchIframes(doc, pattern) {
let elementList=[];
doc.querySelectorAll('iframe').forEach(iframe => {
try {
iframeDoc = iframe.contentDocument || iframe.contentWindow.document
elementList = elementList.concat(iframeDoc.querySelectorAll(pattern) || [])
elementList = elementList.concat(searchIframes(iframeDoc) || [])
} catch (e) { // 直接忽略错误的 iframe (e.g. cross-origin)
}
})
return elementList;
}
获取指定名称的 cookie 值
getCookie = name => `; ${document.cookie}`.split(`; ${name}=`).pop().split(';').shift()
获取URL中指定名称的值
- 备注: 没有找到就返回false
getQueryVariable = (variable) => {
let q = window.location.search.substring(1),
v = q.split("&"),
r = false;
for (let i = 0, l = v.length; i < l; i++) {
let p = v[i].split("=");
p[0] == variable && (r = p[1]);
}
return r;
}
睡眠函数
function sleep(interval) {
return new Promise((success, fail) => {
setTimeout(success, interval);
});
}
简易的md转html
- 备注: 因为多行文字要用模板字符串, 所以代码块的标识符用了
$
, 切记切记
var md2html = function(md) {
// 分别替换两个尖括号
md = md.replace(/</gm, "<");
md = md.replace(/>/gm, ">");
// 加粗
md = md.replace(/\*\*(.+?)\*\*/gm, "<strong>$1</strong>");
// 代码
md = md.replace(/\$(.+?)\$/gm, "<code>$1</code>");
// 链接
md = md.replace(/\[(.+?)\]\((.+?)\)/gm, "<a target='_blank' href='$2'>$1</a>");
// 句子中间的多个空格替换为html中相同数量的空格
md = md.replace(/ /gm, " ");
// 一级标题
md = md.replace(/^# (.+?)$/gm, "<h1>$1</h1>");
// 二级标题
md = md.replace(/^## (.+?)$/gm, "<h2>$1</h2>");
// 三级标题
md = md.replace(/^### (.+?)$/gm, "<h3>$1</h3>");
// 其余的行前后加上p标签, 如果一行的末尾有2个空格则在末尾加上<br>
md = md.replace(/^(.+?)$/gm, "<p>$1</p>");
md = md.replace(/<p>(.+?) <\/p>/gm, "<p>$1</p><br>");
return md;
}
数组快速去重
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let arr2 = [...new Set(arr)];