总有你不知道的JS

166 阅读6分钟

1 Date.now()

Date.now()是毫秒为单位

2 console

2.1 console.dir

输出一个对象或者一个值的详细信息,与console.log区别是后者可以一次输出多个,但是console.dir一次只能输出一个

2.2 console.warn

以警告的形式输出

2.3 console.table

将json数据按照表格形式输出

2.4 console.time和console.timeEnd

计算console.time和console.timeEnd之间的时间(单位:ms)

forExample:

    console.time('计时器1:')
    for(let i = 0; i < 10000; i++){
        var a = 1
    }
    console.timeEnd('计时器1:')

3 数值比较

  • 数字 == 字符串 (将字符串转变为数字,然后进行对比)
  • 数字 == 布尔 (将布尔转变为数字,然后进行对比)
  • 字符串 == 布尔 (将二者都转变为数字在进行比较)
  • 对象 == 数字 (对象转变为数字**,然后进行比较)
  • 对象 == 布尔 (将二者都转变为数字,然后进行比较)
  • 对象 == 字符串 (将对象转变为字符串,然后进行比较)

规律:除了对象和字符串比较的时候,把对象转变为字符串外,剩余只要类型不同,都是先转变为数字类型,然后进行比较。

栗子:

 5 == true // false
 1 == true // ture
 if(1) {
    console.log('可以打印')
 }
 if(5) {
    console.log('可以打印')
 }

需要注意的是,上面的例子中,虽然5 == true结果为false, 但是如果是在if语句中,即:if(5) 此时,会将5转变为true。

4 栈内存和堆内存

  • 占内存:主要存放基本数据类型(string,number,boolean,undefined,null等)以及引用数据类型的指针等。
  • 堆内存:主要存放引用数据类型。

5 数组扁平化

let arr = [1, [2, [3, [4, 5]]], 6];

5.1 正则

    let str = JSON.stringify(arr)
    arr = str.replace(/(\[\]))/g, '').split(',')

5.2 递归

    let result = [];
    let fn = function(arr) {
        for(let i = 0; i < arr.length; i++) }{
            let item = arr[i];
            if (Array.isArray(arr[i])){
                fn(item);
            } else {
                result.push(item);
            }
        }
    }

5.3 flat

let res = arr.flat(Infinity)

6 requestAnimationFrame

js中比定时器动画更好的执行方式,通常为每秒60次。

接收一个回调函数作为参数,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。

为了提高性能和电池寿命,因此在大多数浏览器里,当requestAnimationFrame() 运行在后台标签页或者隐藏的 里时,requestAnimationFrame() 会被暂停调用以提升性能和电池寿命。

7 JSON.stringify()注意点

JSON.stringify(arr)并不是对所有的值都能有效处理,例如,它会把正则变为空对象,函数/undefined/Symbol都会变为null, 日期格式变为字符串后,基于parse也回不到对象格式了。

8 mouseover和mouseenter区别

mouseenter: 默认阻止了事件的冒泡传播。从父元素进入子元素,从子元素,进入父元素,父元素的enter和leave都不会触发。

mouseover: 从父元素移动到子元素,子元素和父元素都会触发over,从子元素移动到父元素,子元素和父元素都会触发out事件。

9 宏任务和微任务

js中的异步任务可以分为宏任务(macroTask)和微任务(microTask)。 常见的宏任务:script(整体代码), setTimeout, setInterval, setImmediate, 微任务:Promises,process.nextTick(其为事件循环设置一项任务,node.js会在下次事件循环调响应时调用回调函数)。

同步任务,微任务,宏任务执行顺序

顺序 : 先执行同步代码,遇到宏任务则将宏任务放入宏任务队列中,遇到微任务则将微任务放入微任务队列中,同步代码执行完毕以后,再执行所有的微任务。微微任务,然后在执行宏任务。

因此需要注意的是

微任务的执行效率要快于宏任务

宏任务执行顺序如下图所示(借用掘友图)

10 数值交换

    let a = 1, b = 2;

10.1 异或

    a = a ^ b;
    b = a ^ b;
    a = a ^ b;

10.2 求和

    a = a + b;
    b = a - b;
    a = a - b;

10.3 解构

 [a, b] = [b, a]

11 另类小数取整

    let a = 10.5 | 0
    let b = ~~10.5

12 判断奇偶数

    const a = 3;
    let flag = !!(a & 1) //奇数为true,偶数为false
    console.log(flag) // true

13 格式化金钱

    const filterMoney = num => num.toString().replace(/(?=(\B)(\d{3})+$)/g, ",");
    
    const money = filterMoney(27898989); // ‘27,898,989’

14 随机打乱数组

    let arr = [1,2,3,4,5]
    let res = arr.sort(() => Math.random() - .5)

15 new Function

let sum = new Function('a', 'b', 'return a + b');
console.log(sum(1,3))

16 Object.defineProperty和Proxy

Object.defineProperty(obj,key,handler)主要有三个:

  • 不能监听数组的变化
  • 必须遍历对象的每个属性
  • 必须深层遍历嵌套的对象

Proxy(obj, handler)

  • 针对对象:针对整个对象,而不是对象的某个属性。
  • 支持数组:不需要对数组的方法进行重载,省去了众多 hack。
  • 嵌套支持: get 里面递归调用 Proxy 并返回。

其他方面:

优势:Proxy 的第二个参数可以有 13 种拦截方法,比 Object.defineProperty() 要更加丰富,Proxy 作为新标准受到浏览器厂商的重点关注和性能优化,相比之下 Object.defineProperty() 是一个已有的老方法。

劣势:Proxy 的兼容性不如 Object.defineProperty() (caniuse 的数据表明,QQ 浏览器和百度浏览器并不支持 Proxy,这对国内移动开发来说估计无法接受,但两者都支持 Object.defineProperty()),不能使用 polyfill 来处理兼容性

17 Map和Object

ES6提供了Map数据结构,它类似于对象,也是键值对的集合。

object只能接收字符串作为键名,但是map可以接收各种类型的值(包括对象)作为键值。

17.1 Map实例的属性和方法如下:

  • size:获取成员的数量
  • set:设置成员 key 和 value
  • get:获取成员属性值
  • has:判断成员是否存在
  • delete:删除成员
  • clear:清空所有
    const map = new Map(['a',1],['b',2]);
    map.set('a', 100);
    map.size // 2
    map.get('a') // 100
    map.has('a') // true
    map.delete('a')
    map.has('a') // false
    map.clear()

17.2 Map实例的遍历方法

keys():返回键名的遍历器。 values():返回键值的遍历器。 entries():返回所有成员的遍历器。 forEach():遍历 Map 的所有成员。

    const map = new Map(['a',1],['b',2]);
    for (let key of map.keys()) {
        console.log(key);
    }
    // "a"
    // "b"

    for (let value of map.values()) {
        console.log(value);
    }
    // 1
    // 2

    for (let item of map.entries()) {
        console.log(item[0], item[1]);
    }
    // 'a' 1
    // 'b' 2
    
    map.forEach((item, i) => {
		console.log(item, i)
	})
	// 1 "a"
    // 2 "b"

18 NaN,indexOf, includes

NaN 不等于 NaN,这是事实。

但是如果一个数组let arr = [1, 2, NaN], 如果arr.includes(NaN)true, arr.indexOf(NaN)-1

19 求幂Math.pow(x,y) 和 **

    let a = Math.pow(2, 3) // 8
    let b = 2 ** 3 // 8

20 Object.entries()

该方法会将某个对象的可枚举属性与值按照二维数组的方式返回。(如果目标对象是数组,则会将数组的下标作为键值返回)

    let arr = [1,2,3]
    Object.entries(arr) // [['0',1], ['1',2], ['2',3]]
    let obj = {"a" : 1, "b": 2}
    Object.entries(obj) // [['a',1], ['b', 2]]

21 for...in于for...of

21.1 for...of

遍历可迭代对象(包括数组、Set 和 Map 结构、arguments 对象、DOM NodeList 对象、字符串等)

    // 数组
    let arr = ['a', 'b', 'c'];
    for (let v of arr) {
        console.log(v);
    }
    // 'a' 'b' 'c'
    
    // 字符串
    let str = 'abc;
    for (let v of str) {
        console.log(v);
    }
    // abc
    
    // 类数组对象
    let obj = {
        0: 'mazey',
        1: 'luna',
        2: 'cherrie',
        length: 3
    };
    // 需使用Array.from转换成可迭代对象
    for (let v of Array.from(obj)) {
        console.log(v);
    }
    // mazey luna cherrie
    
    // Set
    let s = new Set(['a', 'b', 'c']);
    for (let v of s) {
        console.log(v);
    }
    // 'a' 'b' 'c'
    
    // Map
    let m = new Map([
        ['0', 'a'],
        ['1', 'b'],
        ['2', 'c']
    ]);
    for (let [i, v] of m) {
        console.log(v);
    }
    // 'a' 'b' 'c'
    
    // DOM NodeList
    let domList = document.querySelectorAll('li');
    for (let v of domList) {
        console.log(v.innerText);
    }
    // mazey luna cherrie

21.2 for...in和for...of区别

  • for…in 遍历键名,for…of 遍历键值
    let arr = ['mazey', 'luna', 'cherrie'];
    for (let k in arr) {
        console.log(k);
    }
    // 0 1 2
    for (let v of arr) {
        console.log(v);
    }
    // mazey luna cherrie
  • for…in 会把对象上手动添加的属性和原型上的属性暴露出来。
    let obj = {
        0: 'mazey',
        1: 'luna',
        2: 'cherrie',
        length: 3
    };
    obj.name = 'objName';
    for (let k in obj) {
        console.log(k);
    }
    // 0 1 2 length name
    for (let v of Array.from(obj)) {
        console.log(v);
    }
    // mazey luna cherrie

21.3 for...of和数组forEach区别

  • 相对于数组自带的 forEach 方法,for…of 可以与 break、continue 和 return 配合使用。

  • 正确识别32位 UTF-16 字符。