js 随手记
ASkyBig opened this issue · 0 comments
ASkyBig commented
Array.prototype.slice()
- MDN上的slice()方法介绍:Array.prototype.slice() 方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。
原数组不会改变应该针对简单类型:
var arr = [1, 2, 3];
var arr_new = arr.slice(1) // arr_new: [2, 3], arr: [1, 2, 3]
arr_new[0] = 0; // arr_new: [0,3], arr: [1, 2, 3]
要是是个复杂类型就会改变:
var arr = [{a: 1}, {b: 2}, {c: 3}]
var arr_new = arr.slice(1) // arr_new: [{b: 2}, {c: 3}], arr: [{a: 1}, {b: 2}, {c: 3}]
arr_new[0].b = 0; // arr_new: [{b: 0}, {c: 3}], arr: [{a: 1}, {b: 0}, {c: 3}]
JSON.parse(JSON.stringify()) 实现深拷贝要小心呐
var target = {
a: 1,
b: 2,
hello: function() {
console.log("Hello, world!");
}
};
var target_new = JSON.parse(JSON.stringify(target));
console.log(target_new); // {a: 1, b: 2}
判断当前浏览器是否支持webP格式
function checkWebp() {
try {
return document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0
}
catch(err) {
return false
}
}
类不存在变量提升(hoist),这一点与 ES5 完全不同。 ES6 不会把类的声明提升到代码头部。这种规定的原因与继承有关,必须保证子类在父类之后定义。
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
数组一维扁平化
function flatten(arr) {
return arr.reduce((result, item) => {
return result.concat(Array.isArray(item) ? flatten(item) :item;
}, [])
}
判断空对象
JSON.stringify(obj) === '{}'
for in 是遍历对象的可枚举属性,两点:1、可枚举,2、属性,不是属性值。
chrome 控制台输出的数据,你可用 copy(data)
复制到剪切板
js map 方法不会对空数组进行检测,也不会改变原数组。
var arr =[];
arr[1] = 2;
arr[2] = 3;
var newArr = arr.map(value => ++value); // newArr: [empty, 3, 4], arr: [empty, 2, 3]
距今天多久的代码段
const timeFormat1 = ( timestamp ) => {
let nowTime = new Date().getTime()
let duration = (nowTime - timestamp) / 1000
let result
switch (true) {
case duration <= 60: {
result = '刚刚'
break
}
case duration <= 60 * 60: {
let time = Math.ceil(+`${duration / 60}`)
result = `${time}分钟前`
break
}
case duration <= 60 * 60 * 24: {
let time = Math.ceil(+`${duration / 60 / 60}`)
result = `${time}小时前`
break
}
case duration <= 60 * 60 * 24 * 30: {
let time = Math.ceil(+`${duration / 60 / 60 / 24}`)
result = `${time}天前`
break
}
case duration <= 60 * 60 * 24 * 30 * 12: {
let time = Math.ceil(+`${duration / 60 / 60 / 24 / 30}`)
result = `${time}个月前`
break
}
default: {
let time = Math.ceil(+`${duration / 60 / 60 / 24 / 30 / 12}`)
result = `${time}年前`
}
}
return result
}
const timeFormat2 = ( timestamp ) => {
let nowTime = new Date().getTime()
let todayBeginTime = new Date().setHours(0,0,0,0) // 今天零点的时间戳
let yesTime = nowTime - 1000 * 60 * 60 *24 // 获取昨天这个时候的时间
let yesBeginTime = new Date(yesTime).setHours(0,0,0,0) // 昨天零点的时间戳
let beforeYesTime = new Date(nowTime) - 1000 * 60 * 60 * 24 * 2 // 获取前天这个时候的时间
let beforeYesBeginTime = new Date(beforeYesTime).setHours(0,0,0,0) // 前天零点的时间戳
let thisYearTime = new Date(new Date().getFullYear() + '-01-01').getTime() // 今年的一月一号时间戳
let duration = (nowTime - timestamp) / 1000
let result
switch (true) {
case duration < 60: {
result = '刚刚'
break
}
case duration < 60 * 60: {
let time = Math.ceil(+`${duration / 60}`)
result = `${time}分钟前`
break
}
case timestamp > todayBeginTime: {
let hour = new Date(timestamp).getHours()
let min = new Date(timestamp).getMinutes()
result = `今天 ${hour > 9 ? hour : `0${hour}`}:${min > 9 ? min : `0${min}`}`
break
}
case timestamp > yesBeginTime: {
let hour = new Date(timestamp).getHours()
let min = new Date(timestamp).getMinutes()
result = `昨天 ${hour > 9 ? hour : `0${hour}`}:${min > 9 ? min : `0${min}`}`
break
}
case timestamp > beforeYesBeginTime: {
let hour = new Date(timestamp).getHours()
let min = new Date(timestamp).getMinutes()
result = `前天 ${hour > 9 ? hour : `0${hour}`}:${min > 9 ? min : `0${min}`}`
break
}
case timestamp > thisYearTime: {
let mon = new Date(timestamp).getMonth() + 1
let date = new Date(timestamp).getDate()
let hour = new Date(timestamp).getHours()
let min = new Date(timestamp).getMinutes()
result = `${mon > 9 ? mon : `0${mon}`}-${date > 9 ? date : `0${date}`} ${hour > 9 ? hour : `0${hour}`}:${min > 9 ? min : `0${min}`}`
break
}
default: {
let year = new Date(timestamp).getFullYear()
let mon = new Date(timestamp).getMonth() + 1
let date = new Date(timestamp).getDate()
result = `${year}-${mon > 9 ? mon : `0${mon}`}-${date > 9 ? date : `0${date}`}`
}
}
return result
}
12、hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)
13、npm install -g 的安装目录 user/local/lib/node_modules
14、后端返回的 json 中的整型数据到 js 会丢失精度,记得用 bigInt 或者字符串
15、for in 和 for of
let aArray = ['a',123,{a:'1',b:'2'}]
for(let index in aArray){
console.log(`${aArray[index]}`);
}
for(var value of aArray){
console.log(value);
}
aArray.name = 'demo'
for(let index in aArray){
console.log(`${aArray[index]}`);
}
for(var value of aArray){
console.log(value);
}
16、清空数组的小技巧
let arr = [1, 2, 3]
arr.length = 0
// arr: []
17、map 和 foreach 的区别
- map 依次执行后会返回一个数组
- foreach 只会执行回调,并返回 undefined
18、setTimeout 有好多参数
一直都用了两个,其实后面可以加参数:
function foo(p1, p2) {
console.log('p1==', p1, 'p2==', p2)
}
setTimeout(foo, 2000, 'a', 'b')
// p1== a p2== b
19、利用 Math.random() 生成 1~16 位随机数字
function randomNum(len) {
return Math.random().toString().substr(2, len) // 2 是因为去掉前面的 '0.'
}
20、驼峰命名和中划线相互转换
// "abcDefGh"
"abc-def-gh".replace(/-\w+/g, item => item.charAt(1).toUpperCase() + item.slice(2))
// "abc-def-gh"
"abcDefGh".replace(/[A-Z]/g, item => '-' + item.toLowerCase())
21、数字千位分隔符
// 12345678 ===> 12,345,678
// way 1
'12345678'.replace(/(?=(\d{3})+$)/g, ',')
// way 2
'12345678'.split('').reverse().join('').replace(/\d{3}/g, item => item + ',').split('').reverse().join('')
// way 3
Number(12345678).toLocaleString()
22、String.raw
// "Hi\n5!"
String.raw`Hi\n${2+3}!`
// "Hi
// 5!"
`Hi\n${2+3}!`
23、不要直接用 obj.hasOwnProperty([property])
// 如果对象里面有这个属性则会报错
var obj = { hasOwnProperty: 1, a: 1 }
obj.hasOwnProperty('a') // Uncaught TypeError: obj.hasOwnProperty is not a function
如果恶意的客户端传给服务器的 json 里面有类似的字段,则会导致服务奔溃。
正确的方式:Object.prototype.hasOwnProperty.call(obj, 'a') // true
24、console.log({a})可以输出键值对
var a = 1
console.log({a})
// {a: 1}
25、计算字符串的字节
new Blob(['a']).size // 1
new Blob([1, 2, 3]).size // 3
26、setTimeout
浏览器的页面是通过消息队列和事件循环系统来驱动的。settimeout 的函数会被加入到延迟消息队列中,
等到执行完 Task 任务之后就会执行延迟队列中的任务。然后分析几种场景下面的 setimeout 的执行方式。
- 如果执行一个很耗时的任务,会影响延迟消息队列中任务的执行
- 存在嵌套带调用时候,系统会设置最短时间间隔为 4s(超过 5 层)
- 未激活的页面,setTimeout 最小时间间隔为 1000ms
- 延时执行时间的最大值 2^31 - 1 = 2147483647,溢出会导致定时器立即执行
- setTimeout 设置回调函数 this 会是回调时候对应的 this 对象,可以使用箭头函数解决
var name= 1;
var MyObj = {
name: 2,
showName: function(){
console.log(this.name);
}
}
setTimeout(MyObj.showName,1000)
// 方法一 箭头函数
setTimeout(() => {
MyObj.showName()
}, 1000);
//或者function函数
setTimeout(function() {
MyObj.showName();
}, 1000)
// 方法二
setTimeout(MyObj.showName.bind(MyObj), 1000)
27、When will requestAnimationFrame be executed?
28 根据 [0, 7) || (0, 7] 判断星期几
function getWeekDay(n) {
return '星期' + ['日', '一', '二', '三', '四', '五', '六'][n%7]
}
29、循环请求
命令式:
for (let i = 0; i < 5; i++) {
$.post('/api/' + i, data[i])
}
函数式:
[1,2,3,4,5].forEach(item => {
$.post('/api' + item, data[item])
})
30、Unix时间戳和js时间戳不一样
unix是按照秒算的,js的是毫秒,所以会导致后台传过来的时候显示成1970年
31、timestamp format
// 格式化时间
const dateTimeFormat = (time, fmt) => {
const t = new Date(time)
let o = {
"M+": t.getMonth() + 1, //月份
"D+": t.getDate(), //日
"h+": t.getHours(), //小时
"m+": t.getMinutes(), //分
"s+": t.getSeconds(), //秒
"q+": ~~((t.getMonth() + 3) / 3), //季度
"S": t.getMilliseconds() //毫秒
}
if (/(y+)/i.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (t.getFullYear() + "").substr(4 - RegExp.$1.length))
}
for (let k in o) {
if (new RegExp("(" + k + ")").test(fmt))
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)))
}
return fmt
}
32、随机色
const randomColour = () => '#'+(Math.random()*0xFFFFFF<<0).toString(16);
33、判断浏览器是否支持 passive
:
// 检查浏览器是否支持 passive 属性
export let passiveSupported = false;
try {
var options = Object.defineProperty({}, 'passive', {
get: function () {
passiveSupported = true;
return passiveSupported
}
});
window.addEventListener('test', null, options);
} catch (err) {
// do nothing
}
// 设置passive: true 可以提高性能
target.addEventListener(event, handler, passiveSupported
? { passive: true }
: false
);
Object.defineProperty
比如,你需要禁止设置对象某些属性,可以考虑这样:
const arr = ['a', 'b', 'c'];
const obj = {}
arr.forEach(item => {
Object.defineProperty(obj,item, {
get() {
throw new Error('not support')
}
})
})
obj.a; // Uncaught Error: not support
多条件判断可以利用数组:
如果是下面这样的,可扩展性不是很高,你加一个条件需要再 if
里面加 day === xx
:
function test(day) {
if (day === 'Monday' || day === 'Tuesday') {
// do something
}
}
可以改成这样,这样你再扩展时,直接加到数组里面即可:
function test(day) {
const dayArr = ['Monday', 'Tuesday']
if (dayArr.includes(day)) {
// do something
}
}
对象数组去重
利用对象的 key
是唯一的这个特性。
例如,对同名的属性进行去重:
const objArr = [{name: 'axe', age: 20}, {name: 'spe', age: 21}, {name: 'axe', age: 20}]
let res = {}
for (let i = 0; i < objArr.length; i++) {
res[objArr[i]['name']] = objArr[i]
}
res = Object.values(res) // [{name: 'axe', age: 20}, {name: 'spe', age: 21}]
当然,可能是 name
一样,但是 age
不一样,这个时候不想去重:
const objArr = [{name: 'axe', age: 20}, {name: 'spe', age: 21}, {name: 'axe', age: 20}, {name: 'axe', age: 10}]
let res = {}
for (let i = 0; i < objArr.length; i++) {
res[`${objArr[i]['name']}-${objArr[i]['age']}`] = objArr[i]
}
res = Object.values(res)
/*
* [{"name": "axe", "age": 20},
* {"name": "spe", "age": 21},
* {"name": "axe", "age": 10}]
*/
当然,你可能还想把名字相同的放到一起:
const objArr = [{name: 'axe', age: 20}, {name: 'spe', age: 21}, {name: 'axe', age: 20}, {name: 'axe', age: 10}]
let res = {}
for (let i = 0; i < objArr.length; i++) {
res[`${objArr[i]['name']}-${objArr[i]['age']}`] = objArr[i]
}
res = Object.values(res).sort((a, b) => a.name > b.name ? 1 : -1)
/*
* [{"name": "axe", "age": 10},
* {"name": "axe", "age": 20},
* {"name": "spe", "age": 21}]
*/
更进一步,对相同 name
的根据 age
的大小排序:
const objArr = [{name: 'axe', age: 20}, {name: 'spe', age: 21}, {name: 'axe', age: 20}, {name: 'axe', age: 30}, {name: 'axe', age: 5}]
let res = {}
for (let i = 0; i < objArr.length; i++) {
res[`${objArr[i]['name']}-${objArr[i]['age']}`] = objArr[i]
}
const sortFn = (a, b) => {
if (a.name > b.name) return 1
else if (a.name < b.name) return -1
else if (a.age > b.age) return 1
else return -1
}
res = Object.values(res).sort(sortFn)
/*
* [{"name": "axe", "age": 5},
* {"name": "axe", "age": 20},
* {"name": "axe", "age": 30},
* {"name": "spe", "age": 21}]
*/
布尔值取反
1、利用 异或,对于每一个比特位,当两个操作数相应的比特位有且只有一个 1 时,结果为 1,否则为 0。简而言之,相同为 0, 不同为 1.
let isA = true;
isA ^= 1; // 0
isA ^= 1; // 1
2、利用 取反
ture * 1 为 1
let isA = true
isA = !isA*1 // 0
isA = !isA*1 // 1
async 函数
async 函数返回值是一个 Promise,如果要用到里面的返回值,需要 then
来接收:
const foo = async () => {return 1;}
const a = foo(); // Promise {<fulfilled>: 1}
a.then(res => console.log('res ===', res)); // res === 1
保留四位小数,移除多余的 0
toFixed
- 语法:
NumberObject.toFixed(num)
- num默认值为0
- 返回字符串
parseFloat
- 语法: parseFloat(string)
- 字符串中只返回第一个数字
- 开头和结尾的空格是允许的
- 如果字符串的第一个字符不能被转换为数字,那么 parseFloat() 会返回 NaN
- 如果小数点后面有多余的 0,会自动移除,parseFloat('10.00') 返回 10,parseFloat('10.10') 返回 10.1
(Number(text).toFixed(4)).replace(/(\.?0+)$/, '')
// or
parseFloat(Number('123.1020111').toFixed(4))
随机生成中文字符串
// 0x9FA5 和 0x4E00 是十六进制数,分别对应十进制的 40869 和 19968。这两个数值是 Unicode 编码中汉字的起始和结束的编码。
// 0x9FA5 - 0x4E00 这个表达式的结果是汉字在 Unicode 编码中的范围,即 20901。这意味着 Unicode 编码中有 20901 个汉字。
String.fromCharCode(Math.floor(Math.random() * (0x9FA5 - 0x4E00 + 1) ) + 0x4E00)