工作上常用方法每次重写,便想封装一起,方便调用,也当作备忘录
时间字符串格式
js中的Date对象原生的方法里,都不适用实际需求:"2022-11-09 12:03:32"
let t = new Date();
console.log(t.toString());//Wed Nov 09 2022 12:14:09 GMT+0800 (中国标准时间)
console.log(t.toLocaleString());//2022/11/9 12:14:09
将斜杠"/"
替换为"-"
,可以初步实现,其中月、日不满10没补0倒是能接受。
new Date().toLocaleString().replace(/\//g,"-");
replaceAll()
方法有的地方不兼容,所以用replace()
。
其中toLocaleString()
方法,显然是转为当地时间,官方将我们所在时区时间格式定为这样。在某些场景格式不对。
- 其他时区运行该代码
- 将浏览器信息设为其他时区
- 其他接入js的API但时区设置为其他地方的。
可以自己写个自定义的方法,直接给到Date的原型链上:
/**
增加Date自定义原型链
给Date实例对象添加toStr()方法,返回年月日时分秒字符串,补0,如 `2022-08-08 17:12:00`
*/
Date.prototype.toStr = function () {
// return this.toLocaleString().replace(/\//g,"-");//本地时间格式,CN时:`2022/8/11 09:22:08`
let y = this.getFullYear();
let M = this.getMonth() + 1;
let d = this.getDate();
let h = this.getHours();
let m = this.getMinutes();
let s = this.getSeconds();
M = M > 9 ? M : ("0" + M);
d = d > 9 ? d : ("0" + d);
h = h > 9 ? h : ("0" + h);
m = m > 9 ? m : ("0" + m);
s = s > 9 ? s : ("0" + s);
return y + "-" + M + "-" + d + " " + h + ":" + m + ":" + s;
}
引入该文件或插入该代码,在后面就能直接:Date().toStr()
,方法名是自定义的,只要不重名即可。
对象深拷贝
javascript对象可分为:基本数据类型、引用数据类型。其中引用引用型数据,其变量指向的其实是引用目标的存储地址。由于js是弱数据类型语言,在打印的时候也看不出来。如果是其他编程语言的话,引用数据会直接显示引用地址,或者对象类。
基本数据类型拷贝
闲话少说,如果是基本数据类型,复制就直接复制了:
let a=1;
let b=a;//1
对象拷贝
如果是对象:
let obj1 = {
a:1,
b:2
}
let obj2 = obj1;
在obj1
被声明的时候,就会给一个存储空间,有自己的存储地址。比如abc@1234
(随便写的,便于理解),那么obj2
被赋值的时候,是把这个存储地址abc@1234
给obj2
,那么每次使用它的时候,程序会根据该地址(好比X省X市X县X小区X号)找到该对象。
深拷贝
以上两段代码就是浅拷贝,使用简单,但有个问题,如果我是想复制一个完整的对象,另外使用呢?
let obj1 = {
a:1,
b:2
}
let obj2 = obj1;
obj1.a=33;
console.log(obj2.a);//33
浅拷贝只是复制了地址,相当于一个人多了个外号,人还是那个人。改变一个变量下面的属性,另一个变量也会跟着改变。
这时候就要用到深拷贝。
let obj1 = {
a:1,
b:2
}
let arr1=[1,2,3];
let obj2 = {...obj1}
let arr2 = [...arr1];
obj1.a="txt";
arr1.push("text");
console.log(obj2.a);//1
console.log(arr2);//[1,2,3]
多层对象拷贝
js原生的深拷贝能解决大部分问题,但如果是复杂结构的对象,就处理不了。
let obj1 = {
a:1,
b:{
name:"test"
}
}
let obj2 = {...obj1}
obj1.a=100;
console.log(obj2.a);//1
obj1.b.name="abc";
console.log(obj2.b.name);//"abc"
以上示例表明:原生方法的“深拷贝”
其实也不够“深”
,它最多只会给第一层的内容重新开辟存储空间,再下一层还是引用数据。
假如现在想要复制一个多层结构的引用型数据,进行另外的操作,不能让两变量互相影响,那么原生方法就不适用了。
递归复制
第一种方法就是:判断子属性是什么类型,若是基本数据类型,直接复制。若是引用数据类型,如果是数组,将数组遍历,递归。如果是普通对象,也是遍历属性名、属性值,再对属性值递归。如果是function
(特殊的对象),就直接复制(这种情况更建议定义一个类)。
这种方法麻烦,而且递归有溢栈、死循环的风险。(主要是麻烦)。
JSON
将数据转JSON字符串,再转js对象,会完全开辟新的存储空间。
function copy (obj) {
return JSON.parse(JSON.stringify(obj));
}
其他
有些插件会自带深拷贝的方法,如jQuery的$.extend()
。请自行探究。
拷贝的目的就是:复制某变量里所有层级内容,且不能互相影响,如果真的结构过于复杂,更建议新定义一个class
。
兼容iOS的时间字符串
在windows系统上的浏览器中,new Date("2022-11-09 14:22:25")
这种格式是可以正常运行的。但是在苹果手机上,只能new Date("2022/11/09 14:22:25")
,它只识别/
而不识别-
(Windows、Android都可以同时识别-
、/
),有时候后台给的数据就是这种格式的话,就会异常(isNaN
)。
所以保险起见,多平台开发时,一律将-
替换为/
再转时间对象。
function dateByLocal(str){
return new Date(str.replace(/-/g,"/"));
}
也可以写个继承类
class Date_ extends Date {
constructor(param) {
if (typeof param === "string") {
param = param.replace(/-/g, "/");
}
super(param);
}
}
在Windows测试的时候,可以把replace里的-
改为~
,然后直接调用console.log(new Date_("2022~11~09 14:52:04"))
。
可以先判断浏览器是否为苹果,再做相应兼容性处理。可参考这篇:JS判断是苹果系统(ios)还是安卓系统(Android)或者PC端
完整代码
在网上找的其他包括未确认的方法汇总如下。有些冗杂的方法有更好的写法,下面没更新,请自行选择。
/*
* 2022-05-19 11:23:00
* tzj
* 将全局通用方法写在该文件,统一标准,统一调用
* */
"use strict";//开启严格模式
/**
* 自定义数据转字符串
* 不建议使用`引用数据类型`的数据:function、object、array
* */
function toStr(data) {
switch (typeof data) {
case "function":
return data.toString();
case "number":
if (data === Infinity) return "∞";
if (data === -Infinity) return "-∞";
return isNaN(data) ? "" : data.toString();
case "object":
if (data === null) {
return "";
} else if (data instanceof Array) {
return data.map(item => {
return toStr(item);
}
).toString();
} else if (data instanceof Date) {
return timeStr(data);
} else if (data instanceof RegExp) {
return data.toString();
} else {
let str = "";
for (let key in data) {
str += key + ":" + toStr(data[key]) + ",";
}
return str;
}
case "string":
return data;
case "symbol":
return data.description ? data.description : "";
// break;
case "undefined":
return "";
// 大精度整数 let bi=BigInt(1);或者let bi=1n;高精度只能强转低精度number
case "bigint":
data.toString();
break;
case "boolean":
return data ? "1" : "0";
default:
return data.toString();
}
}
// 修改原型链
Date.prototype.toStr =function(format){
return timeStr(this,format);
}
/**
时间转字符串
- ***time*** 时间 String、Number、Date
- ***format*** 要显示的格式 String
- 时间错误,则取当前时间
- 格式错误时,默认使用"yyyy-MM-dd hh:mm:ss"
- y 年 2位或4位,不分大小写
- M 月 MM补0
- d 日 dd补0,不区分大小写
- H 时 HH补0,大写24小时制,小写12小时制,Hh、hH为错误格式
- m 分 mm补0
- s 秒 ss补0,不区分大小写
- 间隔内容仅为"年月日时分秒"或"-"、"/"、”:",可搭配使用
*/
function timeStr(time, format) {
let t = isNaN(Date.parse(time)) ? new Date() : new Date(time);
let reg = /([Yy]{4})([-年\/])(M{1,2})([-月\/])([dD]{1,2})(日?) ([Hh]{1,2})([::时])(m{1,2})([::分])([sS]{1,2})(秒?)/g;
let txt = format ? format.match(reg).toString() : "YYYY-MM-DD HH:mm:SS";//默认值
let arr = txt.split(reg);
let y = t.getFullYear().toString();
y = arr[1].match(/y{4}/i) ? y : y.slice(-2);
let M = t.getMonth() + 1;
M = setZero(M, arr[3] === "MM");
let d = t.getDate();
d = setZero(d, arr[5].match(/d{2}/i));
let h = t.getHours();
h = arr[7].match(/H+/) ? h : (h % 12);
h = setZero(h, arr[7].match(/h{2}/i));
let m = t.getMinutes();
m = setZero(m, arr[9] === "mm");
let s = t.getSeconds();
s = setZero(s, arr[11].match(/s{2}/i));
return y + arr[2] + M + arr[4] + d + arr[6] + " " + h + arr[8] + m + arr[10] + s + arr[12];
}
/**
* 待完善:写个递归函数,多传一个位数number(设置上限,避免溢栈),可补0任意位数
* */
function setZero(n, b) {
return !b ? n : n > 9 ? n : ("0" + n);
}
/**
* 返回时间差 时分秒string
* @param time 结束时间
* */
function countdown(time) {
let t = new Date(time).getTime() - new Date().getTime();
if (t <= 0) {
return false;
}
let seconds = Math.floor(t / 1000);
let h = Math.floor(seconds / 3600);
h = h > 0 ? (h + ":") : "";
let m = Math.floor(seconds % 3600 / 60);
let s = seconds % 60;
s = (s < 10 && m > 0) ? ("0" + s) : s;
m = m > 0 ? (m + ":") : "";
return h + m + s;
}
/**
设置倒计时
@param id 显示节点id
@param title 前缀文字
@param time 结束时间
*/
function setCountdown(id, title, time) {
let box = $("#" + id);
let inter = setInterval(() => {
let t = countdown(time);
if (!t) {
clearInterval(inter);
box.hide();
} else {
box.show();
box.html(title + ` <span style="color: red">${t}</span>`);
}
}, 1000)
}
const IMG_TYPE = [".png", ".jpg", ".jpeg", ".gif"];
/**
检测文件名后缀
- 获取str=123.jpg中的`.jpg`,是否包含于数组arr中
@return boolean
*/
function testFile(str, arr) {
arr = arr ? arr : IMG_TYPE;
let type = /(\.[a-zA-Z1-9]+)$/.exec(str)[0];
if (!type) return false;
return arr.indexOf(type.toLowerCase()) > -1;
}
/**
* 1024制,计算文件大小
* */
function getSize(n) {
n = Number(n);
n = isNaN(n) ? 0 : n;
return n < 1024 ? "0Kb" : n < 1024 * 1024 ? ((n / 1024).toFixed(1) + "Kb") : ((n / 1024 / 1024).toFixed(1) + "M");
}
/*
* 重写new Date();兼容iOS 待完善
* */
// const _Date=Date;
// Date=function (){
// if(arguments.length>0&& typeof arguments[0] === "string" && arguments[0].includes("-")){
// arguments[0]=arguments[0].replace(/-/g,"/");
// }
// return new _Date(...arguments);
// }
// new Date("2022-11-23 11:11:23");
/*
* 设置table
* 没做异常判断,请严格根据格式传参
* */
let tableBox = {
id: "",
varList: [],
/*
* 初始化
* @param id table节点id,元素初始结构,有table、thead、tbody
* @param varList 字段显示内容,name(必需):字段名,title(必需):显示标题名,width:当前列的宽度,callback:回调方法
* */
init(id, varList) {
this.id = id;
this.varList = varList;
let str = "";
varList.forEach(item => {
str += `
<td style="${item.width ? ('width:' + item.width) : ''}">${item.title}</td>
`
})
str = `<tr>${str}</tr>`;
$("#" + id + " thead").html(str);
},
getList(data) {
let str = "";
data.forEach(item => {
let trStr = "";
this.varList.forEach(val => {
let value = item[val.name];
trStr += `
<td style="${val.width ? ('width:' + val.width) : ''}">${val.callback ? val.callback(value) : value ? value : ""}</td>
`
})
str += `<tr>${trStr}</tr>`;
})
$("#" + this.id + " tbody").html(str);
}
}
let local = {
get(key) {
if (!window.localStorage) {
alert("该浏览器不支持localStorage!请升级浏览器!");
} else {
return window.localStorage.getItem(key);
}
},
set(key, value) {
if (!window.localStorage) {
alert("该浏览器不支持localStorage!请升级浏览器!");
} else {
window.localStorage.setItem(key, value);
}
}
}
let session = {
get(key) {
if (!window.sessionStorage) {
alert("该浏览器不支持sessionStorage!请升级浏览器!");
} else {
return window.sessionStorage.getItem(key);
}
},
set(key, value) {
if (!window.sessionStorage) {
alert("该浏览器不支持sessionStorage!请升级浏览器!");
} else {
window.sessionStorage.setItem(key, value);
}
}
}
/** 以下是从网上找的,且未验证,有的会报错。*/
// 1) 滚动条:没整明白
// window.scrollTo(0,0);//置顶
// window.scrollTo(0,document.body.scrollHeight);//置底
// 水平,垂直,取值,赋值
function scrollTop(height = null) {
let a = document.documentElement.scrollTop;
let b = window.pageYOfset;
let c = document.body.scrollTop;
if (height === null) {
return a || b || c;
} else {
if (a) {
document.documentElement.scrollTop = height;
} else if (b) {
window.pageYOfset = height;
} else if (c) {
document.body.scrollTop = height;
}
}
}
// document.documentElement.scrollTop || document.body.scrollTop;
// 获取非行内样式兼容
function f1(obj, attr) {
if (window.getComputedStyle) {
return window.getComputedStyle(obj, false)[attr]
} else {
return obj.currentStyle.attr
}
}
// 获取非行内样式(兼容问题)
function getStyle(obj, attr) { //获取非行间样式,obj是对象,attr是值
if (obj.currentStyle) { //针对ie获取非行内样式
return obj.currentStyle[attr];
} else {
return getComputedStyle(obj, false)[attr]; //针对非ie
}
}
// 3) 网页可视区域兼容
window.innerHeight || document.documentElement.clientHeight
window.innerWidth || document.documentElement.clientWidth
// 4)事件对象兼容
// let e = e || window.event
// 5)阻止事件冒泡兼容
// e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true
// 6)阻止默认行为兼容
// e.preventDefult ? e.preventDefult() : e.returnValue = false
// 7)事件监听兼容
// obj.addEventListener("事件去掉on", function () {
// }, 布尔值)
// obj.attachEvent("事件带on", function () {
// }) //IE
// 8)事件目标对象兼容
// var t = event.target || event.srcElement;
// 10)获取键盘的按键
// IE8以及以下版本不兼容which
// var x = event.which || event.keyCode
// var x = e.which || e.keyCode
//处理innerText和textContent
function getInnerText(element) {
//判断element是否支持innerText
if (typeof element.innerText === 'string') {
return element.innerText;
} else {
return element.textContent;
}
}
//设置元素之间的内容
function setInnerText(element, content) {
if (typeof element.innerText === 'string') {
element.innerText = content;
} else {
element.textContent = content;
}
}
//处理firstElementChild的兼容性
function getFirstElementChild(parent) {
//如果当前浏览器支持firstElementChild
if (parent.firstElementChild) {
return parent.firstElementChild;
}
var node, nodes = parent.childNodes, i = 0;
while (node = nodes[i++]) {
if (node.nodeType === 1) {
return node;
}
}
return null;
}
//注册事件,处理兼容性问题
function addEventListener(element, eventName, callback) {
if (element.addEventListener) {
element.addEventListener(eventName, callback, false);
} else if (element.attachEvent) {
element.attachEvent('on' + eventName, callback);
} else {
element['on' + eventName] = callback;
}
}
//移除事件,处理兼容性问题
function removeEventListener(element, eventName, callback) {
if (element.removeEventListener) {
element.removeEventListener(element, callback, false);
} else if (element.detachEvent) {
element.detachEvent('on' + eventName, callback);
} else {
element['on' + eventName] = null;
}
}
//获取页面滚动的距离,并处理兼容性
function getScroll() {
return {
scrollTop: document.documentElement.scrollTop || document.body.scrollTop,
scrollLeft: document.documentElement.scrollLeft || document.body.scrollLeft
}
}
//获取鼠标在页面上的坐标 解决IE8的问题
function getPage(e) {
return {
pageX: e.clientX + getScroll().scrollLeft,
pageY: e.clientY + getScroll().scrollTop
}
}