ASkyBig/knowledge_point

js 随手记

ASkyBig opened this issue · 0 comments

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

如果全局安装后命令没有生效,可能是没有装上
image


14、后端返回的 json 中的整型数据到 js 会丢失精度,记得用 bigInt 或者字符串

image


15、for in 和 for of

let aArray = ['a',123,{a:'1',b:'2'}]
for(let index in aArray){
    console.log(`${aArray[index]}`);
}

image

for(var value of aArray){
    console.log(value);
}

image

aArray.name = 'demo'
for(let index in aArray){
    console.log(`${aArray[index]}`);
}

image

for(var value of aArray){
    console.log(value);
}

image


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 的执行方式。

  1. 如果执行一个很耗时的任务,会影响延迟消息队列中任务的执行
  2. 存在嵌套带调用时候,系统会设置最短时间间隔为 4s(超过 5 层)
  3. 未激活的页面,setTimeout 最小时间间隔为 1000ms
  4. 延时执行时间的最大值 2^31 - 1 = 2147483647,溢出会导致定时器立即执行
  5. 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年
image

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)