1. 解释下什么是变量声明提升?
JS引擎在运行一份代码的时候,首先,对代码进行预编译,将所有var声明的变量提升到所在作用域的顶部,赋值留在原地。
而let和const声明的变量不存在声明提升。
2. JS 的参数是以什么方式进行传递的?
基本类型:是值传递!
像对象数组,传递的是地址。
3.谈谈你对 JavaScript垃圾回收的理解?
js的垃圾回收机制指的是JS中对内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。
JS环境中分配的内存, 一般有如下生命周期:
内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
内存使用:即读写内存,也就是使用变量、函数等
内存回收:使用完毕,由垃圾回收自动回收不再使用的内存
全局变量一般不会自动回收, 当关闭浏览器的时候会回收。一般局部变量的的值, 不用了, 会被自动回收掉
垃圾回收算法主要有两种:一种是引用算法,只要不再使用就会回收,但如果两个对象相互引用,会造成内存泄漏。还有一种是标记算法,从根部不能到达的对象,就标记不再使用,就会被回收。
4.谈谈你对 JavaScript 作用域链的理解?
JavaScript 在执⾏过程中会创建一个个的可执⾏上下⽂。在全局作用域有全局的可执行上下文, 在每个函数内部,有着函数的可执行上下文。
当在一个函数内部还有函数,存在多层函数嵌套时,若是执行最内层函数的上下文,首先会在当前作用域找是否有要可使用的变量,如果没有就会到上一级找,若是还没有就继续往外层找,直到找到全局作用域,就形成了作用域链。
5、谈谈你对闭包的理解
MDN的官方解释:
闭包是函数和声明该函数的词法环境的组合
更通俗一点的解释是:
内层函数, 引用外层函数上的变量, 就可以形成闭包
闭包的主要作用是什么?
在实际开发中,闭包最大的作用就是用来 变量私有。
6、谈谈你对原型链的理解?
要讲清楚这个问题,主要着重这几个方面:
什么是原型对象
构造函数, 原型对象, 实例的三角关系图
原型链如何形成
构造函数是创建对象的一种方式,构造函数具有一个特性的属性 prototype
指的就是原型对象,通过构造函数名.prototype.属性就可以为为原型对象注入公共属性。
基于同一个构造函数创建出来的实例对象, 都可以共享访问原型对象的属性。
原型链
一般情况下,实例对象访问属性或方法时,优先访问构造函数内部的属性或方法,如果没找到就会到原型对象上找,通过实例对象名.proto.属性或方法名,如果还没找到,就去继续往原型的原型中查找,直到找到根原型Object.prototype,这一层层的原型对象关系构成了原型链。
7、如何判断是否是数组?
Object.prototype.toString()
Array.isArray(arr)
8.谈谈你对this的理解?
有三种情况可以改变this指向,分别是call,apply,bind
call方法传递参数适用逗号分开,apply传递参数是放在一个数组中,bind改变函数this指向的同时会创建一个新的函数
箭头函数中没有属于自己的this,箭头函数中的this指向外层函数的this
9、Promise 的静态方法
promise的三个状态: pending(默认) fulfilled(成功) rejected(失败)
resolve函数被执行时, 会将promise的状态从 pending 改成 fulfilled 成功
reject函数被执行时, 会将promise的状态从pending 改成 rejected 失败
Promise.all([promise1, promise2, promise3])** 等待原则, 是在所有promise都完成后执行, 可以用于处理一些并发的任务
Promise.race([promise1, promise2, promise3]) 赛跑, 竞速原则, 只要三个promise中有一个满足条件, 就会执行.then(用的较少)
10、宏任务 微任务 是什么
宏任务: 主线程代码属于一个宏任务, 一个定时器setTimeout 属于一个宏任务, 上一个宏任务执行完, 才会考虑执行下一个宏任务。
微任务: promise .then .catch的需要执行的内容, 属于微任务, 满足条件的微任务, 会被添加到当前宏任务的最后去执行。
事件循环队列:js是单线程的,js执行代码是从上往下一层一层执行,当遇到异步函数时会交给浏览器,浏览器是多线程的,可以同时执行多个事件,只有主线程执行完,才会按顺序执行异步任务。
11、 相较于 Promise,async/await有何优势?
async放在一个异步函数前,将异步函数转为promise对象,变为跟随主线程同步执行。
await是放在promise函数调用前,阻塞主线程,强制async函数中其他代码等待,直到 Promise 完成并返回结果 再执行后续代码。
优势:让代码更规范,提高阅读性,方便程序员阅读和维护,
12、深拷贝 浅拷贝
引用类型数据像对象或数组, 进行赋值时, 赋值的是地址,两个对象会指向同一个地址,不属于浅拷贝和深拷贝。
浅拷贝:会指向内存中同一个地址,修改一个对象数据,另外一个对象数据也会被修改
深拷贝:会克隆出一个完整的对象,需要利用递归来实现,比较麻烦
13、undefined和null的区别
Undefined类型只有一个值,即undefined。当声明的变量没有赋值时,变量的默认值为undefined。
null类型也只有一个值,即null。null用来表示一个不存在的对象。
14、js怎么实现多线程
js是单线程语言,这是因为js的主要功能是实现用户与浏览器的交互,以及操作dom。这决定了它只能是单线程,否则会带来很复杂的同步问题。
但如果实现多线程可以通过webworker来实现,在主程序中创建worker对象,并传入work子文件,两者就可以进行数据传递。
15、栈内存和堆内存
对于原始类型,数据结构简单,是将数据之间存在栈内存中,而对象数组引用类型,将数据存在了堆内存中,同时会生成一个地址,然后将地址保存到了栈内存的变量中。
16、数组常用的api
Join将数组转为字符串 slice截取数组,splice删除数组,push,unshift shift,reverse sort
17、class面向对象的三大特征
封装、继承、多态
18、promise.all
该方法用于将多个Promise实例,包装成一个新的Promise实例。
**只有b1、b2、b3都是fulfilled(成功)时,b才是fulfilled(成功),只要有一个失败,最终都是显示失败的数据
另外一种方法:先声明一个变量,初始值是1,假如说要发四个网络请求,让请求每成功就加1,最后判断是不是四就行了。
19、判断数据类型的方法
typeOf 不能判断数组
instanceof 判断引用类型数据,包括构造函数 底层是判断左右原型是否是同一个
Object.prototype.toString()
20、箭头函数和普通函数的区别
参数上的区别
普通函数的参数是arguments,而箭头函数的的是args
this指向的不同
箭头函数的this指向上层函数作用域的this对象
原型和构造函数的问题
箭头函数不能使用new生成实例,因为箭头函数没有prototype
21、数组去重的方法
new Set来进行去重,然后利用Array.from 来让变成标准化的数组。。。
22、es6模块化规范
1. const 和 let
2、解构赋值
3、模板字符串
4、数组对象展开运算符
5、promise async/aswait
6、class继承
7、模块化规范
8、数组新用API