JS前端高频面试

本文详细介绍了JavaScript中的数据类型、原始与引用类型、null和undefined的区别、变量类型判断、数组去重、类数组与数组、事件处理、异步编程方法(如Promise、setTimeout、Ajax)、cookies和浏览器存储、DOM与BOM概念,以及跨域问题的解决方案。
摘要由CSDN通过智能技术生成

JS数据类型有哪些,区别是什么

js数据类型分为原始数据类型和引用数据类型。

原始数据类型包括:number,string,boolean,null,undefined,和es6新增的两种类型:bigint 和 symbol。(Symbol是一个函数,会返回一个Symbol类型的值,每个Symbol函数返回的都是唯一的,可以作为对象的key。bigInt特点是数据涵盖范围大,能解决超出普通数据类型范围报错问题)

引用数据类型也叫复杂数据类型,通常用Object代表,例如普通对象,数组,正则等都属于Object。

null和undefined区别,如何让一个属性变为null

区别:
1. null相当于定义变量无值但会占内存空间。undefined是变量定义了但未进行赋值。 
2. null和undefined都是原始数据类型,在v-if中都会被识别为false。
3. typeof undefined判定为‘undfind’,而typeof null 会被判定为object(与底层的二进制有关)
4. undefined转化为数值时结果为NAN,null为0

让属性变为null:
先定义,再赋空值。

js判断变量类型的方法

1. type of:常用于判断原始数据类型(null会判断为object),引用数据类型除了function其他都会返回object
2. instanceof

3. constructor

4. toString(常用)

typeof 和 instanceof 区别

typeof:常用于判断原始数据类型(null会判断为object),引用数据类型除了function其他都会返回object。typeof返回值为类型的字符串。

instanceof:常用于判断引用数据类型,返回的是布尔值。

数组去重

1. new Set():  let arr = [...new Set(...arr1)]
2. findIndex配合for循环 
3. includes配合for循环,或includes配合reduce
4. sort排序,找出第i项与第i-1项不一致的值
5. 利用对象的key去重。

类数组和数组的区别

1. 类数组与数组一样具有length与index属性,但是本质是Object
2. 类数组不能直接调用数组的API。
3. 可以通过for循环、es6拓展运算符、Array.form()转为数组。

什么是arguments

arguments是函数中传递的参数的集合。是一个类数组,与数组一样具有length和index属性,但本质是对象。不可以直接调用数组的API。可以通过通过for循环、es6拓展运算符、Array.form()转为数组。

map和forEach区别

1. map有返回值,返回的是一个全新的数组,不会改变原数组。
2. forEach没有返回值,forEach里面操作数据会影响原数组。
3. 需要注意的是,在map和forEach里面终止循环只能通过try catch抛出异常,return是没用的,如果有需要通过判断某个条件return终止的情况,可以使用some。

防抖和节流

防抖:多次触发 只执行最后一次。高频率触发的事件,在指定的单位时间内,只响应最后一次,若在n秒内重复触发,则重新计时。(创建一个定时器,如果n秒内触发了就清掉上一个定时器,重新创建一个新的定时器,n秒内没有重新出发了,就执行一次)

节流:规定时间内,只触发一次。n秒内只执行一次,若在n秒内重复触发,只有一次生效。(创建一个定时器,指定时间内触发直接return出去不做任何操作,过了n秒后执行后续操作,再次触发定时器。

事件代理(事件委托)是什么

事件代理又称事件委托。就是把原本需要绑定的事件,委托给父元素。事件代理的原理是事件冒泡,给父节点添加事件,当子节点点击时,会冒泡到父节点。

优点:
1. 减小内存消耗。
2. 动态绑定事件。当动态添加或移除页面中的元素时,不需要再绑定或解绑事件。

拓展:事件传播分为三个阶段,捕获阶段 -> 目标阶段 -> 冒泡阶段

事件冒泡和事件捕获

事件捕获是由外到内的。在事件捕获阶段,事件会从根节点=>事件源(由外层到内层)进行事件传播。

事件冒泡是由内到外的,事件会从事件源=>根节点(由内层到外层)进行事件传播。
阻止事件冒泡:event.stopPropagation()
阻止默认事件:event.preventDefault()
return false: 同时阻止冒泡和默认事件。

如何判断对象为空

1. JSON.stringify(obj)=='{}'
2. Object.keys(obj).length==0
3. Object.getOwnPropertyNames(obj).length==0

es6新特性有哪些

1. 新增let 和 const 
2. 模版字符串 `${}`
3. 拓展运算符
4. 解构赋值
6. 箭头函数

let、const、var区别

1. var可以重复声明变量,不受限于块级作用域,可以在声明之前访问。
2. let 和 const 都是 es6 的新增特性,都支持块级作用域,都不可以在声明之前访问。
3. let 不能重复声明,const声明之后就必须赋值且不能重复赋值。

es6箭头函数

1. 箭头函数跟普通函数比较,写法更加简洁。
2. 没有自己的this,this是从外部获取的,所以call、bind、apply都无法改变this指向。
3. 不能使用new,没有arguments。(new的第二步会将this指向新对象,而箭头函数没有this,所以不可以)

说一说this指向(普通函数,箭头函数)

首先箭头函数是没有this的,this是从外部获取得到的。

对于普通函数this指向window,在事件处理函数中,谁触发就指向谁。可以通过call,bind,apply来改变this指向。

call、bind、apply区别

call和apply主要是穿参方式的不同,call是以单一参数形式传入,apply是以数组形式传入。

call和bind的主要区别在于,call是立即执行,而bind需要手动调用执行。

拓展运算符

1. 用于数组拷贝:[...arr]
2. 合并数组
3. 类数组转数组 

对闭包的理解

说起闭包要先说一下作用域,作用域是当前执行代码对于变量的访问权限,起到隔离变量的作用。

查找变量会从当前作用域开始查找,逐层向外层作用域查找,直到抵达最顶层的全局作用域即停止。这个一层一层的关系就叫做作用域链。
而闭包其实就是函数作用域的一个产物。例如函数a里面包含了一个函数b,函数b是能访问到函数a中的变量的,这个时候就已经产生闭包了。
那么如果想要在函数a外面访问到函数a里面的变量,这可以将函数b return出去,所以我们是为了利用到闭包函数才将它return出去,这就造就了闭包就是和return关联在一起的误区。

变量提升

简单来说就是先声明,再赋值。声明在编译阶段就完成了,赋值在原地等待。

声明都会被移动到各自作用域的最顶端,这个过程就叫做提升。
函数声明和变量声明都会被提升,但是函数声明优先于变量声明。
只有 var 声明的变量才会提升,let 和 const 不会。

这里需要注意一点:函数声明会被提升,但是函数表达式不会,因为函数表达式实际上是赋值操作。所以函数声明和函数表达式的一个重要区别就是函数声明可以在声明之前调用,函数表达式必须在表达式之后才可调用。

说一说new会发生什么

1. 分配空间,创建一个新对象
2. this指向这个对象
3. 添加属性和方法
4. 返回这个新对象

深拷贝

1. 递归

copy(obj) {
    let resultObj = Array.isArray(obj) ? [] : {};
    if(obj && typeof obj == 'object') {
        Object.keys(obj)?.forEach(objkey=>{
            if(obj[objkey] == 'object') {
                resultObj[objkey] = copy(obj[objkey]);
            } else {
                resultObj[objkey] = obj[objkey];
            }
        })
    };
    return resultObj;
}

2. JSON.parse(JSON.stringify(obj))

补充:浅拷贝和深拷贝的区别
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

注意:object.assign和拓展运算符是浅拷贝

数组排序

1. 数组sort()方法
2. 选择排序:首先在未排序数组中找到最小(大)元素,存放在数组的起始位置。再从剩余数组元素中继续寻找最小(大)元素,返回放在已排序数组的末尾。重复操作,直到排序完成。
3. 冒泡排序:一次比较两个相邻的数,判断大小规则互换位置,一次比较就能够将最大或最小的值放在数组最后一位。除数组最后一项外,重复操作。
4. 快速排序:取数组的中间值为基准数据,把数组中剩余的小于基准数据的数据放在基准数据的左边的新数组中,大于的放在右边的新数组中,再分别将这两个新数组按照原数组取中间值,划分左右新数组的方法进行递归。
5. 插入排序:假设前面 n-1 的元素已经排好序,将第n个元素插入到前面已经排好的序列中。
6. 希尔排序:基于插入排序进行的优化,先将整个数组按照数组长度的一半作为基准n,数组按照当前项和间隔的第n项进行分组,使用插入排序,完成后,再将整个数组按照n的一半进行分组使用插入排序,重复以上步骤,直到分组长度为1为止。

拓展:既然希尔排序用到了插入排序,那为什么不直接使用插入排序呢。因为数组长度越大,使用插入排序比较次数和交换次数远远高于希尔排序,效率更低。

说一说事件循环Event Loop,宏任务和微任务

事件循环(事件队列):
JS是单线程语言,主线程在执行时会不断循环往复从同步队列中读取任务,执行任务,当同步队列执行完毕后再从异步队列中依次执行。

宏任务和微任务:
宏任务和微任务都是异步任务,在执行上微任务优先级高于宏任务,因此每次都会先执行微任务再执行宏任务。微任务有:promise、process.nextTick;宏任务有:定时器、ajax等。

说一说实现JS异步的方法

1. promise
2. setTimeout
3. async/await
4. 回调函数(把函数作为某个函数的入参)

说一说promise是什么,怎样使用

定义:
Promise是异步编程的一种解决方案,解决了回调地狱问题。

使用方法:
1. Promise通过new Promise()创建实例
2. Promise有三种状态:pending-进行中,fulfilled-已成功,rejected-已失败。
3. 成功调用resolve,失败调用reject。在resolve()中返回的信息可以通过.then()获取,在reject()中返回的信息可以在.catch()中获取。

拓展:同步和异步
同步:一个进程在执行某个请求的时候,需要等待回调返回,才能执行下一个。
异步:执行某个请求是,不需要等待回调。

setTimeout Promise Async/Await 区别

三者都可以实现js异步。主要区别体现在事件循环中。

setTimeout属于宏任务,Promise属于微任务。

1. setTimeout回调函数执行时放到宏任务队列
2. Promise 本身是同步的立即执行函数,then 则会被分发到 微任务 的 Promise 队列中去
3. Async\Await后面的表达式会先执行一遍,将await后面的代码加入到微任务中,然后就会跳出整个async函数来执行后面的代码。

cookie,sessionStorage,localStorage区别

区别:
1. 它们都是浏览器的储存器,存储在浏览器的本地。
2. cookie是由服务器写入的(存疑:cookie前端也可以写,但是不安全,可以被篡改,后端写更安全)。而sessionStorage,localStorage是由前端写入的。
3. 生命周期的区别:cookie生命周期是写入的时候就设置好的。localStorage会一直存在,除非手动清除。sessionStorage页面关闭就会自动清除。
4. 空间大小:cookie存储空间大约4kb,sessionStorage,localStorage比较大,大约5M。
5. 都遵循同源政策(),sessionStorage还需要限制同一页面。

应用:
cookie:储存登陆信息,token等
localStorage:储存不易变更的数据
sessionStorage:检测用户的一些行为,比如是否刷新过页面

如何实现可过期的localStorage

1. 惰性删除:存储时间和获取时间比较,超过过期时间就删掉
2. 定时删除:每隔一段时间就执行一次删除操作。

说一说创建ajax过程

ajax是什么:ajax是一种异步通信的方法,直接通过js向服务器发送http请求。

原理:简单来说通过XMLHttpRequest对象来向服务器发送异步请求,从服务器获得数据,并且更新部分网页。

1. 创建实例对象:let xhr = new XMLHttpRequest()
2. 使用xhr.open()方法创建http请求
3. xhr.onreadyStateChange方法监听响应
4. xhr.send()发送请求

说一说fetch请求方式

1. fetch是一种原生的http请求方式,是XMLHttpRequest的一种替代方案,写法更加简洁。
2. fetch请求是基于promise,无论请求成功还是失败,都会返回一个promise。请求成功会返回response对象,失败则会得到TypeError。

原型和原型链

原型:每个函数中都有一个特殊的对象,叫做prototype,该对象指向原型对象。
原型链:每个对象都拥有原型对象,并从中继承属性和方法。原型对象也可能有原型,一层一层以此类推。这个关系就叫做原型链。

原型链的查找机制:
首先查找自身对象,再查找原型,还是没有找到就查找原型的原型,一层一层往上查找直到查找到或者到达原型链顶端。(JS的顶级对象是Object,Object.prototype是顶层原型对象。Object.prototype._proto_===null)

说一说JS继承的方法和优缺点

原型链继承
优:写法方便简单
缺:对象实例共享所有继承属性和方法。创建子类实例时,不能向父类传参。

借用构造函数继承
优:解决了原型链继承不能传参问题,避免父类的原型被所有实例共享。
缺:方法在构造函数中定义,无法实现函数复用,每次创建实例对象都会重新创建一遍方法。

组合继承
优:融合原型链继承和借用构造函数继承的优点,是js中最常用的继承方法。
缺:父类构造函数会被调用两次,一次是在创建子类原型对象时,一次是在子类构造函数内部。

原型式继承
优:不需要单独创建构造函数。
缺:属性中包含的引用值会被共享,子类实例不能向父类传参。

寄生式继承
优:写法简单,不需要单独创建构造函数。
缺:方法在构造函数中定义,不能做到函数复用。

寄生组合式继承
优:只调用一次父构造函数,并且能避免子类原型对象上不需要的多余属性。
缺:写法复杂。

说一说script标签的defer和async属性

浏览器会按照script的顺序对他们依次进行解析,前一个script中的代码执行完成后,才会执行后一个。并且在解析过程中,页面的其他处理会暂停。script有两个异步属性,defer和async。需要注意的是这两个属性只对外部资源文件有用。

defer和async都是异步加载,区别在于执行时机不同。
1. async在加载完后立即执行,执行过程仍会阻碍后续的html解析。defer是在html解析完成再执行的,不会阻碍html执行。
2. async不能保证script标签的执行顺序,谁先加载完就执行谁。而defer是按顺序执行的。

js延迟加载的方式

1. 将js脚本放在文档底部。
2. 添加defer、async属性
3. 动态创建script标签

保持前后端实时通讯的方法

轮询:每隔一定时间就询问一次。实现简单,但会很耗流量。适用于小型应用,实时性并不高。

长轮训:客户端发送请求后,若没有消息就一直等待,有消息才返回。长轮训在一定程度上能减小CPU利用率问题,但保持连接会消耗资源,服务器一直不返回数据会连接超时。

WebSocket:是HTML5新增的一种协议,实现浏览器和服务器的通讯。实时性高,但浏览器支持程度不一致。

iframe:在页面创建一个隐藏的iframe,通过src属性在客户端和服务的创建一条长连接,服务器向iframe传输数据来实时更新页面。这种方式浏览器兼容性好,消息能实时到达。缺点是页面会显示没有加载完成,图标一直旋转。

SSE:是建立在浏览器和服务器之间的通讯渠道,服务器向浏览器推送消息。是单向通道,只能服务器向浏览器发送。

说一下浏览器输入URL发生了什么

1. URL解析:判断是搜索内容还是URL
2. 查找本地缓存:如果有缓存则直接返回页面,没有则进入网络请求阶段
3. DNS域名解析
4. 三次握手建立TCP连接
5. 发送http请求
6. 服务器响应并返回结果
7. 通过四次挥手断开TCP连接
8. 浏览器渲染
9. js引擎解析

说一说浏览器如何渲染页面

1. 解析HTML,生成DOM树。
2. 解析CSS,生成CSS规则树。
3. 两棵树结合,生成Render树。
4. 计算布局,绘制页面所有节点。
5. 绘制布局。

说一下浏览器垃圾回收机制

程序在执行过程中会产生一些用不到的内存变量,这些变量会一直占据内存。这个时候就需要垃圾回收机制来清理这些用不到的变量,释放内存空间。

垃圾回收机制最常见的有两种:标记清除和引用计数。
标记清除:在执行前将所有变量打上标记,之后完成后未被打上标记的变量就会被当作垃圾回收。浏览器会隔一段时间进行一次标记清除。(缺点是被释放出来的空间不是连续的,导致内存空间不连续)
引用计数:每个变量存在一个计数器记录引用次数,当引用次数为0时,该变量会被回收。(缺点:当出现相互引用时,会导致无法被清除)

说一说跨域是什么,怎样解决跨域问题

在当前页面发送ajax请求时,由于浏览器同源政策限制,要求协议、域名、端口一致。三者任意一项与当前页面url不一致即为跨域。

解决跨域方法:
1. cors跨域资源共享:目前最常用的一种方式,通过设置后端允许跨域。
2. jsonp:利用script标签不受浏览器同源政策限制,将回调函数作为参数拼接在url中。后端收到请求,调用该回调函数,并将数据作为参数返回回去。
3. node中间件、nginx反向代理:让请求发给代理服务器,静态页面和代理服务器同源,通过代理服务器再向后端服务器发送请求。
4. postMessage:HTML5新增特性。通过发送和接收api实现跨域通讯。

DOM和BOM是什么

javascript 有三部分构成,ECMAScript,DOM和BOM。

ECMScript:描述了JS的语法和基本对象。
DOM:叫做文档对象模型,提供处理网页内容的方法和接口。DOM是W3C的标准,及所有浏览器公共遵守的标准。document是DOM对象。最常见的DOM操作:创建、查询、更新、添加、删除节点。
BOM:叫做浏览器对象模型,提供与浏览器交互的方法和接口。BOM不同浏览器定义有差异。常见的BOM对象有:window、location、screen、history等。

  • 20
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值