原生JS面试题整理(2023年)_概念题02

16、闭包的理解

1、闭包的基础概念

1)、官方解释:

一个函数和对其周围状态(变量)(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。

闭包让你可以在一个内部函数中访问到其外部函数的作用域。

2)、通俗的来说:闭包是 函数的嵌套定义,当调用外部函数时,会产生一个闭包。这个闭包就是外部函数定义的局部变量和内部函数。

2、闭包的作用:

内部函数可以使用外部函数的局部变量

让外部函数的局部变量一直存在于内存中(局部变量的生命周期变长了)。

3、缺点:

因为,闭包会让局部变量一直保持在内存中,所以,不要滥用闭包,以防止内存泄漏。

4、应用场景(能干啥):
  • 让数据私有化(同时使用setter和getter函数,可以保证数据的合法性)

  • 防抖

  • 节流

  • 单例模式

  • 柯里化(谨慎的说这一点)

    …………………… 其实用的挺多。

扩展:通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,在创建了一个闭包以后,这个函数的作用域就会一直保存到闭包不存在为止。

17、事件循环

javascript的事件循环(event loop)_javascript的事件循环(event loop)_jiang7701037的博客-csdn博客-CSDN博客

JS是单线程的,那么,它如何处理异步操作?

答:使用事件循环,执行过程如下:

  • 所有同步任务都在主线程上执行,形成一个执行栈。

  • 主线程之外,会存在一个任务队列,只要异步任务有了结果(如:setTimeout的等待时间到了),就在任务队列中放置一个事件(所以,也叫事件队列),进行排队(处于等待状态)。任务队列又分为宏任务队列和微任务队列。

    • 先执行微任务队列里的所有任务,所有任务都执行完毕后,再执行宏任务队列里的任务

    • 宏任务队里的任务执行完一个后,再清空微任务队列。再执行下一个宏任务队列的任务。

  • 当执行栈中的所有同步任务执行完后,就会读取任务队列(事件队列)中的任务(事件)。即:任务队列中的任务就结束了等待状态,进入执行栈。

主线程不断重复第三步。直到任务队列和执行栈里的代码执行完毕,这就是事件循环。

补充:

宏任务一般包括: setTimeout,setInterval,I/O 操作(包括AJAX请求),即上面的示例中setTimeout是宏任务。

微任务一般包括:promise.then() 里的回调函数

18、ES6的新增特性

  • let / const

  • 箭头函数

  • 函数参数默认值

  • 模板字符串

  • 解构赋值

    • 对象解构

    • 数组解构

  • ... 运算符

  • 对象简写形式

  • 模块化语法 import / export

  • Set / Map / for ... of

  • 定义类的class和类的继承:extends。

19、箭头函数与普通函数的区别

1、写法格式不同【可以不说这点,哈哈】

2、箭头函数不能作为构造函数使用

3、箭头函数不具有super

4、箭头函数中没有this和arguments

5、箭头函数不具有prototype原型对象

6、箭头函数的出现:一方面为了解决this指向问题,另一方面写法简单。

7、箭头函数使用的是词法分析,所以,this是词法作用域【这点看情况说】

20、map和filter有啥区别

相同点:都不会改变原数组,返回值都是数组,参数都是回调函数(数组的当前项,下标,数组本身)

不同点:

map返回值是一个新的数组,新数组中的元素为原始数组中的元素调用函数处理后的值,所以,新数组和原数组的元素个数一样。

filter 返回数组中满足提供的测试函数的所有元素的数组。

const arr = [5, 12, 8, 18, 35];
​
const result = arr.filter(item => item > 10);
​
console.log(item);//[12,18,35]

21、Map、foreach的区别

相同点:

forEach和map都是循环遍历数组中的每一项。forEach() 和 map() 里面每一次执行匿名函数都支持3个参数:数组中的当前项item,当前项的索引index,原始数组input。匿名函数中的this都是指Window。只能遍历数组。

不同点:

forEach没有返回值,但map中要有返回值,返回处理后的所有新元素组成的数组。

22、ajax中的readyState和status

1、readyState是什么意思:

readyState:是表示请求和响应过程中的状态(进行到了哪一步)。

0:未初始化,【XMLHttpRequest对象创建完毕】

1:初始化,【调用open完毕】

2:后端接收到请求,

3:后端正在处理

4:后端响应回来。

2、status是什么意思:

status:表示响应结果的状态描述,该状态一般都是三位的数字,常见的取值为:

200:表明该请求被成功地完成,所请求的资源发送到客户端。

301:本网页被永久性转移到另一个URL

404:可连接服务器,但服务器无法取得所请求的网页,请求资源不存在。

500:务器错误。

23、promise的理解

1、Promise的概念

Promise是ES6提供的原生的类(构造函数), 用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作,如:ajax请求,定时器)

2、Promise的两个特点:

1)、对象的状态不受外界影响。

Promise 有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。

2)、一旦状态改变,就不会再变

状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。

3、Promise的作用

解决回调地狱的问题。

4、Promise的方法
1)、对象方法then

功能:把then方法的参数传给resolve和reject。 promise对象.then(resolve回调函数,reject回调函数);

当promise里的异步操作完毕后,会调用then方法的参数。

参数:

then方法的第一个参数是resolve

then方法的第二个参数是reject。

返回值:promise对象,所以,then调用完毕后,还可以继续调用then(即:链式调用)

扩展:

fn01().then(fn02).then(fn03):
​
1、看看第二个then。
​
•      如果fn02没有返回promise对象,那么第二个then就使用的是第一个then的promise。
​
•      如果fn02返回了promise对象,那么第二个then就使用的fn02返回的promise对象。

2)、对象的方法catch:

它和then的第二个参数一样,用来指定reject的回调,

3)、类的方法all

功能: Promise.all() 可以并行执行多个异步操作(并发),并且在一个回调中处理所有的返回数据。返回的数据与传的参数数组的顺序是一样的。当所有的异步操作都成功才表示成功

参数:数组。数组里是若干个返回promise对象的函数(异步操作);

返回值:promise对象。promise对象的then方法的回调函数的参数是 所有promise对象的resolve的参数(数组形式)。

4)、类的方法race

功能:也是并发,但是,与all不同之处时,当一个异步操作完成(resolve或reject)时,就调用方法了。即:多个异步操作,同时执行,谁快就用谁的结果,所以,结果不再是数组。

24、async和await

ES7新增的两个关键字(async和await),是彻底解决回调地狱。是promise的语法糖。把异步的回调变成了同步的写法。

async:异步

修饰函数后,函数的返回值会变成Promise对象。原函数的返回值会成为resolve的参数。async修饰函数后,表示函数里面有异步操作。

await:等待

1、await必须放在async修饰的函数里

2、await是修饰proimse对象的。await修饰后,会把promise对象的resolve的实参作为返回值。

3、await 修饰代码后,处于await所在行后面(而不是await右边)的代码会等到异步结束后执行。但不影响主线程的代码执行。

4、如果要拿到reject里参数,就使用try catch。

25、cookie、localstorage和sessionstorage 区别

相同点:

cookie,localStorage,sessionStorage都是在客户端保存数据的,存储数据的类型:都是字符串。

不同点:

1、生命周期:

1)、cookie如果不设置有效期,那么就是临时存储(存储在内存中),是会话级别的,会话结束后,cookie也就失效了,如果设置了有效期,那么cookie存储在硬盘里,有效期到了,就自动消失了。

2)、localStorage的生命周期是永久的,关闭页面或浏览器之后localStorage中的数据也不会消失。localStorage除非主动删除数据,否则数据永远不会消失。

3)、sessionStorage仅在当前会话下有效。sessionStorage引入了一个“浏览器窗口”的概念,sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面,sessionStorage也是不一样的。

可以简单的理解为:sessionStorage,没有设置有效期的cookie。

 如果说把cookie的有效期设置为永远永远,永久,那么就是localStorage。
​
cookie没有设置有效期,那么就是sessionStorage

2、大小限制:cookie大小限制在4KB,非常小;localstorage和sessionStorage在5M

3、网络流量:cookie的数据每次都会发给服务器端,而localstorage和sessionStorage不会与服务器端通信,纯粹为了保存数据,所以,webstorage更加节约网络流量。

4、安全性:WebStorage不会随着HTTP header发送到服务器端,所以安全性相对于cookie来说比较高一些,不会担心截获。

5、使用方便性上:WebStorage提供了一些方法,数据操作比cookie方便;

setItem (key, value) ——  保存数据,以键值对的方式储存信息。
​
getItem (key) ——  获取数据,将键值传入,即可获取到对应的value值。
​
removeItem (key) ——  删除单个数据,根据键值移除对应的信息。
​
clear () ——  删除所有的数据
​
key (index) —— 获取某个索引的key

26、http和https的区别;两者的优缺点

  1. https的默认端口是443,而http的默认端口是80,且两者的连接方式不同;

  2. http传输是明文的,而https是用ssl进行加密的,https的安全性更高;

  3. https是需要申请证书的,而http不需要。

27、同源策略,如何解决跨域

同源策略: 是浏览器的一种安全机制,限制JavaScript或Cookie只能访问同域名下的内容,否则,会出现跨域问题。

同源的定义: 同样的协议,同样的地址,同样的端口

解决跨域的办法:

1)、原生中有jsonp

2)、脚手架项目(webpack和vite)中有反向代理

3)、后端用cors方式,就没有前端啥事了。

扩展:

1、对内存泄漏的了解

1)、理解 定义:程序中已在堆中分配的内存,因为某种原因未释放或者无法释放的问题 简单理解: 无用的内存还在占用,得不到释放和归还,比较严重的时候,无用的内存还会增加,从而导致整个系统卡顿,甚至崩溃。

2)、内存的生命周期

1.分配期 ​ 分配所需要的内存,在js中,是自动分配的 ​ 2.使用期 使用分配的内存,就是读写变量或者对象的属性值 ​ 3.释放期 不需要时将该内存释放,js会自动释放(除了闭包和一些bug以外) 内存泄漏就是出现在这个时期,内存没有被释放导致的

3)、可能出现内存泄漏的原因

1.意外的全局变量 ​ 2.DOM元素清空时,还存在引用 ​ 3.闭包 ​ 4.遗忘的定时器

4)、如何优化内存泄漏?

全局变量先声明在使用 ​ 避免过多使用闭包。 ​ 注意清除定时器和事件监听器。

2、0.1 + 0.2 为什么不等于 0.3, 在项目中遇到要怎么处理 ?

1)、为什么?

计算机内部存储数据使用2进制存储,两个数字进行的数学运算,首先是将这两个数字以2进制形式,存储在计算机内部,然后在计算机内部使用两个2进制数字进行计算,最后将计算结果的2进制数字转为10进制展示出来。

由于10进制的小数在转2进制的时候,规则是小数部分乘以2,判断是否得到一个整数,如果得到整数,转换完成;如果没有得到整数,则继续乘以2判断。所以,0.1和0.2在转换2进制的时候,其实是一个无限死循环,也就是一直乘以2没有得到整数的时候,但计算机内部对于无线死循环的数据,会根据一个标准保留52位。也就是说,计算机内部在存储0.1和0.2的时候,本来就不精准,两个不精准的小数在计算后,距离精准的结果是有一定误差的。

2)、项目中碰到这种情况,有3种处理方法:
第一种:

将小数乘以10的倍数,转为整数,然后计算,计算完成后,再缩小10的倍数,例如:

  var result = ((0.1 * 10) + (0.2 * 10)) / 10
  // result === 0.3

第二种:

使用数字的toFixed方法,强制保留小数点后多少位,例:

  var result = (0.1 + 0.2).toFixed(2)
  // result === 0.30

第三种:

自定义数字运算方法,当需要进行数学运算的时候,不直接进行,调用自定义的方法进行,例:(加法封装)

function add(...args){
    var num = args.find(item => {
        if(item != 0 && !item){
            throw new Error("数学运算要使用数字")
        }
    })
    var arr = args.map(item => {
        var index = (item+'').indexOf('.')
        if(index >= 0){
            return (item+'').split('.')[1].length
        }
    })
    arr = arr.filter(item => item)
    if(arr.length){
        var max = Math.max(...arr)
        var data = args.map(item => item * Math.pow(10, max))
        var data.reduce((a, b) => a + b) / Math.pow(10, max)
    }else{
        var data = args
        return data.reduce((a, b) => a + b)
    }
}
// 调用使用:
var num1 = add(0.1, 0.2)
console.log(num1); // 0.3
​
var num2 = add(1, 2)
console.log(num2); // 3
​
var num3 = add(1, 2.1)
console.log(num3); // 3.1
​
​

3、JS 如何实现多线程

我们都知道JS是一种单线程语言,即使是一些异步的事件也是在JS的主线程上运行的。如:setTimeout、ajax的异步请求,或者是dom元素的一些事件,都是在JS主线程执行的,这些操作并没有在浏览器中开辟新的线程去执行,而是当这些异步操作被操作时或者是被触发时才进入事件队列,然后在JS主线程中开始运行。

首先说一下浏览器的线程,浏览器中主要的线程包括,UI渲染线程,JS主线程,GUI事件触发线程,http请求线程。

JS作为脚本语言,它的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。(这里这些问题我们不做研究)

但是单线程的语言,有一个很致命的确定。如果说一个脚本语言在执行时,其中某一块的功能在执行时耗费了大量的时间,那么就会造成阻塞。这样的项目,用户体验是非常差的,所以这种现象在项目的开发过程中是不允许存在的。

其实JS为我们提供了一个Worker的类,它的作用就是为了解决这种阻塞的现象。当我们使用这个类的时候,它就会向浏览器申请一个新的线程。这个线程就用来单独执行一个js文件。

var worker = new Worker(js文件路径);
那么这个语句就会申请一个线程用来执行这个js文件。这样也就实现了js的多线程。

4、JavaScript中的全局函数?

  1. escape()函数,对字符串进行编码;

  2. isNaN()函数,检查一个参数是否是非数字值;

  3. Number()函数,把对象的值转换为数字;

  4. String()函数,把对象的值转换为字符串;

  5. encodeURI()函数,把字符串作为URI进行编码;

  6. decodeURI()函数,解码某个编码的URL;

  7. decodeURIComponent()函数,解码一个编码的URI组件;

  8. encodeURIComponent()函数,把字符串编码为URI组件;

  9. eval()函数,计算字符串,并执行其代码;

  10. isFinite()函数,检查参数是否是无穷大;

  11. parseFloat()函数,解析字符串,返回浮点数;

  12. parseInt()函数,解析字符串,返回整数;

  13. unescape()函数,对escape()编码的字符串进行解码

5、重绘和回流(重排)

页面的重排和回流(提升移动端网页性能)_前端如何让主侧页面从移动端的重叠到网页版的并排-CSDN博客

一、重绘重排的概念: 重绘的意思是:重新绘制,如:颜色发生变化, 重排(回流)的意思是:重新排列,即布局会受影响,如:用js动态改变了元素的宽或高,就会影响其它元素的位置,这叫重新排列。

二、什么时候引起重绘,什么时候引起重排: 1、重排(回流):回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流。比如以下情况:

页面一开始渲染的时候(这肯定避免不了) DOM树的结构变化:添加或删除可见的DOM元素 DOM元素的几何属性变化:如外边距、内边距、边框厚度、宽高、等几何属性) 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的) 窗口属性的获取和尺寸改变:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、 clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle() (currentStyle in IE) Display:的变化。 2、重绘

·DOM元素的字体颜色、改变visibility、outline、背景色。重绘不会带来DOM元素的重新计算,所以重绘并不一定伴随重排(回流),但是重排一定会引起浏览器的重绘

三、问题: 重绘,重排太过频繁,就会影响网页的执行性能。亲,你想想:你的页面本来一次可以重排好,你得多重排一次,那肯定是影响性能的。重绘一次就可以,你非得多重绘继承,那肯定也是影响性能的。

跟生活中一样。

重绘:你不停地擦掉重画,擦掉重画,肯定不如一次画到位好

重排:你总是重复性的把某个物体不停地挪动。跟家里的家具要换位置的道理一样。你如果想不来,今晚回去思考一下:假定你要把家里的家具重新布局(从A布局变成B布局),你在家具搬动的过程中,可以出几套搬动方案。看看哪一种搬动方案是挪动家具次数最少的,那么这种方案就是重排的最优方案。

四、浏览器的性能优化方案 浏览器也是很聪明的,由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列。但是!当你获取布局信息的操作的时候,会强制队列刷新,比如当你访问以下属性或者使用以下方法:

offsetTop、offsetLeft、offsetWidth、offsetHeight

scrollTop、scrollLeft、scrollWidth、scrollHeight

clientTop、clientLeft、clientWidth、clientHeight

getComputedStyle()

getBoundingClientRect()

以上属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发回流重绘来返回正确的值。这也是为什么在获取这些值时会引起重排(回流)、重绘的原因,因此,我们在修改样式的时候,最好避免使用上面列出的属性,他们都会刷新渲染队列。如果要使用它们,最好将值先保存起来。

五、如何解决:

1、分离读和写的操作(避免触发同步布局事件):

let ulDom = document.getElementById(“box”);

var curLeft=ulDom .offsetLeft;

var curTop=ulDom .offsetTop;

ulDom .style.left=curLeft+1+'px';

ulDom .style.top=curTop+1+'px';

而不要这样写:

let ulDom = document.getElementById(“box”);

var curLeft=ulDom .offsetLeft;

ulDom .style.left=curLeft+1+'px';

var curTop=ulDom .offsetTop;

ulDom .style.top=curTop+1+'px';

因为,获取offsetLeft和offsetTop会导致浏览器强制清空队列,进行强制同步布局。

2、样式集中改变,而不要一个一个的改变(减少重绘重排)

如:let ulDom = document.getElementById(“box”);

   ulDom.style.left = “100px”;
​
   ulDom.style.top = “200px”;

最好改成如下(cssText):

let ulDom = document.getElementById(“box”);
​
ulDom.style.cssText = ‘left :100px;top:200px;’
​
或者使用class名也可以

ulDom.className = “boxcls”;

3、批量修改DOM

1)、如果可以的话,在重排时,可以使用absolute脱离文档流,这样,元素的尺寸改变时,就不会影响其它元素了。先让元素absolute。然后给元素中放入若干个dom,再把元素的absolute去掉;

如果可以的话,使用display:none(脱离文档流),等元素的样式属性改变完毕后,再让元素显示。

2)、使用createDocumentFragment或拼接html字符串的方式进行DOM的增加

http的常见状态码

http的状态码一般都是三位的数组。

1、1开头的,该类型状态码表示接收到请求并且继续处理

100:客户端必须继续发出请求。 ​ 101:客户端要求服务器根据请求转换HTTP协议版本。

2、2开头的,该类型状态码表示动作被成功接收、理解和接受。

200,表明该请求被成功地完成,所请求的资源发送到客户端。 ​ 201,提示知道新文件的URL。 ​ 202,接受并处理,但处理未完成。 ​ 203,返回信息不确定或不完整。 ​ 204,收到请求,但返回信息为空. ​ 205,服务器完成了请求,用户必须复位当前已经浏览过的文件。 ​ 206,服务器已经完成了部分用户的GET请求。

3、3开头的,该类型状态码表示为了完成指定的动作,必须接受进一步处理。

300,请求的资源可在多处获得。 ​ 301,本网页被永久性转移到另一个URL。 ​ 302,请求的网页被重定向到新的地址。 ​ 303,建议用户访问其他URL或访问方式。 ​ 304,自从上次请求后,请求的网页未修改过。 ​ 305,请求的资源必须从服务器指定的地址获得。 ​ 306,前一版本HTTP中使用的代码,现已不再使用。 ​ 307,声明请求的资源临时性删除。

4、4开头的,该类型状态码表示请求包含错误语法或不能正确执行。

400,客户端请求有语法错误(如:请求方式不对,或者请求方式对应的参数不对) ​ 401,请求未经授权。 ​ 402,保留有效ChargeTo头响应。 ​ 403,禁止访问,服务器收到请求,但拒绝提供服务。 ​ 404,可连接服务器,但服务器无法取得所请求的网页,请求资源不存在。 ​ 405,用户在Request-Line字段定义的方法不被允许。 ​ 406,根据用户发送的Accept,请求资源不可访问。 ​ 407,类似401,用户必须首先在代理服务器上取得授权。 ​ 408,客户端没有在用户指定的时间内完成请求。 ​ 409,对当前资源状态,请求不能完成。 ​ 410,服务器上不再有此资源。 ​ 411,服务器拒绝用户定义的Content-Length属性请求。 ​ 412,一个或多个请求头字段在当前请求中错误。 ​ 413,请求的资源大于服务器允许的大小。 ​ 414,请求的资源URL长于服务器允许的长度。 ​ 415,请求资源不支持请求项目格式。 ​ 416,请求中包含Range请求头字段,在当前请求资源范围内没有range指示值。 ​ 417,服务器不满足请求Expect头字段指定的期望值。

5、5开头的,该类型状态码表示服务器或网关错误。

500,服务器错误。 ​ 501,服务器不支持请求的功能。 ​ 502,网关错误。 ​ 503,无法获得服务。 ​ 504,网关超时。 ​ 505,不支持的http版本。

你可能感兴趣的:(javascript,开发语言,ecmascript)