一些常见前端面试题(持续更新…)

发布于 2023-03-18  155 次阅读


1.防抖与节流

防抖:在同一段时间内,如果指定的函数不断的触发,只会执行最后一次,也就是说,执行函数时,时间会被重新计算。例如:指定了1000ms的防抖函数,只要你在1000ms的时间间隔内一直点击,无论过去多长时间,只会在最后一次点击的时候生效。

const delay = (function () {
        let timer = 0
        return function (callback, ms) {
            clearTimeout(timer)
            timer = setTimeout(callback, ms)
        }
})()

节流:在同一段时间内,只触发1次函数,可以理解为无论执行了几次函数,都只会在每xxx时间内执行一次。例如:指定了1000ms的节流函数,2000ms内点击10次按钮会在第1000ms和第2000ms执行2次点击事件。

function throttle(fn,wait) {
    let timeout=null;
    return function () {
        let that=this;
        let args=arguments;
        if (!timeout) {
            timeout=setTimeout(()=>{
                timeout=null;
                fn.apply(that,args);
            },wait)
        }
    }
}

2.为什么CSS外链需要放头部?

因为页面整个展示都会通过html的解析与渲染过程。css外链无论放哪一个位置都不会影响html的解析,但是影响html的渲染,如果css放头部,则css的下载和解析是可以和html的解析同步完成,如果放到了尾部,则会优先解析出html再去通过css渲染页面,页面就会出现先出现一个没有样式的页面,再完成解析造成闪动的情况,特别是网络不好的情况下。

3.为什么script建议放尾部

页面的解析执行过程是从上到下的,当解析到script的时候,就会立刻下载执行,中断html的解析过程,如果script响应时间很长,会造成页面长时间无响应,影响体验,也就是“阻塞效应”。

对于这种情况,可以进行一些额外的设置,来改变这种模式:

1.defer属性:给js脚本添加defer属性,会让脚本的加载与文档的解析同步,等页面解析完成后,再去执行脚本,但是兼容性不好。
2.async属性:也就是异步属性,和defer差不多的是同样是一起加载,但是如果页面慢与脚本解析完成的话,同样会引起阻塞的情况。
3.动态引入:可以监听页面的加载完成事件,页面加载完成后,动态创建script进行引入。

4.let和const的区别

const是常量,不允许修改,const为对象时,可以修改对象内部值的数据
let是块作用域,只有当前模块上才能访问,必须要声明后才能使用,而且不允许重新定义
var是函数作用域,可以先使用再声明,比如再for循环内使用,for循环外是可以访问的,而且可以重新定义

5.vue双向绑定原理

vue双向绑定是通过数据劫持、组合、发布订阅模式的方式来实现的,核心是通过Object.defineProperty()的get和set来进行数据劫持

6.html5的新特性

1.新增header,footer,nav,section,article,main等语义化标签
2.增强型表单,email,url等字段,placeholder和required等属性
3.新增audio和video标签
4.新增canvas绘图,svg绘图等
5.新增地图定位getCurrentPosition

7.Web Storage

1.localStorage:最大5M,在浏览器中永久保存,小程序中有时效性
2.cookie:最大支持4k,可以设置失效时间,如果不设置的话,关闭浏览失效,请求的时候,会包含在http请求中,产生额外的流量
3.sessionStorage:最大5M,当前会话网页下有效,关闭网页或浏览器失效

8.ES5和ES6

1.新增了const常量和let变量
2.新增了箭头函数()=>
3.es5需要使用require引入包,es6可以使用import引入
4.函数变量默认值设置function(x,y=1)
5.Promises链式异步操作对象
6.对象的操作,如合并对象...

9.Html语义化

1.利于SEO
2.在没有css的情况下能够更好的呈现页面结构
3.方便浏览器解析和一些很低版本的浏览器解读
4.可读性更好
5.alt title等用户体验性提升

10.relative、absolute、fixed、sticky

1.relative 相对定位于原来自己的位置,会占据原有元素空间,脱离文档流
2.absolute 绝对定位于距离自己最近的祖元素或document,完全从文档流删除,不会占用原有空间
3.fixed *固定定位于浏览器窗口
4.sticky 黏性定位当元素未超出目标区域时,相当于relative,当超出时,相当于fixed

11.判断数据类型的方法

1.typeof: typeof a==='function'
2.instanceof [] instanceof Array

12.cookies、session、localstorage

1.cookies:cookie一般是服务器发给客户端的信息,以文本的形式存在,每次请求都会在http请求同中带上该信息,还具有路径的概念,可以存在某个路径下
生命周期:如果不设置过期时间,会随浏览器关闭而关闭(会话cookie),如果设置了过期时间,则会保存到内存里,关闭浏览器不会消失,除非时间到期或手动清除
最大限制:4kb,一般不超过20个
2.session:仅在本地保存,不会发送给服务器
生命周期:会话级,浏览器关闭或网站关闭后,就会清除
最大限制:5M左右
3.localstorage:仅在本地保存,不会发送给服务器
生命周期:持久化保存在本地硬盘里,但是小程序里会有限制
最大限制:5M左右,在uniapp的打包的app里localstorage里无限制

13.Vue组件通信的方式

1.props:父组件向子组件传递数据,子组件还可以通过watch监听
2.$emit和$on可以实现任意组件之间的通信
3.全局事件总线
4.vuex

14.Vuex以及属性

全局状态管理,state上的状态改变时,相应的地方也会改变
1.state:基本数据(通过mapState把state和getters映射到当前组件的计算属性computed上)
2.getters:从基本数据派生的数据(getter可以对state计算操作,就是store的计算属性)
3.mutation:提交更改数据的方法,同步(mutation是进行提交而不是改变状态)
4.action:类似于一个装饰器,可以包裹mutation,使之异步
5.module:模块化vuex,在中大型应用中允许将store分割成不同模块
使用场景:在中大型应用中,使用vuex在组件外部管理状态,利于维护:用户信息存放,一处修改n处改变,订单状态刷新,优惠券,收货地址,购物车数量等更新

15.pop、push、shift、unshift

push() 方法可以在数组的末属添加一个或多个元素
shift() 方法把数组中的第一个元素删除
unshift() 方法可以在数组的前端添加一个或多个元素
pop() 方法把数组中的最后一个元素删除

通过pop和push可以实现类似栈的效果

16.强类型转换和隐式转换

强类型转换:String,Date,JSON.stringify、Number、parseInt之类的
隐式转换:

var a='1'
var b=0

console.log(+a) //输出 number类型1
console.log(b+'')//输出 string0

17.Vue双向绑定原理

vue数据的双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。其核心就是通过Object.defineProperty()方法设置set和get函数来实现数据的劫持,在数据变化时发布消息给订阅者,触发相应的监听回调。也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;

18.虚拟dom

模板 ==> 渲染函数 ==> 虚拟DOM树 ==> 真实DOM

虚拟DOM其实就是用一个原生的JS对象去描述一个DOM节点,实际上它只是对真实 DOM 的一层抽象。最终可以通过一系列操作使这棵树映射到真实环境上。

相当于在js与DOM之间做了一个缓存,利用patch(diff算法)对比新旧虚拟DOM记录到一个对象中按需更新, 最后创建真实的DOM

19.JS原型链

原型:被用于复制现有实例来生成新实例的函数
构造函数:用new来调用,就是为了创建一个自定义类
实例:是类在实例化之后一个一个具体的对象

原型链:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。

prototype
js中每一个函数都有prototype属性,这个属性指向函数的原型对象,每一个由原型对象派生的子对象,都有相同的属性。子对象就叫构造函数,从实例原型中获取相同的属性。所有创建在同一原型的prototype,无论怎么实例化,最终指向的原型都是同一个,减少了内存开销。

function Test(_arg){
    this.arg=_arg
}
Test.prototype.name='小明'
Test.prototype.fn1=function(){}
var t1=new Test(29)
var t2=new Test(20)

console.log(t1.name)//小明
console.log(t2.name)//小明

Proto
它是每一个子对象(除null外)都会有的一个新的属性,实例化(new)的时候,指向该对象的原型,说白了指向的是该构造函数的引用地址

console.log(t1.__proto__===Test.prototype)//true

构造函数 constructor
每个原型都有一个constructor属性,指向该关联的构造函数。

console.log(Test==Test.prototype.constructor)//true

-所有函数的proto都是指向Function的prototype
-构造函数new出来的对象proto指向构造函数的prototype
-非构造函数实例化出的对象或者对象的prototype的proto指向Object的prototype
-Object的prototype指向null

啊~~~~~~~~~
最后更新于 2023-03-29