前端面试题(一)

回调地狱和promise:

回调函数嵌套回调函数的情况就是回调地狱;回调地狱一般是为了实现代码的执行顺序时出现的操作,它会让代码的可读性非常差,后期难维护。

Promise解决回调地狱:

promise是js的一个原生对象,是一种异步编程的解决方案。

promise有三个状态:pending、fulfilled、rejected,两个过程:pending->fulfilled pending->rejected ,一旦状态发生改变就不会再改变;如果状态已经改变再对promise对象添加回调函数,得到的结果是不会改变的

promise函数接收一个函数作为参数,这个函数的两个参数是resolve和reject,要执行的异步任务写在这个函数体内,异步任务执行成功调用resolve函数返回成功的结果,失败调用reject函数。

promise对象的.then方法用来接收处理成功时响应的数据,.catch方法接收处理失败时响应的数据。

promise的链式编程可以保证代码的执行顺序,但是在每次使用.then做完处理时要return一个promise对象,这样后面再使用then时才能接收到数据。

async await:

async作为一个关键词放在声明的函数前,表示该函数是一个异步任务,不会阻塞后面的函数执行;async函数返回数据时自动封装成一个promise对象。

await只能在使用async定义的函数内使用,不能单独使用

await后面可以跟任何表达式(更多的是跟一个返回promise对象的表达式)

await可以直接拿到promise中resolve的数据

当代码执行到async函数中的await时,代码会在此处等待不再往后执行,直到await拿到promise对象中resolve的数据才往后执行。

promise.all()

promise.all()方法提供了并行执行异步操作的能力,可以将多个promise实例包装成一个新的promise实例

promise.all()接收一个数组作为参数,数组中的元素都是promise实例,如果不是则会先调用promise.resolve方法,将其转为promise实例再进一步处理;

只有传入的所有promise实例的状态都变成fulfilled,promise.all()的状态才会变成fulfilled,此时promise实例的返回值组成一个数组传递给promise.all()的回调

只要传入的promise实例之中有一个状态变为rejected,promise.all()的状态就变rejected,此时第一个状态变为rejected的实例的返回值会传递给promise.all()的回调

如果传入的promise实例定义了自己的catch方法,它的状态变为rejected并不会触发promise.all()的catch方法

同源策略和跨域:

同源策略:

同源是指两个URL地址有完全相同的协议、域名、端口号

同源策略是浏览器提供的一个安全功能,不允许两个非同源的URL地址之间进行资源交互

跨域:

两个非同源的URL地址之间要进行资源交换就是跨域

怎么解决跨域:

1.使用 Vue-cli 脚手架搭建项目时 proxyTable 解决跨域问题
    原理:在前端服务和后端接口服务之间架设一个中间代理服务,它的地址保持和前端服务一致
        1.代理服务和前端服务之间协议、域名、端口三者统一不存在跨域问题,可以直接发送请求
        2.代理服务和后端服务之间没有同源策略的限制,可以直接发送请求
    实现方法:在vue.config.js文件中去配置devServer
    
2.后端配置 CORS(跨域资源共享)

this的各种情况:

在一般函数中this指向全局对象window

作为构造函数调用,this指向构造出的实例对象

以方法的形式调用,this指向调用方法的对象

call、apply、bind可以改变this的指向

箭头函数和普通函数的区别:

1.箭头函数中的this指向它的上层作用域的this,箭头函数没有自己的this

2.箭头函数比普通函数更加简洁

3.箭头函数继承来的this指向永远不会改变

4.call、apply、bind不能改变箭头函数中的this指向

5.箭头函数不能作为构造函数使用

6.箭头函数没有prototype

插槽:

slot就是插槽,插槽主要作用是用来拓展组件;在重复使用一个组件时可以通过修改少量代码达到复用的效果;插槽分为默认插槽、具名插槽和作用域插槽,默认插槽和具名插槽都是元素和拓展结构在父元素中,子组件中直接占位,在父组件中添加拓展结构,区别就是具名插槽有名字,当多个插槽存在时可以一一对应;作用域插槽是数据在子组件中,拓展的结构在父组件中,通过父子通信的方式传递数据给数据一个新的作用域。

vue中的data为什么是一个函数:

data写成一个函数,这样每次使用就会返回一份新的data,就相当于创建出一个新的私有数据空间,各组件实例修改和使用的时候不会互相影响不会有数据污染的问题

如果data写成一个对象,对象是引用数据类型,保存的是数据的内存地址,所有组件实例使用的都是同一个data就会造成一个变全部都变的结果,会有数据污染;所以vue组件的data必须写成一个函数

原型和原型链:

函数都有一个prototype属性,这个属性就是原型;原型可以挂一些属性和方法共享给实例使用,也可以做继承

对象都有一个proto属性,这个属性指向对象的原型对象,原型对象也是一个对象,也有proto属性指向它的原型对象,这样一层一层形成的链式结构就称为原型链;指到最顶层找不到则返回null

v-model语法糖:

在父组件中的子组件标签上绑定value属性,向子组件中传入一个名是value的属性,子组件中通过props接收这个属性,然后子组件中发射一个input事件,父组件中监听input事件,这个事件的回调中修改value所绑定的值

v-model一个指令实现两个功能:父传子(value属性)子传父(input事件)

节流和防抖:

节流:频繁触发的事件在指定的时间内只触发一次

场景:鼠标移动/滚动事件,快速点击事件

可以利用定时器完成:等定时器执行完毕才会开启下一个新的定时器

防抖:频繁触发的事件在指定的时间内只触发最后一次

场景:搜索框输入

可以利用定时器完成:每次触发都清除掉之前的定时器(重新开定时器)

在开发中一般可以使用lodash库中的throttle(节流)和debounce(防抖)来做

回流和重绘:

浏览器把获取到的html代码解析成一个dom树,html中的每一个元素都是dom树的一个节点,根节点是document对象;dom树上因元素的尺寸大小、布局、显隐等改变需要重新构建称为回流,页面至少会发生一次回流,在页面第一次渲染的时候

dom树上元素更新新属性只影响元素的外观、风格不影响布局,称为重绘; 重绘不一定会发生回流,但是回流一定会触发重绘

最小化回流和重绘

1.避免使用css表达式

2.尽量使用css属性简写,

3.避免使用table布局(table中元素一旦触发回流就会导致table中所有元素回流)

4.批量修改元素的样式

5.要对元素进行复杂操作时,可以先隐藏(display:’none‘)操作完之后再显示

eventloop事件循环:

js是单线程的,防止代码阻塞把代码分成同步任务和异步任务,异步任务又分成微任务和宏任务;同步代码js引擎执行,异步代码交给宿主环境(浏览器)执行,同步代码直接放入执行栈中,异步代码等时机成熟时会加入到任务队列中排队,执行栈中的代码执行完会去查看任务队列是否有任务,有就加入执行栈执行,反复循环查看执行这个过程就是事件循环

微任务和宏任务

微任务由js引擎发起,宏任务由宿主(浏览器、node)发起

微任务:promise对象的.then()和.catch方法、async await等;

宏任务:script代码块、定时器(setTimeout和setIniterval)等;

先执行微任务再执行宏任务

闭包:

执行函数时,只要在函数中使用了外部局部变量就创建了闭包;作用域链是实现闭包的手段

闭包形成的条件:函数的嵌套;内部函数引用外部函数的变量(延长外部函数的变量生命周期);封装私有化变量;创建模块等

闭包的优点:让外部作用域访问到函数内部的变量,可以让全局变量持续保存下来不随它的上下文一起销毁(延长变量的生命周期)

闭包的缺点:占用内存空间、内存泄漏、栈溢出

优化:主动销毁不使用的闭包:将闭包函数赋值为null即销毁

js垃圾回收

当我们创建函数、数组、对象的时候会自动分配相应的内存空间;当对象不在被引用或不能从根上访问到的时候就是垃圾

引用计数法:

内部通过引用计数器判断一个对象的引用次数是否为0来判断它是否是一个垃圾对象,如果为0则将它所在的内存空间进行回收释放和再使用。

优点:可以及时回收垃圾,减少程序卡顿时间

缺点:资源消耗大,无法回收循环引用的对象;

标记清除法:

将整个垃圾回收操作分成两个阶段:

1.遍历所有对象找到活动的对象并进行标记

2.遍历所有的对象清除那些没有标记的对象回收空间并清除第一阶段的标记

优点:相对于引用计数法来说:可以回收循环引用的对象

缺点:不能及时回收垃圾对象;由于所回收的垃圾对象在地址上是不连续的,回收之后分散在各个角落,会造成后续使用问题

标记整理法:

是标记清除法的增强,标记阶段的操作和标记清除法一样,在清除阶段之前会先整理、移动对象的位置,让他们在地址上是一个连续的空间。

优点:减少碎片化空间

缺点:和标记清除法一样,不会立即回收垃圾对象

==和===

都是用来判断是否相等的;区别是:判断相等程度的深浅不同

==:判断相等的程度较浅,只判断数值是否相同不判断数据类型

===:即判断数值是否相同也判断数据类型是否相同

怎么判断两个对象相等

1.判断两个对象是否指向同一内存地址

2.获取对象所有键名数组,判断两个对象的键名数组是否相等,遍历键名判断键值是否都相等

判断一个值是什么类型的方法

1.typeof:返回基础数据类型

2.instanceof:判断一个值是否属于某个构造函数

3.Object.prototype.toString.call():返回[Object,xxx]格式,判断一个值的数据类型

4.constructor:指向对象的构造函数

事件委托(事件代理)

利用浏览器的冒泡机制,事件在冒泡过程中会上传到父节点,父节点可以通过事件对象获取到子节点,把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件

优点:不用再为每一个子元素都绑定一个监听事件,减少内存上的消耗;

使用事件委托还可以实现动态绑定,新增的节点也不需要再单独添加监听事件;

typeof和instanceof

它们都是判断数据类型的方法,typeof返回的是基础类型,instanceof返回的是布尔值;

typeof可以判断数据的基本类型(null除外),但是不能判断复杂数据类型(function除外);instanceof可以准确的判断引用复杂数据类型,但不能正确的判断基础数据类型

如果需要通用检测数据类型,可以采用Object.prototype.toString.call(),调用该方法统一返回[Object,xxx]字符串格式

js继承的方法

1.原型链继承:让一个构造函数的原型是另一个类型的实例,这个构造函数new出来的实例就具有该实例的属性

2.构造函数继承:定义一个父构造函数this指向Window,再定义一个子构造函数this指向new出的实例对象,子构造函数通过call或apply改变父构造函数的this指向继承父构造函数的属性

3.组合继承(经典继承):将原型链和借用父构造函数组合到一块,用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承

for in 和 for of

for in 和 for of 都是用来遍历的属性

区别:

1.for in 可以用于对数组或对象进行遍历,遍历对象得到对象的key,for in 如果用来遍历字符串,可以得到字符串的下标

2.for of和forEach一样,直接得到值,for of不能用于遍历对象

new操作符具体做了什么

new操作符用于创建一个给定构造函数的实例对象

1.先创建一个新的对象

2.将新的对象和构造函数通过原型链进行链接

3.将构造函数的this绑定到新的对象上

4.根据构造函数类型做判断,如果是基础类型则返回新对象,如果是引用类型,就返回这个引用类型的对象

冒泡排序

冒泡排序是一种简单的排序算法,它重复地遍历要排序的列表,比较相邻的两个元素,并按照大小顺序交换它们,直到整个列表排序完成
    从列表的第一个元素开始,比较它与下一个元素的大小,如果当前元素大于下一个元素则交换它们的位置,继续比较下一个元素,直到到达     列表的最后一个元素,重复比较步骤,每次遍历列表时都会将最大的元素‘冒泡’到列表的末尾,直到整个列表排序完成

冒泡排序的时间复杂度为O(n^2),其中n是列表的长度。尽管冒泡排序的时间复杂度较高,但它的实现简单,适用于小型数据集或部分有序的数据集。

截取字符串substring()方法

substring()方法返回一个字符串在开始索引到结束索引之间的一个子集,substring(开始的索引,结束的索引),包括第一个索引,不包括第二个索引

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值