zhyDaDa的个人站点

JS使用技巧和疑难杂症手册

未分类

目录
代码技巧
轻松的逐个完成多个异步
顺序执行异步(简易)
数组
基本函数
高阶函数
循环
转换为数组
字符串
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)
作用: 循环得到一个对象的所有属性的简洁写法
参数: 这里的akey或者说是property
注意: 这里得到的astring的形式, 千万别写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运行过程中, 会调用两个函数(resolvereject)表示完成与否
紧接着promise实例后有thencatch这两个回调函数, 他们会根据promise的返回传递参数, 调用相应函数

promise有三种状态:
1. Fulfilled: has-resolved, 表示成功解决,这时会调用 onFulfilled.
2. Rejected: has-rejected, 表示解决失败,此时会调用 onRejected.
3. Pending: unresolve, 表示待解决,既不是resolve也不是reject的状态。也就是promise对象刚被创建后的初始化状态.

resolvereject 用来切换状态
thencatch 用来感知状态

构造promise

let M = new Promise((resolve, reject) => {
    if(done)
        resolve(data.done); //处理成功
    if(!done)
        reject(data.fail); //处理成功
});

这里Promise构造器接受一个函数(一般是 无名函数 ), 这个函数有两个参数
参数是两个函数名, 一般是resolvereject
无名函数的内部就是要运行的函数了
promise 实例构造完成就开始运行
下一轮Event Loop就无效了

原因很简单: 状态显示的不是 Pending, 说明已经完成了

resolve和reject

当调用resolve时, 其参数是要传出去的数据, 其效果是把所在的这个 promise 实例 的状态改成 fulfilled
当调用reject时, 其参数是错误信息, 其效果是把所在的这个 promise 实例 的状态改成 rejected

注意:
+ 这两个函数不能类比 return! 函数还会继续走完
+ 这两个函数只干两件事:
1. 修改状态
2. 传递数据
+ 修改状态只能改未完成的状态, 也就是说, resolvereject 哪个在前面哪个生效

then和catch

他们能感知他们对应的 promise 实例 的状态
他们的参数是回调函数的函数名
M.then(f)或是M.catch(f)
当M转变成Fulfilled状态时,then就被送入 “微任务”
当 “宏任务” 完成后, thenMresolve传递出的数据作为参数传递给回调函数
catch同理

注意: thencatch本身也是 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, "&lt;");
    md = md.replace(/>/gm, "&gt;");

    // 加粗
    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, "&nbsp;&nbsp;");

    // 一级标题
    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)];

参考来源

  1. 廖雪峰的官方网站
  2. 菜鸟教程
  3. 在JavaScript中时间戳
Avatar photo
我是 zhyDaDa

前端/UI/交互/独立游戏/JPOP/电吉他/游戏配乐/网球/纸牌魔术

发表回复