1.js的组成
js包括三部分ECMAscript、DOM、BOM
ECMAscript描述了js的语法和基本对象(内置对象)
BOM(浏览器对象模型)是与浏览器交互的方法和接口(常用的对象有window、location、history)
DOM(文档对象)是处理网页内容的方法和接口,javascript对网页进行的所有操作都是通过DOM进行的
2.js的对象
js对象分为三类,内置对象(内部对象、本地对象)、宿主对象、自定义对象。
内置对象有 (ECMAScript提供的需要实例化(new)才能使用的对象:)Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError、(ECMAScript提供的不需要实例化就能使用的对象:)Global (全局对象)和 Math。
宿主对象 浏览器提供的对象。所有的BOM和DOM都是宿主对象。
自定义对象 开发人员自己定义的对象
3.js的数据类型
js基本对象有 Number、Boolean、String、Null、Undefined、Symbol、BigInt
Symbol定义一个唯一的值,有重名问题时,定义的后面的数会覆盖前面的数,只输出一个值。用Symbol定义就算重名也不会被覆盖。
BigInt是一种数字类型的数据,他可以表示任意精度格式的整数,使用BigInt可以安全的存储和操作大整数,即使这个数已经超出了Number(-2^53, 2^53)能够表示的安全整数范围。
js引用数据类型有Object、Array、Function、Date、RegExp
基本类型和引用类型的区别
①基本类型的值是不可变的;引用类型的值是可变的
②基本类型的比较是值的比较;引用类型的比较是引用的比较
③基本类型的值是存放在栈内存中的;引用类型的值是同时放在栈内存和堆内存中的,栈内存种保存的是变量标识符和指向堆内存的指针,堆内存中存放的是变量的值
补充:栈是先进后出,栈内存由编译器自动释放。堆是一个优先队列(队列是先进先出),堆内存一般由开发者释放,开发者不释放的话,程序结束后可能由垃圾回收机制回收。
4.怎么判断不同的js数据类型
1.typeof
语法:typeof(xxx)返回一个字符串
typeof只能判断Nmeber、String、Boolean、Undefined、Function的值
Null及其他引用类型都为Object
2.instanceof
语法:A instanceof B 返回一个布尔值
他只能判断A是否是B的一个实例,而不能判断A具体是哪一个类型
3.constructor
语法:a.constructor
当一个函数被定义时,js引擎会为其添加一个prototype原型,然后在prototype上添加一个constructor属性,并让其指向该函数的引用。null和undefined是无效的对象,不会有constructor存在。函数的constructor是不稳定的,当开发者重写prototype后,原有的constructor引用会丢失,默认值为object。
4.object.prototype.toString.call()
为什么需要call?
由于Object.prototype.toString()本身允许被修改,像Array、Boolean、Number的toString就被重写过,所以需要调用Object.prototype.toString.call(arg)来判断arg的类型,call将arg的上下文指向Object,所以arg执行了Object的toString方法。
5.undefined和null的区别
null:表示没有对象,即此处不应该有值
用法:
①主动释放一个变量引用的对象,表示一个变量不在指向任何对象的地址(闭包中释放内存)
②作为函数的参数,表示该函数的参数不是对象
③作为原型链的终点
undefined:表示缺少值,即此处应该有一个值,但是没有被定义
用法:
①函数没有返回值
②函数应该传入的参数没有传入
③变量被声明了,但是没有赋值
④对象没有赋值的属性
6.数组对象有哪些常用的方法
(1)修改器方法:
pop() 在数组尾部删除一个元素,返回被删除的元素
push() 在数组末尾插入一个元素,返回数组的新长度
shift() 在数组头部删除一个元素,返回被删除的元素
unshift() 在数组头部插入一个元素,返回新的数组长度
sort() 给数组元素进行排序,并返回当前数组 sort(a,b) 不传入参数以字典顺序为标准(一般来说都是ascll编码的排序) a-b升序 b-a降序
reverse() 颠倒数组中元素,返回一个新的反向数组
splice() 在任意位置给数组删除或增加任意个元素,返回的是删除后的数组元素 删除或增加功能由第二个参数控制,第二个参数不为0删除或者替换,为0增加
修改器方法都会改变原数组
(2)访问方法
concat() 返回一个有当前数组和其他若干个数组或非数组的值组成的新数组
slice() 抽取当前数组中的一段元素组成一个新数组 slice()没有参数就是对数组的浅拷贝;slice(2)表示从索引为2开始截取直到最后一个元素;slice(2,3)从索引为2开始索引为3结束;slice(-2)从倒数第二个元素开始截到最后
indexOf() 返回数组中第一个与指定值相等的元素的索引,如果找不到则返回-1
lastIndexOf() 从数组的最后一个元素进行查找
join() 所有的数组元素转化成一个字符串
访问方法不会改变原数组
(3)迭代方法
forEach() 为数组的每个元素都执行一次回调函数,总是返回undefined,无法用break打断,一旦开始无法停止,会不会对原数组发生改变取决于传入的参数类型,基本类型不会,引用类型会
map() 返回一个由回调函数的返回值组成的新数组,可以return出来,不会对原数组产生变化
filter() 将所有在过滤函数中返回true的数组元素放进一个新数组并返回
every() 如果数组中的每个元素都满足测试函数则返回true,否则返回false
some() 如果数组中的有一个元素满足测试函数则返回true,否则返回false
reduce() 接受一个函数作为累加器 有4个参数,prev:函数传进来的初始值或上一次回调的返回值;current:数组中当前处理的元素值;currentIndex:当前元素索引;arr:当前元素所属的数组本身。如果第一个元素没有初始值,他表示前一个元素;有初始值则为初始值。
除了forEach都不会改变原数组
7.js创建对象的方式
①对象字面量
var obj={}
②Object构造函数
var obj=new Object()
③工厂模式
function person(age,name){
var obj=new Object()
obj.name=name
obj.age=age
obj.say=function(){
conlose.log('唱歌')
}
return obj
}
优点:抽象了创建具体对象的过程
缺点:无法解决对象的识别问题
④构造函数模式
function person(age,name){
this.name=name;
this.age=age;
this.say=function(){
conlose.log('唱歌')
}
}
var person=new Person(‘hello’,18)
优点:解决了对象的识别问题
缺点:同样的方法在每个实例上都要重新创建,造成内存空间的浪费
⑤原型模式
function person(){
preson.prototype.name='lilei'
person.prototype.say=function(){
conlose.log('唱歌')
}
}
preson.prototype.friend='hanmeimei'
优点:解决了方法重复创建的问题
缺点:实现了属性与方法的共享,可以动态的添加对象的实例或方法,但是不能创建实例自己的属性和方法,不能向构造函数传参
⑥构造函数和原型结合
function person(age,name){
this.name=name;
this.age=age;
}
person.prototype.say=function(){
conlose.log('唱歌')
}
var person=new Person(‘hello’,18)
优点:可以向构造函数传参,也不用重复创建函数
缺点:构造函数和原型分离,封装性不够
8.对象的深浅拷贝
(1)浅拷贝
浅拷贝就是对对象进行浅层次的复制,只复制一层对象的属性,并不包括对象里面的引用类型数据

其他方法:
①object.assign(target,source)
②Array.prototype.slice()
③扩展操作符...
(2)深拷贝
对对象深层次的复制

其他方法:
JSON.parse(JSON.stringify(source))
(3)深浅拷贝的区别
浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化;深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变。
9.js实现继承的方式
(1)原型链继承
优点:简单、易实现
缺点:对象实例共享所有继承的属性和方法,创建子类实例时无法传参。
(2)构造函数继承
优点:解决了原型链继承不能传参的问题和父类属性共享的问题
缺点:无法实现函数的复用,只能继承父类的属性和方法不能继承原型链上的属性和方法
(3)组合继承
优点:解决了原型链继承和构造函数继承的影响
缺点:会调用两次父类构造函数,一次是在创建子类型原型的时候,另一次是在子类型构造函数内部
(4)寄生式继承
优点:写法简单,不需要单独创建构造函数
缺点:通过寄生式继承给对象添加函数会导致函数难以重用。
(5)寄生组合式继承
优点:高效率只调用一次父构造函数,并且因此避免了在子原型上面创建不必要,多余的属性。与此同时,原型链还能保持不变
缺点:代码复杂
(6)es6的class继承
原理ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this
10.作用域和作用域链
作用域:作用域是在代码执行的过程中代码中某些特定部分的变量、函数和对象的可访问性。作用域决定了代码区块中变量和其他资源的可见性。
作用域链:当我们在某个函数内部作用域中查找某个变量时,如果没有找到,就去他的父级作用域中查找,如果父级作用域中也没有找到,那么就会接着一层一层向上查找,直到找到全局作用域,没有找到就宣布失败。这种一层一层的作用域嵌套关系就是作用域链。
意义:变量隔离。即不同的作用域下同名变量不会有冲突。
11.bind、call、apply
bind、call、apply都是用来改变this指向的
call和apply的区别是call传入的是字符串序列,而apply传入的是数组
bind和它们的区别是它的返回值是一个函数,只有在调用它的时候才会执行
(1)call的其他用法
①判断数据类型 object.prototype.toString.call()
②类数组转数组 Array.prototype.slice.call()
(2)apply的其他用法
求数组的最大或者最小值 Math.max/min.apply(null,args)
(3)bind的其他用法
①偏函数的实现
使用bind()让一个函数拥有初始化参数,调用的时候传入其他参数即可
②在定时器中使用
比如和setTimeout一起使用,一般情况下setTimeout()的this指向window或global对象。当使用类的方法时需要this指向类实例,就可以使用bind()将this绑定到回调函数来管理实例。
12.数组去重
①双层for循环

②indexOf去重

③sort去重

④set去重

13.对象数组去重
①reduce方法

②filter方法

14.给定数组取最大值
①sort方法

②Math.max函数
Math.max(...arr)
③reduce函数

15.函数的防抖和节流
(1)防抖
事件被触发n秒后再执行回调函数,如果n秒内又被触发,则重新计时。应用场景:文本框输入

(2)节流
在规定时间内事件响应函数只能被触发一次,如果多次触发函数,只有一次生效。应用场景:点击按钮提交事件

16.数组拍平
(1)flat

(2)while循环

(3)先转化成字符串,在转化成数组

17.new操作符具体干了什么
①创建一个空对象
②将构造函数的原型赋值给新对象(也就是将对象的_proto_属性指向构造函数的prototype属性)
③执行构造函数,将构造函数中的this指向该对象
④返回一个新对象
18.图片懒加载
当图片出现在可视区域时,获取图片真实的地址并赋值给图片即可
window.innerHeight 浏览器可视区域的高度
document.body.scrollTop 浏览器滚动过的距离
offsetTop元素顶部的距离到文档顶部的高度

19.宏任务和微任务
常见的宏任务:setTimeout、setInterval、ajax请求、script整体代码、ui渲染
常见的微任务:Promise Promise.then/.catch/.finally Async/await node.js里的process.nextTick(下次循环时在调用传入的回调函数) Object.observe(监听一个对象的变化,当对象变化时立即调用回调函数来告知我们) MutationObserve(监听dom树的变化)
宏任务由事件触发线程执行、微任务由js引擎线程维护
20.js事件循环机制
js是一门单线程语言,为了协调事件、用户交互、脚本、ui渲染、和网络处理等行为,用户引擎必须使用evenloop(事件循环)。所有的任务可以分为同步任务和异步任务。同步任务就是立即执行的任务,在js主线程中执行。异步任务会通过任务队列的机制来进行协调。任务队列又分为宏任务和微任务。在执行栈清空后先执行所有可执行的微任务,当所有可执行的微任务执行完毕后,再去执行宏任务,把当前宏任务的微任务执行完毕后才会去执行下一个宏任务。
21.进程和线程
进程是系统分配的内存,是独立的一块内存,各个进程间相互独立,线程是进程的一部分,一个进程由多个线程组成,多个线程在进程中协作完成任务,同一进程下的各个线程之间共享程序空间。
22.发布订阅模式
发布订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到状态改变的通知。订阅者把自己想订阅的事件注册到调度中心,当发布者发布该事件到调度中心,也就是该事件触发时,由调度中心统一调度订阅者注册到调度中心的处理代码
23.事件捕获和事件冒泡
事件冒泡:事件会从最内层的元素开始发生,一直向上传播,直到document对象。
事件捕获:与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。
element.addEventListener(event, function, useCapture)
第一个参数是需要绑定的事件,第二个参数是触发事件后要执行的函数。而第三个参数默认值是false,表示在事件冒泡的阶段调用事件处理函数,如果参数为true,则表示在事件捕获阶段调用处理函数。
如果一个dom节点即注册了事件捕获,又注册了事件冒泡,会先执行捕获阶段后执行冒泡。
用event.stopPropagation阻止冒泡和捕获的进一步传播
用event.preventDefault阻止默认行为
24.字符串和数组的相互变化
字符串转数组:①arr.split(',') ②Array.form(str) 前提是有length属性
数组转字符串:①toString() ②arr.join(',')
25.object.is()与比较操作符'==='、'=='的区别
使用‘==’进行判断时,如果两边的类型不一致,会进行强制类型转化然后再进行比较
使用‘===’进行判断时,类型不一致不会强制转化
使用object.is()进行判断时,一般和三等号判断结果相同,他处理了一些特殊情况,比如-0和+0不再相等,两个NAN是相等的
26.什么是js的包装类型
在javascript中,基本类型是没有属性和方法的,但是为了便于操作基本类型的值,在调用基本类型的属性或方法时javascript会在后台隐式的将基本类型的值转化为对象
可以通过object()显式地将基本类型的值转化为包装对象 使用valueof()方法将包装类型转化为基本对象
27.判断对象是否是空对象
①json.stringify(obj)=={}
②object.keys(obj).length==0
28.箭头函数和普通函数的区别
①外形不同:箭头函数以箭头定义,普通函数不用
②箭头函数都是匿名函数,普通函数既有匿名函数又有具名函数
③箭头函数不能用作构造函数,不能使用new,普通函数可以用构造函数来创建对象实例
④箭头函数本身不创建this,在声明时捕获上下文的this供自己使用;普通函数的this总指向调用它的对象,如果用做构造函数则指向新创建的对象实例
29.对json的理解
json是一种基于文本的轻量级的数据交换方式,它可以被任何的编程语言读取和作为数据格式传递。在项目开发中,使用json来作为前后端数据交换的方式。在前端通过将一个符合json格式的数据结构序列转化为json字符串,然后将它传递给后端,后端通过json格式的字符串解析后生成对应的数据结构,以此来实现前后端数据的传递
在js中提供了两个函数来实现js数据和json格式数据的转化
json.parse() json格式的字符串转换为js数据结构
json.stringify js数据结构转换为json格式的字符串
30.对ajax的理解
ajax指的是通过javascript的异步通信,从服务器获取xml文档从中提取数据,再更新当前网页的对应部分,而不用刷新服务器。

31.commonJS、AMD、CMD
commonJS、AMD、CMD是js模块化开发的标准
commonJS主要针对于服务器端,服务器端一般采用同步加载文件,也就是需要某个模块,服务器就停下来,等到模块加载完后再执行
AMD、CMD是针对于浏览器端,浏览器端要保证效率,所以采用异步加载文件,这就需要一个预处理,提前将所需要的模块文件并行加载好
AMD和CMD的区别:虽然都是并行加载js文件,但AMD是预加载,在并行加载js文件的同时还会解析执行该模块;CMD是懒加载,虽然会一开始就并行加载js文件,但是不会执行而是在需要的时候执行
ES6 module与commonjs的区别:①commonjs是对模块的浅拷贝,es6 module是对模块的引用,即es6 module只存只读,不能改变其值,也就是指针指向不能变。②commonjs模块是运行时加载而es6 module模块是编译时输出接口。③commonjs模块的require()是同步加载模块,而es6模块的import命令是异步加载。
32.ajax和axios的区别
①axios是一个基于promise的http库,而ajax是对原生xhr的封装
②ajax技术实现了局部数据的刷新,而axios实现了对ajax的封装
33.axios的传参
1.get/delete

2.post/put

34.原型链的终点
Object.prototype._proto_===null
35.事件委托
事件委托指的是不在事情的发生地上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素dom的类型来做出不同的回应。
好处:
①减少内存消耗
一个列表有大量列表项,我们需要在点击列表项的时候响应一个事件,那么把这个事件绑定到父级,可以减少大量内存消耗,提高效率
②动态绑定事件
新添加的子元素也会有监听函数,也可以有事件触发机制
36.原型和原型链
1.原型
所有的引用类型都有一个_proto_属性,属性值是一个普通对象。
所有的构造函数都有一个prototype属性,属性值是一个普通对象。
所有引用类型的的_proto_属性都指向其构造函数的prototype属性,这就是一个原型对象
2.原型链
当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有则会去找他的_proto_隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的_proto_中查找,这样一层一层向上查找就会形成一个链式结构,我们称之为原型链。
原型和原型链可以用来创建对象和实现继承
37.cookie、localstorage、sessionstorage的区别
①cookie数据始终在http同源请求中携带,即cookie在浏览器和服务器之间来回传递,而localstorage和sessionstorage不会自动把数据发送给服务器,仅保存在本地。
②cookie数据不能超过4k,而localstorage和sessionstorage可以达到5m或更大
③cookie在设置的有效期之内有效,localstorage永久有效,用于持久存储数据,sessionstorage仅在当前浏览器窗口关闭之前有效
④sessionstorage不在不同的浏览器窗口中共享;而cookie和localstorage可以在所有同源窗口共享
⑤webStorage API接口使用更加方便
38.cookie、session、token的区别
cookie和session都是记录服务器和客户端会话状态的机制
seesion是基于cookie的,session保存在服务器端,而sessionID保存在客户端的cookie中。当用户第一次向服务器发送请求时,服务器会根据用户提交的相关信息,创建对应的session,并将session唯一的标识信息sessionID发送给浏览器,浏览器接收到sessionID信息后,将信息存储在cookie中。第二次向服务器发送请求时,请求会自动判断该域名下是否存在cookie信息,如果存在则将cookie信息也发送给服务器,服务器会从cookie中获取sessionID,再根据sessionID查找相应的session信息。如果没有找到,则说明用户没有登陆或者登录已失效;如果找到,则说明登陆成功,用户可以进行下一步操作。
session和cookie的区别:
①session是存储在服务器端的,cookie是存储在客户端的
②session可存储的数据远远高于cookie
③cookie只支持字符串数据,session可存任意格式的数据
token令牌:客户端使用用户名和密码请求登录,服务端收到请求验证用户名和密码,验证成功后,服务端会签发一个token发送给客户端,客户端收到token后会把它存储起来,比如cookie或者localStorage里,客户端每次向服务端发送请求的时候需要携带服务端签发的token,服务端收到请求后去验证携带的token,如果验证成功,就像客户端返回请求的数据。每一次请求都需要携带token,需要把token放在http的header中,基于token的用户认证是一种服务端无状态的认证方式,服务端不用存放token数据,用解析token的计算时间换取session的存储空间,从而减轻服务器的压力,它可以避免同源策略。
session和token的区别:
session是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。而token是令牌,token使服务端无状态化,不会存储会话信息。
39.什么是闭包,闭包有什么用?
(1)什么是闭包?
闭包是指有权访问另外一个函数作用域中的变量的函数。闭包就是函数局部变量的集合,只是这些局部变量在函数后会继续存在。当一个函数内定义另一个函数就会产生闭包。
(2)有什么用?
(全局变量不会被回收,局部变量在函数执行完成以后,函数内部的东西就会被销毁,除非被被另一个作用域所引用就不会被回收。)
①模仿块级作用域:所谓块级作用域就是指在循环中定义的变量,一旦循环结束,变量也随之销毁,它的作用范围只在这一小块。而在JavaScript中没有这样的块级作用域,由于JavaScript不会告诉你变量是否已经被声明,所以容易造成命名冲突,如果在全局环境定义的变量,就会污染全局环境,因此可以利用闭包的特性来模仿块级作用域。
②结果缓存:如果有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,找不到则进行计算,然后更新缓存并返回值。闭包就可以做到这点,因为它不会释放外部的引用,从而使函数内部的值得以保留
③封装私有变量:我们可以把函数当作一个范围,函数内部的变量就是私有变量,在外部无法引用,但是我们可以通过闭包的特点来访问私有变量。
(3)闭包的优点是可以避免全局变量的污染,实现属性私有化。缺点是闭包会常驻内存,在不用的时候需要删除,否则会导致内存溢出,增加内存使用量。
40.js获取节点的api
①getElementById
根据id返回元素,返回值是一个完整的标签,不存在返回null
②getElementByName
通过指定的name元素来获取元素,返回值是一个NodeList(类数组对象)
③getElementByClassName
根据元素的class返回一个即时的htmlCollection(返回值是一个数组)
④querySelector/querySeletorAll
querySelector返回的是第一个匹配的元素,如果没有返回null
querySeletorAll返回一个nodelist
41.数组排序
①冒泡排序
时间复杂度o(n2) 空间复杂度o(1)
冒泡排序最少1趟,最多n-1趟,最少比较n-1次,最多n(n-1)/2次

②选择排序
时间复杂度o(n2) 空间复杂度o(1)
选择排序一定是n-1趟排序,比较的次数永远是n(n-1)/2

③快速排序
时间复杂度o(nlogn) 空间o(logn)

④插入排序
时间复杂度o(n2) 空间复杂度o(1)

⑤归并排序
时间复杂度o(nlogn) 空间复杂度o(n)

4.2跨域和跨域的解决方案
跨域是指浏览器不能执行其他网站的脚本,这是由于浏览器的同源策略造成的,是浏览器对javascript实施的安全限制。浏览器从一个域名的网页去请求另一个域名的资源时,出现域名、端口、协议的任一不同,都属于跨域。
跨域的解决方案:
①通过jsonp进行跨域(只能实现get请求)
jsonp方法的本质是引用一个<script>标签,在客户端脚本中注册一个回调函数,然后把回调函数的名字传给服务器,服务端得到请求的数据后,用回调函数把输出返回的内容包起来,这样,服务器生成的json数据就能被客户端正确接收。
②postMessage跨域

③跨域资源共享(cros)
普通跨域请求,只服务端设置Access-Control-Allow-Origin即可,前端无须设置
若要携带cookie请求,前端需要设置xhr.withCredentials=true
④nginx代理跨域
nginx具体配置
location / {
proxy_pass 跨域地址
}