这个代码库全部是自己平时工作中总结出来的,还有一些是在网上浏览各种博客时整理出来的。有需要的朋友可以作为参考,某些地方如果有误,还请各位留言指出。
2018-10-25
.1.js中入门/入口函数的写法//前提条件需要引入jQuery
$(document).ready(function() {
//初始化时需要做的一些事情
//如果需要初始化多个函数,并且需要让这几个函数按照顺序执行,则可以采取如下的方式,使用一个立即执行函数
(async () => {
await a();//初始化时执行的函数a
await b();//初始化时执行的函数b
await c();//初始化时执行的函数c
})();
//还有一种初始化方式
当页面初始化时,有多个方法,而一个方法初始化时需要使用另外一个方法初始化的结果,这种情况就可以使用下面这种方式初始化 初始化汇总//这里有一点需要注意,async/await//函数是在ES7的标准规范中才正式确立下来//要不要在ES6中使用还需慎重//不过这种写法倒是值得推介//
initSummary().then(result => {
console.log(`initSummary: ${result}`);
}).catch(err => {
msgError(`${err}`);
console.log(`initSummary Error! ${err}`);
});
//初始化汇总数据
async function initSummary() {
try {
// 初始化通知
await initNotice();
// 初始化待办事项
await initTodo();
// 初始化地图
await initMap();
return Promise.resolve("success");
} catch (e) {
return Promise.reject(e);
}
}
});
.2.js中当键盘按下enter键时,执行一个js方法的方式//最好放在body标签中的onload方法中,这样的话它监听的是整个页面,否则可能导致的问题是方法 失效。
function search() {
if(!window.event){
e = e.which;
}else{
e = window.event.keyCode;
}
if(e==13||e==42) {
doSearch();
}
}
.3.js中一个基本的ajax请求添加时间戳的方式//刷新验证码时可以使用//
(a)url: url + '&random = ' + Math.random()’;
(b)var timestamp = new Date().getTime();
(c)var timestamp = Date.parse(new Date());
在页面写js代码时,当某个操作需要调用函数进行同步操作时,可js本身却是异步执行的,则可以采取下面的方式来解决:
(async () => {
//处理数据
let rs = await handler_distinguish_count(_granularity, result.datas.result);
show_distinguish_count(_granularity, rs);
})();
即是封装一个立即执行函数,并且将这个立即执行函数里面使用async/await操作来讲异步操作变为同步操作。这种方式很巧妙的解决了js中调用函数时,是异步执行的问题。
.4.几个常用的正则表达式
(a)手机号码校验:var regPartton=/1[3-8]+\d{9}/;
//固定电话校验
function isFTel(tel){
let phone1 = /^0\d{2,3}-?\d{7,8}$/;
return phone1.test(tel);
}
IP地址校验//
let reg = /\d+\.\d+\.\d+\.\d+/;
let ip_address = req.ip.match(reg);
邮箱校验//
密码校验//包含大、小写字母,数字和特殊字符
let reg = /^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9])(?=.*?[-!$%^&*()_+|~=`{}\[\]:";'<>?,.\/]).{8,18}$/;
密码的强度必须是包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间。
let reg = /^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$/;
//数字校验
js中将字符串转换为数字的完整的方法:Number.isNaN('字符串');
中文校验。
字符串仅能是中文。
let reg = /^[\\u4e00-\\u9fa5]{0,}$/;
校验由数字、26个英文字母或下划线组成的字符串。
let reg = /^\\w+$/;
校验。校验金额
金额校验,精确到2位小数。
let reg = /^[0-9]+(.[0-9]{2})?$/;
(b)身份证校验第一种方式
var reg = /^[1-9]{1}[0-9]{14}$|^[1-9]{1}[0-9]{16}([0-9]|[xX])$/;
if(!reg.test(val)) {
alert("请输入正确身份证号!");
return false;
}
//身份证校验第二种方式
function checkIDVCard(id) {
id=id.toUpperCase();
var _isIDCard = /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/;
return _isIDCard.test(id);
}
手机号码校验。
var regex = /^1[34578]\d{9}$/;
.5.js中判断是否为数组的方法
js中判断变量是否为数组的方法:Array.isArray('需要判断的对象');
.6.js中获取当前页面中URL中的参数的方法//这个方法经常使用//
function getUrlParam(name){
var nk="";
var reg=new RegExp("(^|&)"+name+"=([^&]*)(&|$)");
var r=window.location.search.substr(1).match(reg);
if (r!=null) return unescape(r[2]);return nk;
};
.7.js中打开一个弹出窗口的方法//使用easyui时才能使用//
首先页面中需要有一个div,调用此方法时,传入两个参数:一个是打开的地址,一个是页面显示的标题//
$("#detailNewWin").window({
width:'90%',
height:'90%',
modal:true,
href:url,
title:'新窗口详情',
collapsible:false,
minimizable:false,
maximizable:false
});
2019-01-14
.8.判断浏览器是否为IE浏览器//https://www.jianshu.com/p/92b28d4e79be
function isIE(){
if(!!window.ActiveXObject || "ActiveXObject" in window)
return true;
else
return false;
}
.9.使用原生的JS代码打开一个新的窗口
/**
* 打开窗口
* @param url
* @param title
*/
function openWin(url,title){
window.open(url,"maxwindow",'toolbar=no,location=no,directories=no,menubar=no,scrollbars=yes,resizable=no,status=no');
}
.10.判断字符中是否有空格,换行符//
function isBlankChar(c){
return c==' '||c=='\f'||c=='\n'||c=='\r'||c=='\t';
}
.11.js获取系统当前时间2019-05-04 16:39:40//
function getCurTime() {
var now = new Date();
var year=""+now.getFullYear();
var month=now.getMonth()+1;
if(month<10){month="0"+month;}
var day = now.getDate();
if(day<10){day = "0"+day;}
var hours = now.getHours();
if(hours<10){hours = "0"+hours;}
var minutes = now.getMinutes();
if(minutes<10){minutes = "0"+minutes;}
var seconds = now.getSeconds();
if(seconds<10){seconds = "0"+seconds;}
return year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":"+ seconds;
}
.12.网址校验方式一//
function IsURL(str_url){
var strRegex = "^((https|http|ftp|rtsp|mms)?://)"
+ "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" //ftp的user@
+ "(([0-9]{1,3}\.){3}[0-9]{1,3}" // IP形式的URL- 199.194.52.184
+ "|" // 允许IP和DOMAIN(域名)
+ "([0-9a-z_!~*'()-]+\.)*" // 域名- www.
+ "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\." // 二级域名
+ "[a-z]{2,6})" // first level domain- .com or .museum
+ "(:[0-9]{1,4})?" // 端口- :80
+ "((/?)|" // a slash isn't required if there is no file name
+ "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$";
var re=new RegExp(strRegex); //re.test()
if (re.test(str_url)){
return (true);
}else{
return (false);
}
}
.13.//由18位的合法身份证得到性别
function getSexByCode(idCode){
var sex = idCode.charAt(16);
if(sex.valueOf()%2 == 1){
return "1";//男
}else if(sex.valueOf()%2 == 0){
return "2";//女
}
return "";
}
.14.整数或者是小数校验
function check_number1(e){
var check = /^[-\+]?\d+(\.\d+)?$/;
if(e.value != ""){
if(!check.test(e.value)){
alert("必须输入合法的数字,整数或小数!");
}
}
}
金额校验
function check_money(e){
var check = /^\d+(\.\d+)?$/;
if(e.value != ""){
if(!check.test(e.value)){
alert("必须输入合法的金额!");
e.value="";
e.focus();
}
}
}
.15.判断文件是不是图片
function isPicture(fileName) {
if (fileName && fileName.lastIndexOf(".") != -1) {
var fileType = (fileName.substring(fileName.lastIndexOf(".") + 1,
fileName.length)).toLowerCase();
var isImg = new Array("jpg","gif","bmp","png","jpeg");
for ( var i = 0; i < isImg.length; i++) {
if (isImg[i] === fileType) {
return true;
} else {
continue;
}
}
return false;
} else {
return false;
}
}
.16.获取页面中的表单数据
function getFormData(){
var rtn = new Object();
// input元素的值
$("input[type=text]").each(function(){
var element=$(this);
if ((element.attr("id")!=null)&&(element.attr("id")!="")){
var val = element.val();
if(val != ""){
if(val.indexOf("+")>-1){
val = val.replace("+","");
}
}
var i_d= element.attr("id");
if(i_d.indexOf("param")>-1){
val = trimStr(val,"g");
rtn[element.attr("id")] = val;
}else{
rtn[element.attr("id")] = element.val();
}
}
});
$("input[type=hidden]").each(function(){
var element=$(this);
if ((element.attr("id")!=null)&&(element.attr("id")!="")){
rtn[element.attr("id")]=element.val();
}
});
// input元素的值
$("input[type=password]").each(function(){
var element=$(this);
if ((element.attr("id")!=null)&&(element.attr("id")!="")){
rtn[element.attr("id")]=element.val();
}
});
// 获取checkbox的值
$('input[type=checkbox]').each(function(){
var element=$(this);
if (element.attr("checked")==true){
if ((element.attr("id")!=null)&&(element.attr("id")!="")){
rtn[element.attr("id")]=element.val();
}
}
});
$("input[type=radio]").each(function(){
var element=$(this);
if (element.attr("checked")==true){
if ((element.attr("name")!=null)&&(element.attr("name")!="")){
rtn[element.attr("name")]=element.val();
}
}
});
// 获取textarea的值
$("textarea").each(function(){
var element=$(this);
if ((element.attr("id")!=null)&&(element.attr("id")!="")){
rtn[element.attr("id")]=element.val();
}
});
// 获取select值
$("select").each(function(){
var element=$(this);
if ((element.attr("id")!=null)&&(element.attr("id")!="")){
rtn[element.attr("id")]=element.val();
}
});
return rtn;
}
.17.清空页面中所有的表单数据
function resetContent(id){
var vaildForm=$('#'+id);
vaildForm.find("input, select, textarea").val("");
vaildForm.find('input[type="checkbox"]').each(function(){
$(this).attr("checked",false);
});
}
.18.获取当前窗口的宽度和高度
function getHeight(){
var winHeight=500;
//获取窗口高度
if(window.innerHeight){
winHeight=window.innerHeight;
}else if((document.body)&&(document.body.clientHeight)){
winHeight=document.body.clientHeight;
}
//通过深入Document内部对body进行检测,获取窗口大小
if(document.documentElement && document.documentElement.clientHeight && document.documentElement.clientWidth){
winHeight=document.documentElement.clientHeight;
}
return winHeight;
}
function getWidth(){
var winWidth=500;
//获取窗口高度
if(window.innerWidth)
winWidth=window.innerWidth;
else if((document.body)&&(document.body.clientWidth))
winWidth=document.body.clientWidth;
//通过深入Document内部对body进行检测,获取窗口大小
if(document.documentElement && document.documentElement.clientHeight && document.documentElement.clientWidth){
winWidth=document.documentElement.clientWidth;
}
return winWidth;
}
.19.打开一个新的对话框
function opendialog(url,iwidth,iheight){
var iTop = (window.screen.availHeight-30-iheight)/2; //垂直居中
var iLeft = (window.screen.availWidth-100-iwidth)/2; //水平居中
var result=
window.open(url,"_blank","width="+iwidth+"px,Top="+iTop+",Left="+iLeft+",height="+iheight+"px,,modal=yes,status=no,toolbar=no,scrollbars=yes,location=no,Resizable=no,directions=no");
window.οnfοcus=function (){
if (result!=null){
try{
result.focus();
} catch(e){
}
}
};
window.οnclick=function (){
if (result!=null){ try{
result.focus();
} catch(e){
}}};
return result;
}
.20.js中Math库的应用
max() 方法可返回两个指定的数中带有较大的值的那个数。
项目应用实例
// 获取最大值
let maxVal = Math.max(...data.map(item => {
return item.value;
})
);
其中data为一个数组。
.21.ES6中map函数的用法//就类似于处理返回的数据,将数据处理成自己想要的方式。
resultData为一个数组
resultData = resultData.map(function (v) {
return {
name: v.area_name,
value: formatPrecent(v.area_rate),
};
});
.22.可以借鉴的一个方法//进入/退出全屏
//退出全屏
function exitFullScreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
}else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
}else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
//进入全屏
function fullScreen() {
let el = document.documentElement;
let rfs = el.requestFullScreen || el.webkitRequestFullScreen || el.mozRequestFullScreen || el.msRequestFullscreen;
if (rfs && typeof rfs !== "undefined") {
rfs.call(el);
}
}
.23.async/await是在ES7的语法中才出现的//
async后面返回一个promise对象//
await后面接受一个promise实例//
页面跳转的方式//
跳转到新页面
(1)可以采用如下的方式//
window.open("http://192.168.11.136:13000/project/api/front/course_out/quiz_in?exam_id=" + exam_id);
(2)使用a标签点击跳转//
.24.
es6常用语法
1.map 常用于对数组里所有元素做同样操作后返回新的数组。 有返回值。
使用方法:[].map(function(item, index, array){
})
2.filter:过滤掉不符合条件的数组元素,返回所有符合条件的数组元素组成的新的数组。有返回值。
使用形式:[].filter(function(){
})
.25.JS判断某个变量是否为数字,可以使用函数isNaN()函数//
JS代码中欧冠常用的写法//当某个传入的变量没有值的时候,则给出一个默认值//
写函数时的一个注意事项//始终认为用户调用函数时,传递的参数不是按照正常调用来传递,
需要对传入的参数做相应的处理//
function getSum(a, b){
if(!a){
a = 0;
}
}
.26.js中60S倒计时的设置//
let send = 60;
var interval = setInterval(function(){
send--;
if(send < 0){
clearInterval(interval);
$('#send_message').attr('disabled', false);
}
},1000);
.27.利用jquery删除元素中内容的方法//
$(".btn1").click(function(){
$("p").empty();
});
.28.
格式化日期的方法,直接使用即可//
var date=new Date();
var year = (date.getYear() < 1900) ? date.getYear() + 1900 : date.getYear();
var o = {
"M+" : date.getMonth() + 1, //month
"d+" : date.getDate(), //day
"h+" : date.getHours(), //hour
"m+" : date.getMinutes(), //minute
"ss" : date.getSeconds(), //second
"ms" : date.getMilliseconds()
};
.29.日期格式检查
/*
* 日期校验
* @param date 日期 2019-07-18 12:23:20
* @return Boolean
*/
function isdate(date) {
let result = date.match(/(\d{4})-(\d{1,2})-(\d{1,2})/);
if (null == result){
return false;
}
let d = new Date(result[1], result[2] - 1, result[3]);
let rs = (d.getFullYear() == result[1] && d.getMonth() + 1 == result[2] && d.getDate() == result[3]);
return rs;
}
再次也顺便写一下,在写函数时,一定要对传入的参数进行有效性的检查,这是开发中的一个好习惯。
.30.
/**
* 创建多级目录
* 递归创建文件夹
* @param dirName
*/
exports.mkdirs = function(dirName) {
if (fs.existsSync(dirName)) {
return;
} else {
try {
fs.mkdirSync(dirName);
} catch (e) {
this.mkdirs(path.dirname(dirName));
}
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName);
}
}
};
.31.判断数组中是否存在某个元素的简单方法:
方法一:indexOf(item,start);
.32.
函数式编程//
把字符串 functional programming is great 变成每个单词首字母大写//代码示例
var string = 'functional programming is great';
var result = string .split(' ') .map(v => v.slice(0, 1).toUpperCase() + v.slice(1)) .join(' ');
console.log(result)
上面的例子先用 split 把字符串转换数组,然后再通过 map 把各元素的首字母转换成大写,最后通过 join 把数组转换成字符串。 整个过程就是 join(map(split(str))),体现了函数式编程的核心思想: 通过函数对数据进行转换。
由此我们可以得到,函数式编程有两个基本特点:
- 通过函数来对数据进行转换
- 通过串联多个函数来求结果
//声明式
var CEOs = companies.map(c => c.CEO);
从上面的例子中,我们可以看到声明式的写法是一个表达式,无需关心如何进行计数器迭代,返回的数组如何收集,它指明的是做什么,而不是怎么做。函数式编程的一个明显的好处就是这种声明式的代码,对于无副作用的纯函数,我们完全可以不考虑函数内部是如何实现的,专注于编写业务代码。
涨见识了//
了解性知识//函数是一等公民
我们常说函数是JavaScript的"第一等公民",指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
.1.闭包(Closure)
如果一个函数引用了自由变量,那么该函数就是一个闭包。何谓自由变量?自由变量是指不属于该函数作用域的变量(所有全局变量都是自由变量,严格来说引用了全局变量的函数都是闭包,但这种闭包并没有什么用,通常情况下我们说的闭包是指函数内部的函数)。
闭包的形成条件:
- 存在内、外两层函数
- 内层函数对外层函数的局部变量进行了引用
闭包的用途: 可以定义一些作用域局限的持久化变量,这些变量可以用来做缓存或者计算的中间量等.
代码示例//
// 简单的缓存工具
// 匿名函数创造了一个闭包
const cache = (function() {
const store = {};
return {
get(key) {
return store[key];
},
set(key, val) {
store[key] = val;
}
}
}());
console.log(cache) //{get: ƒ, set: ƒ}
cache.set('a', 1);
cache.get('a'); // 1
上面例子是一个简单的缓存工具的实现,匿名函数创造了一个闭包,使得 store 对象 ,一直可以被引用,不会被回收。
闭包的弊端:持久化变量不会被正常释放,持续占用内存空间,很容易造成内存浪费,所以一般需要一些额外手动的清理机制。
.2.高阶函数//map
map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。map 不会改变原数组。
假设我们有一个包含名称和种类属性的对象数组,我们想要这个数组中所有名称属性放在一个新数组中,如何实现呢?
代码示例//
// 使用高阶函数
var animals = [
{ name: "Fluffykins", species: "rabbit" },
{ name: "Caro", species: "dog" },
{ name: "Hamilton", species: "dog" },
{ name: "Harold", species: "fish" },
{ name: "Ursula", species: "cat" },
{ name: "Jimmy", species: "fish" }
];
var names = animals.map(x=>x.name);
console.log(names); //["Fluffykins", "Caro", "Hamilton", "Harold", "Ursula", "Jimmy"]
//让我不得不佩服这种操作方式,简单、整洁//
filter
filter() 方法会创建一个新数组,其中包含所有通过回调函数测试的元素。filter 为数组中的每个元素调用一次 callback 函数, callback 函数返回 true 表示该元素通过测试,保留该元素,false 则不保留。filter 不会改变原数组,它返回过滤后的新数组。
假设我们有一个包含名称和种类属性的对象数组。 我们想要创建一个只包含狗(species: "dog")的数组。如何实现呢?
代码示例//
// 使用高阶函数
var animals = [
{ name: "Fluffykins", species: "rabbit" },
{ name: "Caro", species: "dog" },
{ name: "Hamilton", species: "dog" },
{ name: "Harold", species: "fish" },
{ name: "Ursula", species: "cat" },
{ name: "Jimmy", species: "fish" }
];
var dogs = animals.filter(x => x.species === "dog");
console.log(dogs); // {name: "Caro", species: "dog"}
// { name: "Hamilton", species: "dog" }
reduce
reduce 方法对调用数组的每个元素执行回调函数,最后生成一个单一的值并返回。 reduce 方法接受两个参数:1)reducer 函数(回调),2)一个可选的 initialValue。
假设我们要对一个数组的求和:代码示例//
// 使用高阶函数
const arr = [5, 7, 1, 8, 4];
const sum = arr.reduce((accumulator, currentValue) => accumulator + currentValue,0);
console.log(sum)//25
我们可以通过下图,形象生动展示三者的区别:
.3.面试加分项// 所以大家常说的 Node 是单线程的指的是 JavaScript 的执行是单线程的(开发者编写的代码运行在单线程环境中),但 Javascript 的宿主环境,无论是 Node 还是浏览器都是多线程的;
.4.javaScript中数组常用的方法//
join()方法把一个数组中的元素连接成一个字符串//
split()方法标石切割字符串//
reverse()函数表示反转数组中的元素;
sort()方法表示对数组进行排序,
代码示例//
let num = [25, 100, 23]
console.log(num.sort((a, b) => {
return a - b
}))
//[ 23, 25, 100 ]
可以使用比较函数来解决这一问题,这种方法将函数传递给语法: function(a, b){return a — b}
.5.立即执行函数
立即执行函数,即Immediately Invoked Function Expression (IIFE),正如它的名字,就是创建函数的同时立即执行。它没有绑定任何事件,也无需等待任何异步操作:
(function() {
// 代码
// ...
})();
function(){…}是一个匿名函数,包围它的一对括号将其转换为一个表达式,紧跟其后的一对括号调用了这个函数。立即执行函数也可以理解为立即调用一个匿名函数。立即执行函数最常见的应用场景就是:将var变量的作用域限制于你们函数内,这样可以避免命名冲突。//还用于需要同步执行时,使用async/await、、
2019-08-25
多个异步函数的异步回调机制
可通过 Promise.all 来等待多个异步函数完成。
await Promise.all([anAsyncCall(), thisIsAlsoAsync(), oneMore()]) ;
2019-08-27
.1.浮点数转换整形
当需要将浮点数转换成整型时,应该使用Math.floor()或者Math.round()。而不是使用Number.parseInt(),该方法用于将字符串转换成数字。而且Math是内部对象,所以Math.floor()其实并没有多少查询方法和调用时间,速度是最快的。
.2.字符串转换
当需要将数字转换成字符时,采用如下方式:"" + 1。从性能上来看,将数字转换成字符时,有如下公式:("" +) > String() > .toString() > new String()。String()属于内部函数,所以速度很快。而.toString()要查询原型中的函数,所以速度逊色一些,new String()需要重新创建一个字符串对象,速度最慢。
.3.少用eval
尽量少使用eval,每次使用eval需要消耗大量时间,这时候使用JS所支持的闭包可以实现函数模板。
.4.循环比较
JS提供了三种循环:for(;;)、while()、for(in)。在这三种循环中 for(in)的效率最差,因为它需要查询Hash键,因此应尽量少用for(in)循环,for(;;)、while()循环的性能基本持平。
.5.直接使用字面量
创建对象和数组推荐使用字面量,因为这不仅是性能最优也有助于节省代码量。
var obj = {
name:'tom',
age:15,
sex:'男'
}
.6.字符串连接
str += 'one'+'two';
str= str+'one'+'two';
后者方式会比前者少在内存中创建一个临时字符串,所以性能有相应的提升,所以,所以推荐后者的写法。
Node删除多级目录//
function delete_dir(dirPath) {
if(fs.existsSync(dirPath)){
fs.readdirSync(dirPath).forEach(function (file) {
let curPath = path.join(dirPath, file);
if(fs.statSync(curPath).isDirectory()) {
//删除文件夹
delete_dir(curPath);
} else {
//删除文件
fs.unlinkSync(curPath);
}
});
//删除当前文件夹
fs.rmdirSync(dirPath);
}
}
2019-11-04
页面跳转时,对中文参数进行编码和解码的处理方式:
.a.传递参数之前进行编码:特别注意,需要编码两次才能生效;
let params = `cable_id=${r.cable_id}&county_name=${encodeURI(encodeURI(r.county_name))}&area_code=${area_code}`;
.b.获取地址栏中的参数时进行解码处理:
let county_name = decodeURI(getUrlParam('county_name'));
2019-12-23
Node.js中拼接路径时一定要使用path.join(),其会根据不同的系统来自行处理路径,避免出现路径出错的情况。
重要的事情要说三遍//
Node.js中拼接路径时一定要使用path.join();
Node.js中拼接路径时一定要使用path.join();
Node.js中拼接路径时一定要使用path.join()