前端面试题四(金九银十)加分技巧

发布于:2023-01-04 ⋅ 阅读:(264) ⋅ 点赞:(0)

1.生成随机颜色

const color = () => `#${Math.floor(Math.random() * 0xffffff).toString(16)}`

console.log(color ())

2.数组重排序

const shuffle = (arr) => arr.sort(() => Math.random() - 0.5)

const arr = [1, 2, 3, 4, 5]
console.log(shuffle(arr))

3.数组快速去重

var mySet = new Set([1, 2, 3, 4, 4]);
[...mySet];

4.一键复制

const copy = (text) => navigator.clipboard && navigator.clipboard.writeText && navigator.clipboard.writeText(text)

copy ("Hello World!")

5.暗色主题

const isDarkMode = () => window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;

console.log(isDarkMode())

6.滚到顶部

const scrollToTop = (element) => 
  element.scrollIntoView({ behavior: "smooth", block: "start" });

7.滚到底部

const scrollToBottom = (element) => 
  element.scrollIntoView({ behavior: "smooth", block: "end" });

8.检测元素是否在屏幕中

const callback = (entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      // `entry.target` is the dom element
      console.log(`${entry.target.id} is visible`);
    }
  });
};

const options = {
  threshold: 1.0,
};
const observer = new IntersectionObserver(callback, options);
const btn = document.getElementById("btn");
const bottomBtn = document.getElementById("bottom-btn");
observer.observe(btn);
observer.observe(bottomBtn);

9.检查设置运行设备

const detectDeviceType = () =>
  /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
    navigator.userAgent
  ) ? "Mobile" : "Desktop";

console.log(detectDeviceType());

10.隐藏元素

const hideElement = (el, removeFromFlow = false) => {
  removeFromFlow ? (el.style.display = 'none')
  : (el.style.visibility = 'hidden')
}

11.在路径地址中获取参数

const getParamByUrl = (key) => {
  const url = new URL(location.href)
  return url.searchParams.get(key)
}

12.深拷贝对象

注: JSON 的 API,还有更新的深拷贝对象的 structuredClone API,有些浏览器不支持

const deepCopy = obj => JSON.parse(JSON.stringify(obj))
structuredClone(obj)

13.定时函数

const wait = (ms) => new Promise((resolve)=> setTimeout(resolve, ms))

const asyncFn = async () => {
  await wait(1000)
  console.log('等待异步函数执行结束')
}

asyncFn()

14.什么是尾调用优化和尾递归?

尾调用实际就是在最后返回值调用其他函数

function f(x){ 
    return g(x); 
}

如果所有函数都是尾调用,那么完全可以做到每次执行时,调用记录只有一项,这将大大节省内存。这就是"尾调用优化"的意义

function f() {
    let m = 1; 
    let n = 2; 
    return g(m + n); 
} 
f(); 

// 等同于 
function f() { 
    return g(3); 
} 
f(); 

// 等同于 
g(3);

尾递归:函数调用自身,称为递归。如果尾调用自身,就称为尾递归

function factorial(n) { 
    if (n === 1) return 1; 
    return n * factorial(n - 1); 
} 
factorial(5) // 120

使用尾递归,就不会发生栈溢出,相对节省内存

function factorial(n, total) { 
    if (n === 1) return total; 
    return factorial(n - 1, n * total); 
} 
factorial(5, 1) // 120

15.栈与堆的区别

(1)管理方式不同。栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏;
(2)空间大小不同。每个进程拥有的栈的大小要远远小于堆的大小。理论上,程序员可申请的堆大小为虚拟内存的大小,进程栈的大小 64bits 的 Windows 默认 1MB,64bits 的 Linux 默认 10MB;
(3)生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。
(4)分配方式不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是由操作系统完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由操作系统进行释放,无需我们手工实现。
(5)分配效率不同。栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是由C/C++提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。显然,堆的效率比栈要低得多。
(6)存放内容不同。栈存放的内容,函数返回地址、相关参数、局部变量和寄存器内容等。当主函数调用另外一个函数的时候,要对当前函数执行断点进行保存,需要使用栈来实现,首先入栈的是主函数下一条语句的地址,即扩展指针寄存器的内容(EIP),然后是当前栈帧的底部地址,即扩展基址指针寄存器内容(EBP),再然后是被调函数的实参等,一般情况下是按照从右向左的顺序入栈,之后是被调函数的局部变量,注意静态变量是存放在数据段或者BSS段,是不入栈的。出栈的顺序正好相反,最终栈顶指向主函数下一条语句的地址,主程序又从该地址开始执行。堆,一般情况堆顶使用一个字节的空间来存放堆的大小,而堆中具体存放内容是由程序员来填充的。
从以上可以看到,堆和栈相比,由于大量malloc()/free()或new/delete的使用,容易造成大量的内存碎片,并且可能引发用户态和核心态的切换,效率较低。栈相比于堆,在程序中应用较为广泛,最常见的是函数的调用过程由栈来实现,函数返回地址、EBP、实参和局部变量都采用栈的方式存放。虽然栈有众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,主要还是用堆。
无论是堆还是栈,在内存使用时都要防止非法越界,越界导致的非法内存访问可能会摧毁程序的堆、栈数据,轻则导致程序运行处于不确定状态,获取不到预期结果,重则导致程序异常崩溃,这些都是我们编程时与内存打交道时应该注意的问题。

16.JS代码中的use strict是什么?

use strict是一种ECMAscript5添加的(严格)运行模式,这种模式使得Javascript 在更严格的条件下运行 特性:
禁止使用with语句。 禁止this关键字指向全局对象。 对象不能有重名的属性

17.[] == ![]会相等么?

== 中,左右两边都需要转换为数字然后进行比较。 []转换为数字为0。 ![] 首先是转换为布尔值,由于[]作为一个引用类型转换为布尔值为true, 因此![]为false,进而在转换成数字,变为0。 0 == 0 , 结果为true

18.Instanceof能否判断基本数据类型么?

class PrimitiveNumber {     
    static [Symbol.hasInstance](x) {         
        return typeof x === 'number'         
    }    
}     
console.log(111 instanceof PrimitiveNumber) // true

19. 0.1+0.2为什么不等于0.3?

0.1和0.2在转换成二进制后会无限循环,由于标准位数的限制后面多余的位数会被截掉,此时就已经出现了精度的损失,相加后因浮点数小数位的限制而截断的二进制数字在转换为十进制就会变成 0.30000000000000004。

20. 什么是防抖和节流,能举例么?

节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

栗子

想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应
假设电梯有两种运行策略 debounce 和 throttle,超时设定为15秒,不考虑容量限制
电梯第一个人进来后,15秒后准时运送一次,这是节流
电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖

21.说说 Javascript 为什么会存在数字精度丢失的问题,以及如何进行解决?

丢失问题

计算机存储双精度浮点数需要先把十进制数转换为二进制的科学记数法的形式,然后计算机以自己的规则{符号位+(指数位+指数偏移量的二进制)+小数部分}存储二进制的科学记数法
因为存储时有位数限制(64位),并且某些十进制的浮点数在转换为二进制数时会出现无限循环,会造成二进制的舍入操作(0舍1入),当再转换为十进制时就造成了计算误差

解决方法
使用 toPrecision 凑整并 parseFloat 转成数字后再显示

function strip(num, precision = 12) { 
    return +parseFloat(num.toPrecision(precision)); 
}
/** 
* 精确加法 
*/ 
function add(num1, num2) { 
    const num1Digits = (num1.toString().split('.')[1] || '').length; 
    const num2Digits = (num2.toString().split('.')[1] || '').length; 
    const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits)); 
    return (num1 * baseNum + num2 * baseNum) / baseNum; 
}

好了,今天就分享到这,祝你薪资+++
以上文章
请斧正

本文含有隐藏内容,请 开通VIP 后查看