0


前端高频面试题

一、js

1.什么是js原型?原型链是什么?

所有的函数默认都有一个“prototype”这样公有且不可枚举的属性,它会指向另一个对象,这个对象就是原型。

构造函数的prototype属性就等于实例对象的__proto__属性,构造函数的prototype属性被称为显式原型,实例对象的__proto__属性被称为隐式原型

原型链,当访问对象的属性或方法时,首先对象会从自身去找,找不到就会往原型中去找,也就是它构造函数的“prototype”中,如果原型中找不到,即构造函数中也没有该属性,就会往原型后面的原型上去找,这样就形成了链式的结构,称为原型链。

       1.原型链可以解决什么问题?

           对象共享属性和共享方法

        2. 谁有原型

                  函数拥有:prototype

                  对象拥有: __proto__

       3.对象查找属性或者方法的顺序

  先在对象本身找 --> 构造函数中查找 -->  对象的原型 --> 构造函数的原型 -->当前原型的原型中
function Fun(){
  this.name = '1';
}
Fun.prototype.name='2';
let newObj = new Fun();
newObj.name='3';
newObj.__proto__.name='4';
Object.prototype.name='5';
 console.log(newObj.name)    // 3 -> 1 -> 4 -> 2 -> 5
        
2.闭包是什么?应用场景是什么?

闭包:函数嵌套函数,内部函数就是闭包,即有权访问另一个函数作用域的变量的函数

场景:立即执行函数,缓存,实现封装,实现模板

示例:

function outerFun(){
    let a = 10;
    function innerFun(){
        console.log(a);
    }
    return innerFun;
}
let fun = outerFun();
fun();     // 10     或者写成outerFun()();
3.promise是什么?和async await的区别是什么?

promise是处理异步操作的一种解决方案。解决了回调地狱问题(层层嵌套的回调函数)

场景:封装ajax

async await是异步处理同步化。是ES7引入的一种基于Promise的语法糖,用于更简洁的处理异步操作。它可以让异步的代码看起来更像是同步的代码,提供了更好的可读性和编写体验。

promise 与async await的区别是:

  1. 语法差异:Promise 使用 then() 和 catch() 方法来处理异步操作的结果和错误,而 async/await 使用 async 和 await 关键字以更直观的方式编写异步代码。
  2. 错误处理:Promise 使用 catch() 方法来捕获错误,而 async/await 可以使用 try-catch 语句来捕获错误。
  3. 代码可读性:async/await 可以让异步代码看起来更像同步代码,逻辑更清晰,可读性更好。通过使用 await 关键字,可以将异步操作写成一系列顺序执行的语句,而不是嵌套的回调函数。
  4. 错误堆栈追踪:使用 Promise,错误发生时,堆栈追踪会包含 Promise 内部的异步操作,使得定位错误变得更困难。而使用 async/await,错误堆栈追踪会显示在出错的地方,更方便调试和定位错误。
  5. 错误处理方式:Promise 是使用回调函数来处理异步操作的错误,而 async/await 使用 try-catch 机制来处理错误。
  6. 虽然 async/await 是基于 Promise 的,但它们有不同的用法和语法,让异步代码的编写更简洁和可维护,并提供了更好的错误处理机制。
4.防抖,节流是什么?应用场景是什么?
   都是用于控制函数执行频率的方法,以避免不必要的重复操作,减轻系统负担,提高用户体验。

   区别:

   1. **防抖**:多次触发,只执行最后一次

        是指当事件被触发后,延迟n秒后再执行回调,如果在这n秒内事件又被触发,则重新计时

        **场景**:适用于需要等待用户停止操作后在执行的场景,如搜索框输入文本后请求接口、实时保存,表单输入验证,实时聊天,

   2. **节流**:规定时间内,只触发一次

         是指当事件持续触发时,每n秒只执行一次

        **场景**:按钮点击事件,鼠标移动事件(懒加载),浏览器resize

   实现:用于setTimeout实现
5.事件循环机制是什么?

js是单线程,指在同一时刻只能执行一个任务,任务只有在前一个任务执行完毕后才能开始下一个任务,但是js通过事件循环机制实现了异步。

Js事件循环机制,指的是js运行时环境按照一定的规则处理代码中的异步操作和事件的机制。包含任务队列,微任务队列,宏任务队列。

事件循环机制是指:

1、代码执行时,先执行同步任务,然后把异步任务放入任务队列中,等待执行。

2、当所有同步任务执行完毕后,js引擎会读取任务队列中的任务。

3、将队列中的第一个任务压入执行栈中执行,执行完毕后将其出栈。

4、如此循环执行,直到任务队列中的所有任务都执行完毕。

   总的来说,JavaScript通过事件循环机制来实现异步操作,将异步任务放到任务队列中,然后在任务队列中等待执行,直到JavaScript引擎空闲,再将任务队列中的任务拿出来执行。

 事件执行顺序:

     1.同步

     2.processs.nextTick

     3.微任务(promise.then、process. nextTick)

     4.宏任务(定时器,ajax,读取文件)

     5.setImmediate

  注意:setImmediate类似于setTimeout,但是会在当前事件的末尾立即执行回调函数,而不是等待一定的延迟时间。优先级比setTimeout高
        console.log(1);
        async function async1(){
            await async2();
            console.log(2);
        }
        async function async2(){
            console.log(3);
        }
        async1();
        setTimeout(()=>{
            console.log(4);
        })
        new Promise(resolve =>{
            console.log(5);
            resolve();
        }).then(()=>{
            console.log(6);
        }).then(()=>{
            console.log(7);
        })
        console.log(8); // 1 3 5 8 2 6 7 4
6.事件委托是什么?

事件委托也称之为事件代理,是利用冒泡机制,将事件的处理交给父元素或更高层级的元素来管理,而不是直接将事件处理程序绑定到每个子元素。

  • 事件冒泡:目标元素事件先触发,然后父元素事件触发
  • 事件捕获:父元素事件先触发,然后目标元素事件触发

事件执行顺序是:

  • 先事件捕获(从 Windows -> document 依次往下)
  • 再是目标事件处理
  • 最后是事件冒泡

addEventListener() 第三个参数为 false 事件触发顺序是冒泡顺序,true 为捕获顺序,默认为 false(这个冒泡和捕获是说当子元素被点击时,我是先执行还是后执行)。

好处:

  1. 减少事件处理程序的数量:使用事件委托可以减少需要绑定事件处理程序的元素数量。相对于为每个子元素绑定事件处理程序,只需在父元素上绑定一个事件处理程序即可。
  2. 动态绑定和增加灵活性:当新的子元素被添加到父元素中时,它们会自动继承来自父元素的事件处理程序,无需再次手动绑定事件。
  3. 节省内存消耗:由于事件委托采用冒泡机制,它只需绑定一个事件处理程序,因此减少了多个元素各自绑定事件处理程序所占用的内存。
7.什么是js作用域?作用域链
 js作用域指变量可访问的范围,主要有全局作用域和局部作用域

 局部作用域又分为函数作用域和块级作用域。 

注意:
  1. 除了函数外,js是没有块级作用域
  2. 作用域链:内部可以访问外部的变量,但是外部不可以访问内部的变量。 如果内部有,优先查找到内部,如果内部没有就查找外部的
  3. 声明变量是用var,还是没有写(window.)
  4. js有变量提升的机制【变量悬挂声明】
  5. 优先级:变量声明 > 声明普通函数 > 参数 > 变量提升
function Foo(){
    getName = function(){ console.log(1) }
    return this;
}
Foo.getName = function(){ console.log(2) }
Foo.prototype.getName=function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){
    console.log(5)
}

Foo.getName();      //2
getName();          //4
Foo().getName();    //1,相当于这里执行了window.getName = function(){ console.log(1) }
getName();          //1
new Foo().getName();//3

8.箭头函数和普通函数的区别?

  1. this的指向不同。普通函数,谁调用这个函数,this指向谁;箭头函数,在哪里定义函数,this指向谁,即箭头函数没有自己的this,它继承父级作用域的this。
  2. 原型和构造函数。箭头函数没有自己的原型(prototype),因此不能用作构造函数,使用new关键字调用箭头函数会报错,因为箭头函数没有构造函数(constructor)
  3. arguments对象。普通函数有一个arguments对象,可以以数组的方式呈现函数的所有参数,箭头函数则使用(...args)接受不定参数的参数
const fun = (...args)=>{
    //console.log(arguments)  //arguments is not defined
    console.log(...args)
}
fun(1,2,3);   // 1,2,3

二、vue

1.token是什么?说一下登录流程

Token是服务端生成的一串字符串,作为客户端进行请求的一个令牌。

大概的流程:

  1. 客户端使用用户名和密码请求登录(服务器--后端)
  2. 服务端收到请求,验证用户名和密码
  3. 验证成功后,服务端(后台)会生成一个token,然后把这个token发送给客户端(前端)
  4. 客户端收到token后把它存储起来,可以放在cookie或者Local Storage(本地存储)里
  5. 客户端每次向服务端发送请求的时候都需要带上服务端发给的token(客户端--前端)
  6. 服务端收到请求,然后去验证客户端请求里面带着token,如果验证成功,就向客户端返回请求的数据

注意:这个token必须在每次请求时传递给服务器,应该保存在请求头里,另外服务器要支持CORS(跨域资源共享)的策略

2.computed和watch的区别

1.缓存:computed依赖data中的属性,会自动计算并缓存结果,watch不支持

2.异步:computed不支持异步, 当有异步操作时无效,无法监听数据的变化,watch 支持异步操作

3.return:computed中的函数必须用return返回,watch没有return

4.在computed中的,属性都有一个get和一个 set 方法,当数据变化时,调用 set 方法;

3.路由懒加载的原理?

Vue 路由懒加载的实现依赖于动态导入语法

import()

来实现异步组件。当路由被访问时,

import()

函数会返回一个Promise对象,这个Promise对象会在组件被加载完成后resolve,并将组件作为参数传递给回调函数。这样,只有在组件被实际需要时才会加载对应的模块,从而实现了路由的懒加载。

Webpack编译打包后,会把每个路由组件的代码分割成多个js文件,初始化时不会加载这些js文件,只当激活路由组件才会去加载对应的js文件

使用动态导入时,只有在用户访问对应路由时才会加载相应的组件,这样可以减少初始加载时间和资源消耗

注意:路由懒加载通常只在非首页路由中使用,因为首页路由通常需要在页面加载时就立即呈现给用户,不适合使用懒加载

// 使用动态导入进行懒加载
component: () => import('@/components/Home.vue')
4.webpack的编译原理是什么?

webpack是一个串行流程,启动到结束依次执行:
1)初始化参数:解析webpack配置参数,合并shell传入和webpack.config.js文件配置的参数,形成最后的配置结果
2)开始编译:初始化Compiler对象,加载插件,执行对象的run方法
3)确定入口:从配置的entry入口,开始解析文件构建AST语法树,找出依赖,递归下去
4)编译模块:从entry出发,调用所有配置的loader对模块进行编译,再找到该模块所依赖的模块,递归本步骤直至所有入口依赖的文件经过处理
5)完成模块编译:得到每个模块被编译后的最终内容,以及依赖关系
6)输出资源:根据入口和模块之间的依赖关系,组装成一个个chunk,再把每个chunk转换为单独的文件输出
7)输出完成:输出所有的chunk到文件系统

plugin与loader的区别:

plugin:插件用于扩展webpack的功能。可以在打包的不同阶段执行不同特定的任务。如压缩代码。

loader:是在模块加载时,对其进行转换和处理。


本文转载自: https://blog.csdn.net/yecengxiangshi0426/article/details/139498486
版权归原作者 yecengxiangshi0426 所有, 如有侵权,请联系我们删除。

“前端高频面试题”的评论:

还没有评论