你一定要掌握的JS理论题!!

500 阅读6分钟

前言

总结的是一些比较偏理论的知识,有一些会比较难以理解,整理的时候会按着我自己的理解说的会比较容易理解那么一丢丢,如果有不对的地方,请指出

你知道的越多,你不知道的越多 点赞再看,手留余香,与有荣焉

1.JavaScript 的数据类型有哪些

最新的 ECMAScript 标准定义了 8 种数据类型:

原始数据类型:

1.undefined 2.Boolean 3.Number 4.String 5.BigInt 6.Symbol 7.null

8.Object:任何 constructed 对象实例的特殊非数据结构类型,也用做数据结构:new Object,new Array,new Map,new Set,new WeakMap,new WeakSet,new Date,和几乎所有通过 new keyword 创建的东西。

2.this的指向

想要理解this指向可以记住两点:

1:this永远指向一个对象

2:this的指向完全取决于函数调用的位置 大致如下几种情况:

  • 在全局中this指向的是Windows
  • 在普通函数中this指向的是Windows
  • 在定时器中this指向的是Windows
  • 在对象中this指的是当前对象
  • 在构造函数中this指向的是构造函数所创建出来的实例对象
  • 在事件处理程序中,this指的是事件源

3.什么是作用域链?

简单来说:采取就近原则的方式来查找变量最终的值

只要是代码都是一个作用域,写在函数内部的是局部作用域,未写在任何函数内部是全局作用域;如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域;根据在内部函数可以访问外部函数变量的这种机制就称作作用域链

4.Eventloop事件循环机制

说到事件循环就要说一下,JS的一大特点就是单线程,单线程就是同一个时间只能做一件事情,这就意味着,所有任务都需要排队,只有前一个任务结束,才会执行后一个任务,如果前一个任务耗时很长,后一个任务就不得不一直等着,这样就会导致程序假死,卡在那了,为了解决这个问题,JS中出现了同步任务和异步任务

同步任务指的是:

  • 在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;

异步任务指的是:

  • 不进入主线程、而进入任务队列的任务,当主线程中的任务运行完了,才会从任务队列取出异步任务放入主线程执行。

在异步任务中又分为宏任务与微任务:

宏任务指的是:

  • 异步 Ajax 请求,setTimeout、setInterval等

微任务指的是:

  • Promise.then、.catch等

优先检查微任务队列是否有任务执行,如果没有,再去执行宏任务,如果有,执行所有待执行的微任务,完毕之后再执行宏任务,再继续执行下一个微任务

JavaScript 主线程从任务队列中读取异步任务的回调函数,放到执行栈中依次执行。这个过程是循环不断的,所以整个的这种运行机制又称为 EventLoop(事件循环)

5. 什么是原型链?

原型链的作用是用来继承,查找对象属性值与变量的值的一种机制

每一个实例对象都有一个proto属性,指向构造函数的原型对象prototype,构造函数的原型对象也是一个对象,也有proto属性,这样一层一层的往上找,就形成了一条原型链

6.你对原型(prototype)理解

原型的主要作用就是为了实现继承与扩展对象

JavaScript中所有都是对象,原型也是一个对象,通过原型可以实现对象的属性继承,JS的函数对象中都包含了一个prototype内部属性,这个属性所对应的就是该函数对象的原型   

prototype作为函数对象的内部属性,是不能被直接访问的。所以浏览器JavaScript 引擎中提供了--proto--这个访问器来查看原型

7.什么是闭包?

简单理解就是 :

  • 内层函数访问外层函数的变量,就可以形成闭包。
  • 我理解的闭包在开发中的作用是:封闭数据,提供操作。
  • 闭包就是一个概念,闭包在我们的代码无处不在
  • 闭包作用:变量私有化

优点:

  • 包含函数内变量的安全,实现封装,防止变量流入其他环境发生命名冲突,造成环境污染。

  • 在适当的时候,可以在内存中维护变量并缓存,提高执行效率。

缺点:

消耗内存: 通常来说,函数的活动对象会随着上下文环境一起被销毁,但是由于闭包引用的是外部函数的活动对象,因此这个活动对象无法被销毁,因为闭包比一般函数消耗更多内存。

出现内存泄漏怎么去定位

(1)排查内存泄漏的第一步,就是要先梳理一遍自己的代码,看一下哪部分内存的升高是合理的,哪部分内存的升高是不合理的。

(2)先用Performance。 当我们怀疑页面发生了内存泄漏的时候,可以先用Performance录制一段时间内页面的性能变化。如果录制结束后,看到内存的下限在不断升高的话,你就要注意了 —— 这里有可能发生了内存泄漏。

(3) 当你怀疑发生了内存泄漏的时候,你就可以用Memory面板来进一步定位泄漏的源头了。

7.1常见的内存泄漏

1.意外的全局变量

2.被遗忘的计时器或回调函数

3.使用不当的闭包

(函数本身会持有它定义时所在的词法环境的引用,但通常情况下,使用完函数后,该函数所申请的内存都会被回收了

但当函数内再返回一个函数时,由于返回的函数持有外部函数的词法环境,而返回的函数又被其他生命周期东西所持有,导致外部函数虽然执行完了,但内存却无法被回收

所以,返回的函数,它的生命周期应尽量不宜过长,方便该闭包能够及时被回收

正常来说,闭包并不是内存泄漏,因为这种持有外部函数词法环境本就是闭包的特性,就是为了让这块内存不被回收,因为可能在未来还需要用到,但这无疑会造成内存的消耗,所以,不宜烂用就是了

 

4.全局事件监听

5.遗漏的 DOM 元素

(DOM 元素的生命周期正常是取决于是否挂载在 DOM 树上,当从 DOM 树上移除时,也就可以被销毁回收了

但如果某个 DOM 元素,在 js 中也持有它的引用时,那么它的生命周期就由 js 和是否在 DOM 树上两者决定了,记得移除时,两个地方都需要去清理才能正常回收它)

8.防抖与节流?

防抖与节流都是防止某一时间事件高频的触发

防抖就是:

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

节流就是:

  • 可以减少一段时间内事件的触发频率
  • 可以理解为:把一直连续放水的水龙头关小,直到让水是一点一点向下滴(函数还是可以被调用多次,只不过是频率变慢了)

9.class 和普通构造函数有何区别?

写法不同,运行结果是一致的,class 是构造函数的语法糖

1.class 在语法上更加贴合面向对象的写法

2.class 实现继承更加易读、易理解

3.更易于写 java 等后端语言的使用

10.ES6 的继承和 ES5 的继承有什么区别

ES5的继承是通过原型链,或借用构造函数的方式来实现继承的,

而ES6的继承是通过class定义类,实例属性写在constructor中,子类通过extends关键字继承父类,在constructor中调用super方法触发调用父构造函数,进行实例的属性初始化,注意super方法要写在子类自己的属性声明之前,否则会报错。

11.var、let、const 之间的区别

  • 使用 var 声明的变量,存在变量提升现象,可以重复声明
  • 使用 let 声明的变量,具有块级作用域,只在所处的块级有效,具有暂时性死区,不存在变量提升
  • 使用 const 声明的是常量,不能重新进行赋值,基本数据类型不能更改值,复杂数据类型不能更改地址,具有块级作用域,不存在变量提升

12.谈谈你对 Javascript 垃圾回收机制的理解?

1.垃圾回收机制所存在的意义

在不需要字符串、对象的时候,需要释放其所占用的内存,否则将会消耗完系统中所有可用的内存,造成系统崩溃

2.内存泄漏

由于疏忽或错误造成程序未能释放那些已经不再使用的内存,造成内存的浪费

3. 垃圾收集机制的原理

垃圾收集器会按照固定的时间间隔,周期性的找出不再继续使用的变量,然后释放其占用的内存

不再使用的变量也就是生命周期结束的变量,是局部变量,局部变量只在函数的执行过程中存在,当函数运行结束,没有其他引用(闭包),那么该变量会被标记回收。

全局变量的生命周期直至浏览器卸载页面才会结束,也就是说全局变量不会被当成垃圾回收。

13.setTimeout、Promise、Async/Await 的区别?

  • setTimeout属于宏任务,等到执行栈清空以后执行
  • Promise本身是同步的,但在执行resolve或者rejects时是异步的,即then方法是异步的,.then方法属于微任务,
  • Async/Await中await语法后面紧跟的表达式是同步的,但接下来的代码是异步的,属于微任务。 async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。

await的含义为等待,也就是 async 函数需要等待await后的函数执行完成并且有了返回结果(Promise对象)之后,才能继续执行下面的代码。await通过返回一个Promise对象来实现同步的效果

14.如何获取多个 Promise 最后整体结果?

可以使用 Promise.all() 方法

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例

Promise.all()接受一个数组作为参数,数组里的元素都是 Promise 对象的实例,如果不是,就会先调用 Promise.resolve(),将参数转为 Promise 实例,再进一步处理。Promise.resolve()方法可以将现有对象转为Promise对象

15.Promise 中 reject 和 catch 处理上有什么区别

首页我们先要区分几个概念,

  • 第一:reject是用来抛出异常的,catch是用来处理异常的;
  • 第二:reject是Promise的方法,而then和catch是Promise实例的方法。
  • 第三:reject 后的东西,一定会进入 then 中的第二个回调,如果 then 中没有写第二个回调,则进入catch网络异常(比如断网),会直接进入 catch 而不会进入 then 的第二个回调

16.call、apply与bind 的区别?

相同点:

  • 都可以改变函数内部this指向 不同点:
  • call 和 apply 会调用函数, 并且改变函数内部this指向.
  • call 和 apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递
  • bind 不会调用函数, 可以改变函数内部this指向

应用场景

  1. call 经常做继承.
  2. apply经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
  3. bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向

17.for in 和 for of 的区别?

1.先看图:对象

情况一:

image.png

情况二:

image.png 结论:

  • for in 遍历对象输出的是key值
  • for of 遍历对象报错,要搭配 Object.keys() 获取对象的 key值集合后,再使用 for of

2.先看图:数组

情况一:

image.png

情况二:

image.png 结论:

  • for in 遍历数组返回的是下标值,不仅可以遍历数字键名,还会遍历原型上的值和手动添加的
  • for of 遍历数组正常输出值

3.总结

for in 循环特别适合遍历对象

for of 适合遍历数组,它可以与 break、continue和return 配合使用,也就是说 for of 循环可以随时退出循环。

18.移动端适配原理?

利用媒体查询或JS动态检测设备宽度,不同设备下动态设置根元素大小,根元素大小变化了,所有使用rem做单位的元素就一起变化了

19.css盒子居中的几种方式?

  • 定位
  • 利用margin:auto
  • 利用display:flex
  • css3的新增属性table-cell, vertical-align:middle
  • 利用transform的属性

20.重绘与回流的区别?

什么是回流(重排)?

比如我们增删DOM节点,修改一个元素的宽高,页面布局发生变化,DOM树结构发生变化,那么肯定要重新构建DOM树,而DOM树与渲染树是紧密相连的,DOM树构建完,渲染树也会随之对页面进行再次渲染

什么是重绘?

当你给一个元素更换颜色,这样的行为是不会影响页面布局的,DOM树不会变化,但颜色变了,渲染树得重新渲染页面

你应该能感觉到,回流的代价要远大于重绘。且回流必然会造成重绘,但重绘不一定会造 成回流。

区别

结合上面的解释,引起DOM树结构变化,页面布局变化的行为叫回流,且回流一定伴随重绘。 只是样式的变化,不会引起DOM树变化,页面布局变化的行为叫重绘,且重绘不一定会便随回流。

21.Set和Map数据结构?

Set:

Set类似于数组,但是成员的值都是唯一的,没有重复的值。

Map:

Map类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串--值”的对应,Map 结构提供了“值--值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

22.forEach和Map的区别?

  • forEach()方法不会返回执行结果且会修改原来的数组,而是undefined。
  • map()方法会得到一个新的数组并返回。

23.new关键字的执行过程

  • new 构造函数在内存中创建了一个空的对象
  • this 指向刚才创建的空对象
  • 执行构造函数里面的代码,给这个空对象添加属性和方法
  • 返回这个对象