2023前端面试题--不含框架篇

跨域怎么做的

  1. JSONP
    所谓的JSONP就是前端通过script标签不受通源策略的影响的特性,向API接口拼接一个回调发送给服务端,然后服务端拿到这个回调,执行拼将需要的数据放进回调的参数里,最后服务端将该函数返回给浏览器,所以这种跨域方式需要前端和后端配合
  2. CORS
    所谓的CORS就是服务端通过设置响应头实现(比如Access-Control-Allow-Origin:也就是设置给那个请求域响应数据;Access-Control-Allow-Methods:给请求域指定能够使用哪些方法来请求该服务端),所以这种方法,服务端可单方面完成
  3. Node接口代理
    还是回到同源策略,同源策略它只是浏览器的一个策略而已,它是限制不到后端的,也就是前端-后端会被同源策略限制,但是后端-后端则不会被限制,所以可以通过Node接口代理,先访问已设置Cors的后端1,再让后端1去访问后端2获取数据到后端1,后端1再把数据传到前端

scss用过哪些功能

  1. 使用变量: SCSS中的变量以$开头
$border-color:#aaa; //声明变量

.container {
    $border-width:1px;
    border:$border-width solid $border-color; //使用变量
}
  1. 混合器(函数)
    2.1 声明一个函数
    使用@mixin指令声明一个函数,看一下自己的css文件,有重复的代码片段都可以考虑使用混合器将他们提取出来复用。
@mixin border-radius{
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
    border-radius: 5px;
    color:red;
}

混合器作用域内的属性都是return的值,除此之外,还可以为函数传参数。

@mixin get-border-radius($border-radius,$color){
    -moz-border-radius: $border-radius;
    -webkit-border-radius: $border-radius;
    border-radius: $border-radius;
    color:$color;
}
// 也可以设置混合器的默认值。
@mixin get-border-radius($border-radius:5px,$color:red){
    -moz-border-radius: $border-radius;
    -webkit-border-radius: $border-radius;
    border-radius: $border-radius;
    color:$color;
}

2.2 使用函数: 使用函数的关键字为@include

.container {
    border:1px solid #aaa;
    @include get-border-radius;         //不传参则为默认值5px
    @include get-border-radius(10px,blue);   //传参
}
/*多个参数时,传参指定参数的名字,可以不用考虑传入的顺序*/
.container {
    border:1px solid #aaa;
    @include get-border-radius;         //不传参则为默认值5px
    @include get-border-radius($color:blue,$border-radius:10px);   //传参
}

tips:混合器中可以写一切scss代码。

  1. 继承
    继承是面向对象语言的一大特点,可以大大降低代码量。
    3.1 定义被继承的样式
%border-style {
  border:1px solid #aaa;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
}

使用%定义一个被继承的样式,类似静态语言中的抽象类,他本身不起作用,只用于被其他人继承。
3.2 继承样式
通过关键字@extend即可完成继承。

.container {
	@extend %border-style;
}
上述例子中看不出混合器与继承之间的区别,那么下一个例子可以看出继承与混合器之间的区别。
.container {
	@extend %border-style;
	color:red;
}
.container1 {   //继承另一个选择器
	@extend .container;
}

垃圾回收机制

根据对象的存活时间将内存的垃圾回收进行不同的分代,然后对不同分代采用不同的回收算法

  1. 新生代采用空间换时间的 scavenge 算法:整个空间分为两块,变量仅存在其中一块,回收的时候将存活变量复制到另一块空间,不存活的回收掉,周而复始轮流操作
  2. 老生代使用标记清除和标记整理,标记清除:遍历所有对象标记标记可以访问到的对象(活着的),然后将不活的当做垃圾进行回收。回收完后避免内存的断层不连续,需要通过标记整理将活着的对象往内存一端进行移动,移动完成后再清理边界内存

深拷贝

//方法一
function deepCopy(oldObj) {
    let newObj = {};
    for(let key in oldObj) {
        if(typeof oldObj[key] === 'object') {
            newObj[key] = deepCopy(oldObj[key]);
        }else{
            newObj[key] = oldObj[key];
        }
    }
    return newObj;
}
//方法二
//优点:简单方便,大多数时候可以满足需求
//缺点:1.无法复制函数 2.原型链没了,对象就是object,所属的类没了。
function deepCopy2(oldObj) {
    let newObj = {}
    newObj = JSON.parse( JSON.stringify(oldObj) )
    return newObj
}

用Promise实现图片的异步加载

let imageAsync=(url)=>{
    return new Promise((resolve,reject)=>{
        let img = new Image();
        img.src = url;
        img.οnlοad=()=>{
                    console.log(`图片请求成功,此处进行通用操作`);
                    resolve(image);
        }
        img.οnerrοr=(err)=>{
            console.log(`失败,此处进行失败的通用操作`);
            reject(err);
        }
    })
}
        
imageAsync("url").then(()=>{
    console.log("加载成功");
}).catch((error)=>{
    console.log("加载失败");
})

简述事件循环原理

事件循环的原因: js的本职工作是为了处理dom元素,因此不能是一个多线程的语言,例如这个操作删除了某个dom元素,而其他操作正在移动这个元素,会造成一些不可预见的问题,因此js从开始就是一个单线程语言。
JavaScript 只有一个主线程和一个执行栈。
当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。

虚拟dom是什么? 原理? 优缺点?

Virtual DOM是对DOM的抽象,本质上是JavaScript对象,这个对象就是更加轻量级的对DOM的描述。

  • 真实DOM的优势:易用

  • 真实DOM的缺点:效率低,解析速度慢,内存占用量过高;性能差,频繁操作真实DOM,易于导致重绘与回流。

  • 虚拟DOM的优势:

简单方便:如果使用手动操作真实DOM来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难
性能方面:使用Virtual DOM,能够有效避免真实DOM数频繁更新,减少多次引起重绘与回流,提高性能
跨平台:React借助虚拟DOM, 带来了跨平台的能力,一套代码多端运行

  • 虚拟DOM的缺点:

在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化
首次渲染大量DOM时,由于多了一层虚拟DOM的计算,速度比正常稍慢

async await对比promise的优缺点

async/await优点:
  1. 它做到了真正的串行的同步写法,代码阅读相对容易
  2. 对于条件语句和其他流程语句比较友好,可以直接写到判断条件里面
function a() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(222)
      }, 2222)
    })
  };
async function f() {
    try {
      if ( await a() === 222) {
        console.log('yes, it is!') // 会打印
      }
    } catch (err) {
      // ...
    }
  }
  1. 处理复杂流程时,在代码清晰度方面有优势
async/await缺点:
  1. 无法处理promise返回的reject对象,要借助try...catch...

  2. 用 await 可能会导致性能问题,因为 await 会阻塞代码,也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成,导致代码失去了并发性。promise 可以通过all解决并发问题(Promise.all([ajax1(), ajax2()]))。

  3. try...catch...内部的变量无法传递给下一个try...catch...,Promise和then/catch内部定义的变量,能通过then链条的参数传递到下一个then/catch,但是async/await的try内部的变量,如果用let和const定义则无法传递到下一个try...catch...,只能在外层作用域先定义好。
    但async/await确确实实是解决了promise一些问题的。更加灵活的处理异步

promise的一些问题:
  1. 一旦执行,无法中途取消,链式调用多个then中间不能随便跳出来
  2. 错误无法在外部被捕捉到,只能在内部进行预判处理,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
  3. Promise内部如何执行,监测起来很难,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

防抖和节流

  • 函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
  • 函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
// 防抖
function debounce(fn, date) {
  let timer  //声明接收定时器的变量
  return function (...arg) {  // 获取参数
    timer && clearTimeout(timer)  // 清空定时器
    timer = setTimeout(() => {  //  生成新的定时器
      //因为箭头函数里的this指向上层作用域的this,所以这里可以直接用this,不需要声明其他的变量来接收
      fn.apply(this, arg) // fn()
    }, date)
  }
}
//--------------------------------
// 节流
function debounce(fn, data) {
  let timer = +new Date()  // 声明初始时间
  return function (...arg) { // 获取参数
    let newTimer = +new Date()  // 获取触发事件的时间
    if (newTimer - timer >= data) {  // 时间判断,是否满足条件
      fn.apply(this, arg)  // 调用需要执行的函数,修改this值,并且传入参数
      timer = +new Date() // 重置初始时间
    }
  }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值