2023年1月 前端面试高频考点

271 阅读4分钟

【2023年1月整理】

考点内容备注
数据存储
用户登录
localStorage、SessionStorage
Token、cookie、session、JWT
深拷贝和浅拷贝浅拷贝:Object.assign方法、ES6 扩展运算符、数组的 sliceconcat 方法、jQuery 中的 $.extend
深拷贝:Object.assign()、JSON 序列化、递归
递归、判断类型、检查环(嵌套)
闭包在函数内部再定义一个函数实现读取到函数内的局部变量父对象的所有变量对子对象都是可见的,反之不成立
节流 throttle单位时间内频繁触发事件,只执行一次
典型场景:高频事件快速点击获取验证码
代码实现思路:利用定时器,等定时器执行完毕,才开启定时器
防抖 debounce单位时间内,频繁触发事件,只执行最后一次
典型场景:搜索框搜索输入
代码实现思路:利用定时器,每次触发先清掉以前的定时器
开发中一般用lodash
async / wait
ES8新特性
解决面条地狱(链式调用太多)
Promise.all方法:成功时返回结果数组 失败时返回最先被reject失败状态的值
.race方法:哪个结果获得快,就返回哪个结果
Axios基于promise封装的网络请求库 是基于XHR进行二次封装
同源策略和跨域同源是指两个URL的协议、端口和域名都相同
跨域手段有JSONP和CORS
Webpack和vite开发环境:vite不打包代码利用浏览器module支持 w打包
生产环境:vite用rollup+esbuild打包代码 w用babel打包 慢
文件处理时机:vite只在请求文件时 w提前打包
vite缺点:热更新总失败、部分功能rollup不支持
loader与plugin区别l是文件加载器 在创建最终产物之前运行
p是插件在整个打包过程都能运行
HTTP缓存方案强缓存和协商缓存
HTTP和HTTPS的区别
HTTPS = HTTP+SSL加密
HTTP明文传输 +s加密传输
HTTP使用80端口 +s使用443端口
HTTP不需CA证书 +s需要证书
HTTP速度快 +s有复杂的SSL握手
HTTPS使通信不容易受到拦截和攻击
Vue组件间通信方式父子组件用props和事件
爷孙用依赖provide注入inject
任意组件用eventBus或vuex、Pinia

深拷贝 浅拷贝的方式

什么是深拷贝、浅拷贝

浅拷贝是指,对基本类型的拷贝,以及对象类型的地址拷贝。

深拷贝是指,除了拷贝基本类型的值,还完全复刻了对象类型。(新旧对象没有关联或交集,完全隔离)

浅拷贝举例:

var a = 1;
var b = a;  // 浅拷贝-值拷贝

console.log(b);  // 1
b = 2;
console.log(b);  // 2
console.log(a);  // 1

如何实现 浅拷贝

1. 直接赋值

var p1 = {
  name:'jack',
  age:12
}
var p2 = p1;      // 浅拷贝-对象地址拷贝
console.log(p2);  // {name:'jack', age:12}
p2.age = 20;
console.log(p2);  // {name:'jack', age:20}
console.log(p1);  // {name:'jack', age:20}

2. Object.assign方法

var user1 = {
    name : "法医"
}
var user2 = Object.assign(user1, {age : 18}, {sex : 'male'});
console.log(user2); // { name: '法医', age: 18, sex: 'male' }

当属性对应的一个值是引用类型时,此方法是一个浅拷贝。

3. ES6 扩展运算符

var user1 = {
    name: "法医",
    like: {
        eat: "面条",
        sport: "篮球",
        },
};
             
var user2 = {...user1};
console.log(user1); //{name: '法医', like: {eat: '面条', sport: '篮球'}}
console.log(user2); //{name: '法医', like: {eat: '面条', sport: '篮球'}}
user1.name = "前端猎手";
console.log(user1); //{name: '前端猎手', like: {eat: '面条', sport: '篮球'}}
console.log(user2); //{name: '法医', like: {eat: '面条', sport: '篮球'}}
user1.like.eat = "米饭";
console.log(user1); //{name: '前端猎手', like: {eat: '米饭', sport: '篮//{name: '法医', like: {eat: '面条', sport: '篮球'}}球'}}
console.log(user2); //{name: '法医', like: {eat: '米饭', sport: '篮球'}}

4. 数组的 sliceconcat 方法

// 使用 slice 截取 和 concat 拼接 方法来拷贝数组
var user1 = ["法医",{eat:"面条",sport:"篮球"}];

var user2 = user1.slice();
var user3 = user1.concat();
console.log(user1);//{name: '法医', like: {eat: '面条', sport: '篮球'}}
console.log(user2);//{name: '法医', like: {eat: '面条', sport: '篮球'}}
console.log(user3);//{name: '法医', like: {eat: '面条', sport: '篮球'}}
user1[0] = "前端猎手";
console.log(user1);//{name: '前端猎手', like: {eat: '面条', sport: '篮球'}}
console.log(user2);//{name: '法医', like: {eat: '面条', sport: '篮球'}}
console.log(user3);//{name: '法医', like: {eat: '面条', sport: '篮球'}}
user1[1].eat = "米饭";
console.log(user1);//{name: '前端猎手', like: {eat: '米饭', sport: '篮球'}}
console.log(user2);//{name: '法医', like: {eat: '米饭', sport: '篮球'}}
console.log(user3);//{name: '法医', like: {eat: '米饭', sport: '篮球'}}

5. jQuery 中的 $.extend

此方法可以实现深浅拷贝:

$.extend(deep, target, object1, object2, ...)

// deep: true 表示深拷贝 false 表示浅拷贝
// target: 要拷贝的目标对象
// object1: 待拷贝的对象
<body>
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  <script>         
    var user = {
      name: "法医",
      age: 18,
      like: {
        eat: "面条",
        sport: "篮球",
      },
    };
    var target = {};
    $.extend(target, user);
    target.name = "前端猎手";
    console.log(user); //{name: '前端猎手', like: {eat: '面条', sport: '篮球'}}
    console.log(target); //{name: '前端猎手', like: {eat: '面条', sport: '篮球'}}
  </script>
</body>

如何实现 深拷贝

1. 最基本的方法

// 适用于属性较少的情况
var p1 = {
  name:'jack',
  age:12
}
var p2 = {
  name: p1.name,
  age: p1.age
};      

2. 通用方法 Object.assign()

可对非嵌套对象进行深拷贝的方法

var p1 = {
  name:'jack'
}
var p2 = {}
Object.assign(p2,p1);  // 第一个参数为目的地 第二个参数为源头
console.log(p2);  // {name:'jack'}

3. 用 JSON 序列化

把一个对象转换为 JSON 格式的字符串

不支持Date、正则、undefined、函数等数据;不支持引用;

var p1 = {
  name:'jack',
  age:12,
  toy:{
    name:'car'
  }
}
// 序列化:把 JS 对象转换为 字符串
JSON.stringify(p1)   
console.log(p1)  
// '{"name:'jack',"age":12,"toy":{"name":"car"}}'

// 反序列化:把 JSON 格式的字符串转回 JS 对象
JSON.parse(JSON.stringify(p1)) 

4. 递归

递归、判断类型、检查环(嵌套)

var p1 = {
  name:'jack',
  age:12,
  toy:{
    name:'car'
  }
}
var p2 = deepCopy({},p1);
p2.toy.name = 'plane';
console.log(p1)  //  ... name: 'car'
console.log(p2)  //  ... name: 'plane'

function deepCopy(dest, src){  // 形参分别为目标对象、源对象
  let dest = dest || {};  // 容错处理,防止用户不传 dest 值
  for(let key in src){
    // 如果对象的属性又是对象,则递归处理
    if (src.hasOwnProperty(key)) {   // 防止拿到原型链属性
      if(src[key] !== "null" && typeof src[key] === "object"){
        dest[key] = (src[key].constructor === Array)?[]:{};
        deepCopy(dest[key], src[key]);
      } else {
        dest[key] = src[key];
      }
    }
  }
  return dest;
}

闭包

闭包是JS的一种语法特性,一个语言可以选择是否支持闭包这个语法特性。

什么是闭包

  1. 闭包就是密闭的容器,用于存储数据。(存储数据的容器:变量、对象、数组、es6引入的set、map容器,分这么多类别是基于存储数据的结构不同)
  2. 闭包是一个特殊对象,存放数据的格式为 key:value

闭包形成的条件

  • 函数嵌套
  • 内部函数引用外部函数的局部变量
function f1(){
  const a = 1;
  return function(){
    console.log(a);  // 内部函数引用外部 f1 函数的局部变量 a
  }
}
f1()  // 外部函数调用

闭包的组成

由执行上下文(A)和在该执行上下文中创建的函数(B)两部分组成。

当B执行时,如果访问了A中变量对象中的值,那么闭包就会产生。

闭包的应用场景

function fun(){
  const a = 1;
  return function(){
    console.log(a);  // 内部 fun2 函数引用外部 fun 函数的局部变量 a
  }
}
let fun2 = fun()  // 外部函数调用
fun2()

这个内部函数可以访问外部函数作用域的变量,并且如果外部函数不暴露这个内部函数的话,那么外界就不知道这个内部函数的存在,只能在自己的内部使用,也就形成了一个私有的函数。

// 计算两个数的平方和
function squSum(a, b){
    function squ(x){    // 内部函数单独负责计算一个数的平方
        return x * x;
    }
    return squ(a) + squ(b);
}
console.log(squSum(2, 3));  // 13
  • 内部函数也可以作为返回值返回出去,整个的外部函数就形成了一个高阶函数,即返回函数的一个函数。
function person(){
    let name = "马云";
    function getName(){
        return name;
    }
    return getName;
}

var getName = person();
console.log(getName());  // 马云
  • 上例的做法,等于给name设置了只读属性,外界只能访问他的值,而不能修改他的值,起到了保护作用,person()即为高阶函数。

闭包的优点

延长外部函数局部变量的生命周期。通过闭包,我们可以在其他的执行上下文中,访问到函数的内部变量。

函数的执行上下文,在执行完毕之后,生命周期结束,那么该函数的执行上下文就会失去引用。其占用的内存空间很快就会被垃圾回收器释放。可是闭包的存在,会阻止这一过程。

闭包的缺点

使用不当容易造成内存泄露

闭包的应用场景

柯里化

模块化