整理了一些常用的【JS开发小技巧】(持续补充中......)

545 阅读5分钟

下面整理了一些js开发常用的小技巧以及一些细节,它们你可能知道也可能不知道或者忘记,没事就当做一次整理了,后面会补充的;没有顺序之分,可以跳着看,会持续补充~~~

一,数值型

求数字数组最小值

let list = [1,2,3]
let min = Math.min(...list)  // 1

数值精度问题

所有利用IEEE754标准的64位双精度浮点数存储数值型的语言都有这个问题(如:Python),会出现:

console.log(0.1 + 0.2) // 0.30000000000000004
console.log(0.7 + 0.1) // 0.7999999999999999
// ......

原因可以看这个:www.lizenghai.com/archives/35…

可以这样解决

parseFloat((0.1 + 0.2).toFixed(1)) // 0.3
parseFloat((0.7 + 0.1).toFixed(1)) // 0.8

获取当前时间戳

由于+单元运算符会发生往数字型的类型转换,所以可以这样获取时间戳

+new Date()
/*
此外还有:
new Date() * 1
new Date().getTime()
Date.now()
*/

二,字符型

字符串倒序

字符串倒序就是把字符串倒着排序,可以用下列方法.split('').reverse().join('')

let str = 'abc'
let revStr = str.split('').reverse().join('')
console.log(revStr) // cba

三,数值,字符,布尔

由于这三者都存在字面量,且有自己的数据类型,不像正则的字面量属于对象型,所以它们有些特别要注意的地方

构造函数

它们都有自己的构造函数,且这些构造函数构造的实例都属于对象

 typeof new Number(1)  // "object"
 typeof new String('hello')  // "object"
 typeof new Boolean(true)  // "object"

想要查看它们的字面量的值需要使用valueOf方法

new Number(1).valueOf() // 1
new String('hello').valueOf() // hello
new Boolean(true).valueOf() // true

四,数组

创建定长的空数组

可能有人认为直接用new Array(),但是这样创建出来并不是你想的那样是几个undefined或者null,如:

let emptyList = new Array(3)
console.log(emptyList)

这个打印出来啥呢?在不同浏览器是不同的,

  • 谷歌
     [empty × 3]
    
  • 火狐
     Array(3) [ undefined, undefined, undefined ]
    

不要相信火狐显示的那样,其实里面只有length:3没有那三个undefined。 我们得用.fill()方法才可以达到目的

let emptyList1 = new Array(3).fill() 
console.log(emptyList1)
//  [undefined, undefined, undefined]
// or
let emptyList2 = new Array(3).fill(null)
console.log(emptyList2)
//  [null, null, null]

或者

let emptyList = Array.apply(null, { length:3 })
console.log(emptyList)
// [undefined, undefined, undefined]

或者

let emptyList = Array.from({ length:3 })
console.log(emptyList)
// [undefined, undefined, undefined]

捣乱数组顺序

let arr = [1,2,3,4,5]
Array.prototype.shuffle = function() {
  this.sort(function () {
    return Math.random() - 0.5
  })
}
arr.shuffle()
console.log(arr) // [4, 1, 2, 3, 5]

边遍历数组,边增加数组

下面这个做法用for不行,

let arr = [1,1]
for(let i in arr) {
  arr.push(1)
} 
 // 结果 arr = [1, 1, 1, 1]

并不会无限制地遍历下去,需要这样的操作需要用while

let arr = [1,1]
while(arr.length < 10){  // 让arr的长度为10就跳出
  arr.push(1)
} 
 // 结果 arr = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

数组拼接

let ls1 = [1,2]
let ls2 = [3,4]
// 第一种
let ls3 = ls1.concat(ls2)
// 第二种
let ls4 = [...ls1, ...ls2] 

数组降维

let arr = [[1,2],[3,4]]
// 1
function flatten1(arr) {
   return [].concat.apply([], arr)
}
// 2
function flatten2(arr) {
    return arr.flat()
}

伪数组转真数组

如下所示的,就是一个伪数组

var arrLike = { 
    length:2,
    0:'foo',
    1: 'bar'
}

它不能调用Array.prototype上面的方法,需要为其转化为真数组

1,用ES6的Array.from

var arrLike = { 
    length:2,
    0:'foo',
    1: 'bar'
}
var arr = Array.from(arrLike)  // ["foo", "bar"]

2,Array.prototype.slice.call

var arrLike = { 
    length:2,
    0:'foo',
    1: 'bar'
}
var arr = Array.prototype.slice.call(arrLike)  // ["foo", "bar"]

数组浅拷贝

先看下这个例子:

let ls1 = [1,2,3]
let ls2 = ls1
let ls3 = [1,2,3]
console.log(ls2 === ls1) // true 
console.log(ls3 === ls1) // false
console.log(ls3 === ls2) // false

这是由于ls2ls1的浅拷贝,拷贝的是变量,而不是值,ls1ls2都指向同一块堆内存,所以为true,而ls3新开了一块堆内存,所以和另外两个比都为false; 了解了什么是浅拷贝之后,接下看看怎么方便地进行深拷贝

1,利用slice()方法

let ls1 = [1,2,3]
let ls2 = ls1.slice()
console.log(ls2 === ls1) // false

2,利用Array.from方法

let ls1 = [1,2,3]
let ls2 = Array.from(ls1)
console.log(ls2 === ls1) // false

3,利用map()方法

let ls1 = [1,2,3]
let ls2 = ls1.map(i=>i)
console.log(ls2 === ls1) // false

4,利用filter()方法

let ls1 = [1,2,3]
let ls2 = ls1.filter(_=>true)
console.log(ls2 === ls1) // false

5,利用values()方法

let ls1 = [1,2,3]
let ls2 = [...ls1.values()]
console.log(ls2 === ls1) // false

6,利用Symbol.iterator方法

let ls1 = [1,2,3]
let ls2 = [...ls1[Symbol.iterator]()]
console.log(ls2 === ls1) // false

注意以上这些方法拷贝的只是最外层的数组,如果数组里面有数组或者对象,那些是不会被深拷贝的,所以这些方法只适用于简单的数组

查找对象在数组里的index

利用findIndex方法

let arr = [
    {
        name: '小明'
    },
    {
        name: '小红'
    }
]

let index = arr.findIndex(item => {
    return item.name === '小明'
})

// index 为 0

判斷某個值是否在數組裏面

注意:不能使用in關鍵字來判斷,例如:

let arr = ['a', 'b', 'c']

console.log( 'b' in arr ) // false
console.log(0 in arr) // true

in判斷的是索引有沒有在數組裏

计算对象数组里面某个属性的合计

let carts = [{ num:1 }, { num:2 }]
let total = carts.reduce((s, o) => s+o.num, 0) // 3

五,对象

对象浅拷贝

1,用Object.assign()

let obj1 = { key:'value' }
let obj2 = Object.assign({}, obj1)
console.log(obj1 === obj2) // false

但注意不能像下面这样用

let obj1 = { key:'value' }
let obj2 = Object.assign(obj1, {})
console.log(obj1 === obj2) // true

2,通过解构赋值

let obj1 = { key: 'value' }
let obj2 = { ...obj1 }
console.log(obj1 === obj2) // false

以上方法只针对简单的对象,如果对象里面嵌套对象,函数,或者数组,则不行

对象深拷贝

1,用JSON.parse(JSON.stringify())

let obj1 = { key:'value' }
let obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj1 === obj2) // false

美化对象的输出

我们平时在开发的时候,常常需要输出我们的数据,数据如果是一些比较复杂的对象的话,输出是木有换行的看起来不舒服,可以用下面这个方法美化输出

console.log(JSON.stringify(obj, null, "  "))
// or
console.log(JSON.stringify(obj, null, "  "))

获取对象的所有键

Reflect.ownKeys()

这里说的对象的概念是JS里最本质的对象,就是包括数组,函数,正则等等;我们想获取对象上面的所有属性时,不可以用Object.keys(), 因为这东西对于数组,函数,正则这些是拿不到上面的key的;

Object.keys([]) // []
Object.keys(()=>{}) // []
Object.keys(/hello/) // []

// 都是[]说明没有拿到key

可以利用Reflect.ownKeys()这个方法来获取对象的所有键组成的一个数组,和Object.keys()不同的是,这个方法会把Symbol属性也给获取到,并且对于数组,函数,正则都可以拿到

Reflect.ownKeys([]) // ["length"]
Reflect.ownKeys(()=>{}) // ["length", "name"]
Reflect.ownKeys(/hello/) // ["lastIndex"]

六,函数

在函数里使用参数数组

在函数里想获取参数数组,之前是在函数里使用arguments,这种做法获取的是一个假数组,而且不被推荐,我们可以使用ES6的解构来代替

function fun(...arg) {
  console.log(arg)
}

fun(1,2,3)  // [1,2,3]

获取函数形参个数

function fn(a,b,c) {};

fn.length // 3

七,其他

判断js的数据类型

function getType(x){
	return Object.prototype.toString.call(x).slice(8,-1)
}

基本上可以把所有构造函数都打印出来

判断某个变量是不是某个类型

这个判断看起来不那么精准,其实不是这样的

({}) instanceof Object  //true
[] instanceof Array  // true
/.*/  instanceof RegExp  // true
new Date() instanceof Date  // true
(function(){}) instanceof Function  // true

注意,他们其实都是属于对象

[] instanceof Object // true
/.*/  instanceof Object // true
new Date() instanceof Object // true
(function(){}) instanceof Object // true

再注意

'hello' instanceof Object  // false
'hello' instanceof String  // false
new String('hello') instanceof String // true
new String('hello') instanceof Object // true

Number,Boolean也是如此

出现以上的原因是因为instanceof是以原型链来判断的,原型链只会出现在对象里,所以不是对象的Number,String,Boolean的字面量在instanceof Object时都为false

两个变量交换赋值

let a = 2
let b = 3
[a,b] = [b,a]
console.log(a,b)  // 3 2

随机验证码

1,四位数字

Math.random().toString().slice(2,6)
// or
(Math.random() * 9999).toFixed()
// or
Math.random().toFixed(4).slice(-4)

2,四位数字加字母

Math.random().toString(16).slice(2,6).toUpperCase()