### graph TD
Start --> Stop
1、数据类型
基本数据类型==》Number String null undefined boolean symbol(ES6),BigInt(ES2020)
引用数据类型==》Object,对象子类型(Array,function)
BigInt数据类型的目的是比Number数据类型支持的范围更大的整数值,在对大整数实行运算时,以任意精度表示整数的能力尤为重要,整数溢出将不是问题。
typeof和instanceof的区别?
typeof一般都用来判断一个变量类型,返回的是一个基本类型
instanceof返回的是一个布尔值,可以准确的判断复杂引用数据类型,但是不能正确的判断基础数据类型
instanceof判断一个变量是数组还是对象。
2、this指向问题;apply、call、bind?
1,普通函数调用时,this指向winow
2,当函数作为对象调用时,this就会指向该对象
3,构造函数调用时,this指向返回的这个对象
4,call、apply、bind方法显示调用时,this指向指定参数的对象上
5,箭头函数的this绑定看的是this所在函数定义在哪个对象下,就绑定哪个对象。如果有嵌套的情况下,则this绑定到最近的一层对象上。
apply、call、bind区别?
call()方法使用一个指定的this值和单独给出的一个或多个参数来调用一个函数。
apply()方法使用一个指定的this值和单独给出的一个或多个数组来调用一个函数。
bind()方法也改变this指向,但会返回一个新的函数,需要再次调用。
3、操作数组、对象的方法?
数组排序:sort();
var last=[1, 59, 6, 9, 2, 4];
last.sort((A,B)=>{A-B})升序
数组反转:reverse()
var list =["a", "b", "c", "d"];
list.revese();
合并数组:concat(a,b)
数组删除:slice不改变原数组 splice改变原数组
数组过滤 filter函数;要用return返回
数组转为字符串的方式:数组.join("拼接的字符") 强制转换:arr.toString();
数组第一次出现的位置:arr.indexof() 返回下标,不存在返回-1
Math.random()结果为0-1间的一个随机数
Math.round(num);参数num为一个数值,函数结果为num四舍五入的整数
Math.ceil(n)与 Math.floor() 向上取整和向下取整
一个数组随机去一个值:
var arr =【"被表白", "你爱的人也喜欢你","好运连连", "遍地桃花", "开心", "升职", "防dao", "加薪", "健康", "出名", "暴富", "瘦成闪电"】
var aa=Math.floor((Math.random()*arr.length));
console.log(arr[aa]);
操作数组的案例: (juejin.cn/post/685481…)
数组遍历的方式有哪几种?
forEach((item,index,array)=>{})三个参数 item每一项 index 下标 array原数组
map((item,index,array)=>{})三个参数 item每一项 index 下标 array原数组
map的速度大于forEach;
map返回一个新的数组,不会改变原来的数组,forEach不会返回新数组,会改变原数组
for...in 大多数用来遍历对象,但是也可以遍历数组
for...of 试用了遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合
区别:
for...in遍历的是索引,for...of遍历的是value值
for...in总是得到对象的key或者数组、字符串的下标
for...of得到的是对象的value值或者数组、字符串的值
filter过滤,传参与forEach一致,用于过滤不合格的元素如果回调数是true,则被保留下来
some传参和forEach一致;用于表示查找,只要数组里面元素符合条件则返回的true;否则返回false
errey与some类似,some是数组里面某个元素符合条件返回true,errey是数组里面的所有元素符合条件返回true
遍历数组方法: (juejin.cn/post/697070…)
操作对象的方法?
var arr={ firstname:"John", lastname:"Doe", age:50, }
返回对象的key值组成数组:Object.keys(arr)//['firstname', 'lastname', 'age']
返回对象的value值组成数组:Object.values(arr)//['John', 'Doe', 50]
源对象复制到目标对象中:Object.assign(arr,obj1,obj2) arr目标对象,obj1源对象可以为多个对象。
判断对象中是否存在属性:arr.hasOwnProperty('lastname')
Object.getPrototypeOf() 获取原型对象的标准方法
Object.setPrototypeOf(a,b) 接收两个参数一个是现有对象,一个是原型对象
遍历对象的方法?
对象通过转换为数组遍历:
for...in 大多数用来遍历对象,但是也可以遍历数组
for...of 试用了遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合
Object.getOwnPropertyNames(obj) 返回一个数组,包含对象自身的所有属性(不包含Symbol属性)
Object.getOwnPropertySymbols(obj) 返回一个数组,包含自身所有的属性(包含Symbol属性)
4、深拷贝与浅拷贝的区别?
浅拷贝:创建一个新对象,这个对象有着原始对象的属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象;
案例地址(juejin.cn/post/684490…)
5、什么是原型,什么是原型链?
原型关系:每一个class都显示原型prototype
每一个实例都有隐式原型_proto_
实例的_proto_指向对应的class的prototype
原型: 在js中,每当定义一个对象(函数也是一个对象)时,对象中都包含一些预定义的属性。其中每个
函数对象都有一个prototype属性,这个属性指向函数的原型对象。
原型链:函数的原型链对象constructor默认指向函数本身,原型对象除了有原型属性外,为了实现继承,
还有一个原型链指针_proto_,该指针是指向上一层的原型对象,而上一层的原型对象的结构依然类似。因
此可以用_proto_一直指向Object的原型对象上,而Object上的原型对象用Obkject._proto_=null表示
原型链顶端。如此形成了js的原型链继承。同时所有的js对象都有Object的基本防范。
特点:js对象都是通过引用来传递的,我们创建每一个新的实体中并没又一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一属性
一个对象查找属性和方法时会从自身查找,如果查找不到则会通过_proto_指向实例化的构造函数的prototype
隐式原型也是一个对象,是指向我们构造函数的原型
除了最顶层的Object对象没有_proto_,其他所有的对象都有_proto_,这是隐式原型
隐式原型_proto_的作用是让对象通过他一直往上查找属性或方法,直到顶层的Object的_proto_属性,它的值是null,这个查找的过程是原型链
构造函数与普通函数的区别?
1,构造函数的this指向实例本身
2,普通函数的this指向调用者
3,构造函数需使用new关键字来创建实例对象,普通函数不需要
4,构造函数首字母要大写
6、new操作符做了什么事情?
1、创建了一个新的对象
2、设置原型,将对象的原型设置为函数的prototype对象
3、让函数的this指向这个对象,为函数添加新的属性
4,返回类型对象,如果是值类型,返回的创建的对象。如果是引用类型,返回的是引用类型的对象
7、什么是闭包,闭包的应用场景?
闭包的条件:函数嵌套函数,内部函数使用外部函数变量时,调用外部函数生成闭包
闭包的优点:变量长期驻扎在内村中,可以重复使用变量,并且不会造成变量污染
可以用来定义私有属性和私有方法。
闭包的缺点:常驻内存,会增加内存的使用量,使用不当会造成内存泄漏
可以改变父函数内部变量的值
闭包的使用场景:1,setTimeout
2,回调函数
3,函数防抖
4,封装私有变量
8、箭头函数与普通函数的区别?
箭头函数:箭头函数相当于匿名函数,并简化了函数定义,不能换行。
箭头函数不能作为构造函数,不能使用new,没有原型属性
箭头不能绑定arguments,取而代之用rest参数解决
箭头函数不绑定this,会捕获其所在的上下文
普通函数this指向调用他的对象。
9、ES6新增语法?
新增变量声明:let/const
区别:
1,var定义的变量没有块的概念可以跨块访问,不能跨函数访问
let定义的变量只能在块作用域访问,不能跨函数访问
const用来定义常量,用时必须初始化,只能在块作用域里访问,且不能修改
2,var可以先使用后声明,因为存在变量提升;let必须先声明后提升
3,var是允许在相同的作用域内重复声明同一个变量,而let与const不允许这一现象
4,在全局上下文中,及于let声明的全局变量和全局对象window没有任何关系;
var声明的变量会和window映射有关系
5,会产生暂时性死区
暂时性死区是浏览器的bug,暂时区本质就是只要进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声名变量的那一行出现,才可以获取和使用该变量
const:声明变量的时候必须赋值,否则会报错;使用const声明的变量被修改了也会报错
声明的变量不能改变,如果声明的是一个引用类型的话,则不能改变的他的内存地址(深拷贝浅拷贝
)
建议:一般声明变量都使用let关键字,而当声明一些配置项(接口地址、npm依赖包、分页器默认页数等一些一旦声明后就不会改变的变量)的时候使用const;同时也要了解var关键词的缺陷(变量提升,污染全局变量等 )
箭头函数
是否
iterator迭代器
iterator迭代器是ES6非常重要的概念;是常用的解构赋值、剩余/扩展运算符,生成器,for..of循环的实现基础,ES6新增的Set,map数据结构也是到它
iterator迭代器是一个对象,它具有一个next方法所以可以这么调用
执行一次next方法(消耗一次迭代器)会返回一个有value,done属性的对象
解构赋值
通过解构赋值我们可以用更具有表现力和更紧凑的语法来做同样的事情
对象的解构赋值
let dliata = {firstName:'Code', lastName:'Burst', age:22 }
const {firstName,age} = dliata;
console.log(firstName); //Code
console.log(age); //22
非同名变量赋值
const person = { name: 'jsPool', country: 'China' };
const {name:nameList,country:list}=person
console.log(name) //jsPool
console.log(country) //China
数组的结构赋值
let const= [211,'Baker Street','londems']
let [noe1,noe2]=list
console.log(noe1) //211
console.log(noe2) //Baker Street
嵌套数组的结构赋值
let const= [211,[red,greeds,heriang],'londems']
let [list,[firgat]]=const;
console.log(list,firgat) //211 red
不确定元素
let colors = ['red','green','blue'];
let [firstColor,...otherColors] = colors;
console.log(firstColor); // red
console.log(otherColors); // ['green','blue']
剩余/扩展运算符
剩余/扩展运算符也是ES6非常重要的语法,使用3个点(...),后面跟着一个含有iterator接口的数据结构
扩展运算符:
1,拷贝数组对象
let arr=【1,2,3,4,5】
let arr2=【...arr,6,7,8】 [1,2,3,4,5,6,7,8]
扩展运算符只会在一层进行深拷贝:
const listTop=[1,2,[3,4,5,6,7]]
const listtwe=[...listTop] //1,2,3,4,5,6,7
严格来说扩展运算符不算是深拷贝
2, 合并操作
let arr = [1,2,3,4,6]
let arr2 = [7,12,43,54]
let listTop=[...arr,...arr2]
3,参数传递
const sum =(num1,num2)=>{num1 + num2}
sum(...[6,7]) //13
sum(...[6,7,8]) //
4,解构赋值
const arrayNum=[1,23,33,33,12,435,12,243,23,1]
const mustSet =[...new Set(arrayNum)]
console.log(mustSet)
5,字符串转为字符数组
const stirng='githave'
const listgit=【..stirng】//['g','i','t','h','a','v','e']
剩余参数:当实参大于形参个数时,我们使用...来接收剩余参数,并且形式为数组的形式
let list_end=['listop','listgit','listweigt'];
let [s1,...s2]=list_end;
console.log(s1); //'listop'
console.log(s2); // ['listgit' ,'listweigt']
对象属性和方法简写(常用)
1,对象的属性简写:
let bar =()=>({x:4,y:5,z:6})
let {x:x,y:y,z:z}=bar()
简写:let {x,y,z}=bar()
2,方法简写
let obj3={
fun:function(){
}
}
简写:let obj3={
func(){
}
}
Promise(常用)
Promise一个构造函数,通过new关键字创建一个Promise实例
大部分异步请求使用promise是一个异步的解决方案,主要是解决了回调地域的问题;
基本原理:
1,Promise是一个类,在执行这个类的时候会传入一个执行器,这个执行器叫做立即执行
2,会有三种状态:Pending(等待)、Fulfilled(完成)、Rejected(失败)
3,状态只能由Pending===》Fulfilled或者是Pending===》Rejected,且一旦发生改变便不可二次修改
4,Promise中使用resolve、reject两个函数来更改状态
5,this的方法内部做但是事情就是状态判断
如果状态成功,调用成功回调函数。
如果状态失败,调用失败回调函数。
resolve和reject为什么要用箭头函数?
如果直接调用的话,普通函数this指向的是windows或者是undefined;用箭头函数可以让this指向当前的实例对象。
在Promise类中加入异步逻辑:
1,缓存成功和失败的函数回调
存储成功回调函数 onFulfilledCallback = null;
存储失败回调函数 onRejectedCallback = null;
》》实现then方法的链式调用:
then方法链式调用那么就返回了一个Promise 对象
then方法里面的return一个返回值作为下一个then方法的参数,如果是return一个Promise对象,那么需要判断他的状态。
如果then方法返回的是自己的promise对象,则会发生循环调用,这个时候程序会报错。
》》捕获执行器中的代码,如果执行器中的代码错误,那么promise的状态要变为失败;
》》参考fulfulled状态下的处理方式,对rejected和pending状态进行改造;
》》增加异步状态下的链式调用;增加回调函数执行结果的判读;增加识别promise是否返回自己;增加错误捕获
》》then中的参数可变为可选,我们处理then中的参数的时候默认可传onFulfilled、onRejectede两个回调函数,但实际上原生Promise是可以选择参数单传或者是不传,都不会影响执行。
什么是宏任务与微任务与eventLoop?
微任务在宏任务执行之前执行
宏任务称为Task,是有宿主(浏览器、Node)发起的
微任务称为jobs,是有js自身发起的
宏任务的创建方式:setTimeout、setInterval、I/O,事件队列、setImmediate(Node环境)、script(整体代码块)、MessageChannel
微任务的创建方式:requestAnimationFrame(有争议)、MutationObserver(浏览器环境)、Promise.[ then/catch/finally ]、process.nextTick(Node环境)、queueMicrotask
eventLoop(时间循环):js是个单进程的语言,同时不同处理多个任务;
检查过程是持续进行的,没完成一个任务都会进行一次,这样的操作是eventLoop;
什么是异步与同步?
同步: 由于js是单线程,换句话就是,在同一个时间内,只能处理一个任务,干一件事请,然后再去处理下一个任务,浏览器解析网页中的js代码,是逐步进行读取,从上至下执行。
异步:浏览器是多线程的,但解析我们的js代码,却是单线程的,但有些任务是需要消耗时间的,如果按照普通的同步凡是,就会阻塞我们的代码,主线程的任务没有做完,那么下面的任务将不会执行。
实现异步的核心原理,就是利用回调函数callback;
异步操作有哪些?
定时器都是异步操作;
事件绑定都是异步操作;
ajax一般都采用异步操作(也可以同步);
promise.then
事件监听
async await
async与await区别
async和await两个语法结合可以让异步代码向同步代码一样
async和await让我们可以用一种更简洁的方式写出基于promise的异步行为,而无需刻意的链式调用
async是声明异步函数,函数会返回一个promise对象
await是用来暂停异步函数代码的执行,等待promise解决
async:
是一个加在函数前面的修饰符
被async修饰的函数会默认返回一个promise对象;可以使用then方法添加回调函数
返回promise对象的结果是由async函数执行的返回值的结果决定的
await:
await必须写在async函数中,但是async函数中可以没有await
await的右侧表达式一般为promise对象,也可以是其他的值,他会返回该对象的结果;
如果await右侧表达式为其他的值,则直接将该值作为await的返回值
Module(常用)
模块化的目的:为了代码的可组织性、隔离性、可维护性、版本管理、依赖管理等
模块是实现特定功能的一组属性和方法的封装
Module使用import关键字导入模块,export关键字导出模块
特点:
1,ES6 Module是静态的,也就是说它是在编译阶段运行,和var以及function一样具有提升效果
2,自动采用了严格模式(顶层的this返回undefined)
3,ES6 Module支持使用export{<变量>}导出具名的接口,或者export default导出匿名的接口。
CommonJS(模块规范Node.js):
这种模块规范方案主要用于服务器端,Node.js实践了该规范,Node.js的接口:
定义模版的输入接口:module.exports
加载/引入模块:require
CommonJS以同步的方式来加载模块。因为服务器端的文件都储存在本地磁盘,所以读取非常快,同步加载没问题。
但在浏览器端的话,模块加载需要浏览器发起请求到服务端,等待返回文件,使用异步加载更合适。
AMD与CMD的区别:
AMD是RequireJS在推广过程中对模块定义的规范化产出。将所有依赖这个模块的语句都放到回调函数里,等待加载完成后再执行回调函数,require.js实践了该规范
指定引用路径:require.config()
定义的模块:define
加载的模块:require
CMD是SeaJS在推广过程中对模块定义的规范化产出。和AMD相似,区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。
目前两种都能实现浏览器端的模块化开发的目的,不同处是CMD是懒执行,AMD是预执行
区别:
对于依赖模块,AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成延迟执行(根据写法不同,处理的方式就不同)。CMD推崇依赖就近,AMD推崇依赖前置。
AMD用户体验比较好,CMD性能好并不推荐;
AMD定义模块时就要声明其依赖的模块,等所有模块加载完成之后,会进入require回调,执行主逻辑;
CMD:只有用到某个模块的时候再去require;等所有依赖模版加载完成后进入主逻辑,遇到require才执行对应模块。
CommonJS和ES6不同:
CommonJS模块输出的一个值得拷贝,ES6模块输出的是值得引用。
CommonJS模块是运行是加载,ES6模块是编译时输出接口。
CommonJS模块的require()是同步加载的,ES6模块的inport命令是异步加载。
函数默认值
ES6允许在函数的参数中设置默认值
ES5案例:比较繁琐
function pursout(name,num){
name = name || 'apple'
if(typeof num =="undefined"){
num =1
}
console.log(name+':'+num)
}
pursout("barmue",0)//
ES6案例:
function pursout(name=“apple”,num=“1”){
console.log(name+':'+num)
}
pursout()//apple:1
pursout("barmue",0)//barmue:0
Proxy拦截器
proxy在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作,必须通过这层拦截
作用:
1,拦截和监事外部对对象的访问
2,降低函数或类的复杂度
3,在复杂操作前对操作进行效验或对所需资源进行管理
10、作用域与作用域链?
作用域分为:全局作用域,局部作用域(控制着变量与函数的可见性和生命周期)
全局作用域:如果一个变量在函数外面或者大括号{}外面声明,那么就定义一个全局作用域
局部作用域:在ES6之前局部作用域只包含了函数作用域,ES6为我们提供的块级作用域,也属于局部作用域;只能在固定代码中可以访问
函数作用域:定义在函数中的变量就在函数作用域中。并且函数在每次调用时都有一个不同的作用域。这意味着同名变量可以用在不同的函数中,因为这些变量绑定在不同的函数中,拥有不同的作用域,彼此之间不能访问。
函数声明的优先级优于变量声明,且函数声明会连带定义一起被提升;
作用域链:javaScript上每一个函数执行时,会先在自己创建的AO(活动对象)上面找对应的属性值。若找不到则往父函数的AO(活动对象)上找,再找不到则再上一层的AO(活动对象),直到找到大boss:window(全局作用域)。而这一条形成的“AO链”就是javaScript中的作用域链。
11、事件流的描述:事件捕获阶段,处于目标阶段,事件冒泡阶段。
事件流描述了页面接收事件的顺序。事件流分为两种:事件冒泡和时间捕获。
事件冒泡:又称IE事件流,指从最具体的元素(绑定事件处理程序的元素)开始触发,然后向上传播直至文档元素。所有浏览器都支持事件冒泡。
事件捕获:从外层元素到目标元素的过程,为提供拦截事件提供了可能。
目标阶段:实际的目标元素接收到了事件。
12、防抖与节流原理、区别及应用?
防抖:n秒后在执行该事件,若在n秒内重复触发,则重新计时
节流:n秒内只运行一次,若在n秒内重新触发,只有一次生效
》》区别:
相同点:都可以通过setTimeout实现
目的都是降低回调执行的频率。节省计算资源
不同点:防抖在一段连续操作结束后,处理回调,利用clearTImeout和setTimeout实现。函数节流,在一段连续操作中,每一段时间只执行一次,而函数节流一段时间内只执行一次。
函数防抖一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次。
》》应用:防抖:搜索框搜索输入,只需用户最后输入完,再发请求
手机号、邮箱验证码的输入检测;
窗口的大小,只需窗口完成后计算窗口的大小。防止重复污染
节流:滚动加载,加载更多或者滚动到底部监听
搜索框,搜索联想功能。
13、Js垃圾回收机制?
1,项目中如果存在大量不被释放的内存(堆、栈、上下文),页面性能会变慢。当某些代码操作不能被合理释放,就会造成内存泄漏。我们尽可能减少使用闭包,因为他会消耗内存
2,浏览器垃圾回收机制/内存回收机制:
浏览器的Javascript具有自动垃圾回收机制,垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。
3, 优化手段:内存优化;手动释放,取消内存的占用即可
堆内存:fn = null 【null:空指针对象】
栈内存:把上下文中,被外部占用的堆的占用取消即可。
4,内存泄漏;在js中常见的内存泄漏有四种,全局变量、闭包、DOM元素的引用、定时器
目的:防止页面占用内存过大,引起客户端卡顿或无响应 Node.js使用V8引擎内存对后端服务的性能至关重要(因为后端的服务的持久性,容易造成内存的溢出)
14、JS性能优化?
加速的主要影响因素有:资源响应速度、资源体积优化、资源加载顺序、代码质量、用户网络速度、用户设备条件,不过用户设备和网速我们无法控制,所以我们主要优化方向是其他几方面