JS
1.JS组成:
ECMAScript JS的核心,描述了语言的基础语法,比如var,for,数据类型(数组,字符串等);
文档对象模型DOM,把整个HTML页面规划为元素构成的文档;
浏览器对象模型BOM,对浏览器窗口进行的访问和操作。
2.JS内置对象
String Boolean Number Array Object function Math Date RegExp
Math:abs() sqrt() max() min()
Date new Date() getYear()
String concat() length slice() split()
......
3.数组方法:
push() pop() sort() splice() unshift() shift() reverse() concat() join() map() filter()
every() some() reduce() isArray() findIndex()
哪些方法会改变原数组? push() pop() sort() splice() unshift() shift() reverse()
4.splice()和slice()区别
1.splice改变原数组,slice不改变原数组。splice会导致数组塌陷。
2.splice除了可以删除之外,还可以插入。
3.splice可传入3个参数,slice接受2个参数。
splice(index,deleteCount,item1,.....,itemX)
slice ( startIndex, endIndex )
注:数组塌陷:使用splice删除元素时,剩余的数组元素索引的顺序会改变。
5.数据类型检测方法:
- typeof() 检测基本数据类型,引用数据类型不行 console.log(typeof 666) //number
- instanceof() 只能判断引用数据类型,基本数据类型不行 console.log([1,2] instanceof Array) //true
- constuctor 几乎二者都可以判断 console.log( 'abc'.construtor === String ) //true 但是如果声明了一个构造函数,并把他的原型指向了Array,就不行了
- Object.prototype.toString() 都可以完美解决 var obj = Object.prototype.toString().call(2) console.log(obj)//number
6.闭包:
函数嵌套函数,内部函数可以访问其局部变量的函数。
特点:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收
缺点:闭包较多的时候,会消耗内存,导致页面的性能下降,使用不当很容易造成内存泄露。
应用场景:设计私有的方法和变量;维护变量的生命周期;模拟块级作用域;模块化代码,减少全局变量的污染。
7.内存泄漏:
JS中已经分配内存地址的对象,但是由于长时间没有释放或者没有办法消除,造成长期占用内存的现象,会让内存资源大幅浪费,最终导致运行速度慢,甚至崩溃。
因素:1、全局变量使用不当;2、闭包使用不当;3、延时器或定时器没有被清除;4、没有清理的DOM元素引用(dom清空或删除时,事件未清除)。
8.事件委托:
又叫事件代理,原理就是利用事件冒泡机制来实现的,也就是说把子元素的事件绑定到了父元素身上,如果子元素阻止了事件冒泡,那么委托也就不成立。
$('ul').on('click','li',function(){
console.log(this.innerHTML)
})
由ul下的li触发点击事件。
事件委托的好处:提高性能,减少事件绑定,也就减少内存的占用。
9.阻止事件冒泡的方法:
- event.stopPropagation()
- event.stopImmediatePropagetion() //除了可以阻止冒泡捕获,还会阻止绑定在该元素的其他事件发生
- addEventListener('click',函数名,true/false) //false 默认,采用事件冒泡;true,采用事件捕获
10.基本数据类型:
String Boolean Number undefined null symbol(es6) 保存在栈内存中,是具体值,占据固定大小的空间。
引用数据类型:Array Object Function 保存在堆内存中,保存的是地址,动态分配内存的大小。
11.原型和原型链:
原型:原型是一个对象,是函数的一个属性prototype;通过该函数实例化出来的对象都可以继承得到原型上的所有属性和方法。原型对象默认有一个属性constructor,值为对应的构造函数;另外,有一个属性__proto__,值为Object.prototype。
总的来说,就是原型就是一个普通对象,为构造函数的实例共享属性和方法,所有实例中引用的都是同一个对象,使用prototype可以把方法挂在原型上,内存中保存一份。
原型链:在JavaScript中万物都是对象,对象和对象之间并不是独立存在的,对象和对象之间有一定关系。一个实例对象在调用属性和方法的时候,会依次从实例本身,构造函数原型,原型的原型上查找,层层继承的链接结构叫做原型链(通过proto属性形成原型的链式结构,专业术语叫做原型链)。
__proto__可以理解为指针,实例对象中的属性,指向了构造函数的原型(prototype)。
js为什么有原型和原型链:
- 继承 :原型和原型链提供了一种方便的形式来实现对象之间的继承关系。通过原型链,对象可以继承原型的属性和方法,从而减少了代码的冗余和重复。
- 方法共享:通过原型,多个对象可以共享相同的方法,从而节省内存,并提高代码的性能和效率。
- 动态性:JavaScript的原型和原型链允许我们在运行时动态地添加、修改和删除对象的属性和方法。这使得代码具有灵活性和可扩展性。
- 原型链查找 当我们在一个对象上调用方法或访问属性时,如果对象本身没有该方法或属性,JavaScript会自动沿着原型链向上查找,直到找到对应的方法或到达原型链的末尾。这提供了一种灵活的机制来处理对象之间的属性和方法查找。
- 共享内置方法:JavaScript的内置对象(如'Array'、'String'、'Object'等)都通过原型链共享了一些通用的方法。这意味这我们可以直接在实例上调用这些方法,而不需要重复定义。
12.new操作符具体干了什么
-
创建一个新的对象
-
将对象与构建函数通过原型链连接起来
-
将构建函数中的this绑定到新建的对象
obj
上 -
根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理
13.JS是如何实现继承的
- 原型链继承:原型链继承是指将父类实例作为子类的原型。这种方式下,子类实例可以共享父类实例的属性和方法,但是无法向父类构造函数传递参数。
- 借用构造函数继承:借用构造函数继承是指在子类构造函数中调用父类构造函数,并使用 call 或 apply 方法将父类的 this 指向子类实例。这种方式的缺点是无法继承父类原型上的方法。
- 组合式继承:组合继承是指将原型链继承和借用构造函数继承结合起来。这种方式可以继承父类实例和原型上的属性和方法,但是会调用两次父类构造函数,且父类原型上的属性和方法会被继承两次。
- ES6中可以使用class和extends关键字来实现继承。
14.JS中关于this指向的问题
-
全局中的this指向window
-
全局作用域或者普通函数中的this指向window
-
this永远指向最后调用它的那个对象(不是箭头函数的情况下):(1).对象方法调用, 此时 this 指向 该方法所属的对象 (2).通过事件绑定的方法, 此时 this 指向 绑定事件的对象
-
new关键词改变了this指向:构造函数调用, 此时 this 指向 实例对象
-
apply ,bind,call改变this指向,不是箭头函数
-
箭头函数没有this,看外层是否有函数,有就是外层函数,没有就是window
-
匿名函数中的this永远指向window,匿名函数的执行环境具有全局性,因此this指向window
注:匿名函数:就是没有函数名的函数。
第一种方式:
var double = function(x) {
return 2* x;
}
//这里=右边的函数就是一个匿名函数,创造完毕函数后,又将该函数赋给了变量double。
//第二种方式:
(function(x, y){
alert(x + y);
})(3, 4);
//在第一个括号内创建了一个匿名函数,第二个括号用于调用该匿名函数,并传入参数。
15.call,apply,bind区别?
call和apply功能类似,都是立即执行。call方法传的是一个参数列表,性能 好一些 test.call(obj,'参数1 ','参数2');apply传递的是一个数组 test.apply(obj,['参数1 ','参数2'])
bind传参后不会立即执行,会返回一个改变了this指向的新函数,var newFn = test.bind(obj,'参数1 ','参数2')。
16.script标签中的async和defer有什么区别?
<script defer src="example1.js"></script>
<script async src="example2.js"></script>
defer 和 async属性都是去异步加载外部的JS脚本文件,它们都不会阻塞页面的解析,没有async和defer两个属性时,浏览器会立刻加载并执行脚本,当有两个属性时,区别如下:
有async时:加载和渲染后面元素的过程将和script的加载和执行并行进行,也就是文件加载完就会执行,不用考虑其他scrpt。
有defer时:加载和渲染后面元素的过程将和script的加载和执行也是并行进行,但是它的执行事件要等所有元素解析完成之后才会执行,也就是页面渲染完才会执行defer文件。
17.setTimeout最小执行时间是多少?
4ms
setInterval的最小执行时间是10ms
18.递归函数的时候有没有遇到什么问题?
递归函数:如果一个函数内可以调用函数本身,那么这个函数就是递归函数。
特别注意:写递归必须要有退出条件return
19.ES6新特性
- 新增块级作用域(let定义变量,const定义常量):不存在变量提升,不能重复声明,存在暂时性死区的问题。
- 新增了定义类的语法糖calss
- 新增了一种基本数据类型symbol,用来表示一个独一无二的值,防止第三方框架的同名属性被覆盖,let xxx=Symbol(‘标识字符串’);
- 新增了解构赋值,从数组或者对象中取值,然后给变量赋值。
- 新增了给函数参数设置默认值 function add(x=3,y=5){ return x+y; } add(5)//10
- 新增了数组的API:map() some() every() includes() find() findIndex() filter()
- 对象和数组新增了扩展运算符
- 新增Promise:解决了回调地狱,自身有all,reject,resolve,race方法,原型上有then,catch,为了就是把异步操作队列化。
- 新增了模块化(import,export)
- 新增了set和map数据结构,set的数据不能重复,有size属性,add() 方法,has() 方法,delete() 方法,clear() 方法,forEach() 方法;map的值的类型不受限制,有size属性,.set(key, value)方法,.get(key) 方法,.has(key),.delete(key)方法。clear()方法,forEach() 方法。
- 新增了generator
- 新增了箭头函数
20.数据去重的方法
1.ES6的new set()加上扩展运算符去重
var newArr = [...new Set(arr)]; //利用了Set结构不能接收重复数据的特点
2.利用filte()去重
var newArr = arr.filter(function(item,index){
return arr.indexOf(item) === index; // 因为indexOf 只能查找到第一个
});
3.利用inclue()给数组去重
function noRepeat(arr) {
let newArr = [];
for(i=0; i<arr.length; i++){
if(!newArr.includes(arr[i])){
newArr.push(arr[i])
}
}
return newArr
}
4.利用indexof查找特性去重
function noRepeat(arr) {
var newArr=[];
for(var i=0;i<arr.length;i++) {
if(newArr.indexOf(arr[i]) === -1) { //indexOf() 数组中查不到此值则返回-1
newArr.push(arr[i]);
}
}
return newArr
}
21.如何实现一个深拷贝
深拷贝就是完全拷贝一个新的对象,会在堆内存开辟新的空间,拷贝的对象被修改后,原对象不受影响,主要针对引用数据类型。
方法:
- 使用 JSON.parse(JSON.stringify()) 方法
- 使用递归函数拷贝
let origin = { name:'张三', age:18, say(){ console.log('hello') } } function deepCopy(origin,deep){ let obj = {} if( origin instanceof Array ){ obj = [] for (let i = 0; i < origin.length; i++) { obj[i] = deepCopy(origin[i]); } } else { obj = {}; for (let key in origin) { obj[key] = deepCopy(origin[key]); } } return obj } }
- jQuery 中的 $.extend() 第一个参数设置为true为深拷贝,为false为浅拷贝(要引入JQuery库) const objClone = $.extend(true, {}, obj);
- structuredClone()方法
浅拷贝: 浅拷贝是将一个对象或数组的值复制给另一个对象或数组,但如果对象或数组中包含对其他对象或数组的引用,则这些引用仍然指向原来的对象或数组。
方法:
- 使用 Object.assign(target,...source) 方法,该方法用于将一个或多个对象的属性复制到目标对象上。
- 利用扩展运算符:let objClone = { ...obj }
- 利用Array.prototype.slice()拷贝数组
- 利用Array.prototype.contact()拷贝数组
总结:
- 深拷贝:在堆内存中重新开辟一个存储空间,完全克隆一个一模一样的对象;
- 浅拷贝:不在堆内存中重新开辟空间,只复制栈内存中的引用地址。
22.事件循环
JS是一个单线程的脚本语言。
主线程先执行同步任务,然后才会去执行任务队列里的任务,如果在执行宏任务之前有微任务,那么先执行微任务,全部执行完成后等待主线程的调用,调用完成后再去任务队列里看是否有异步任务,有就执行。这样一个循环反复的过程就是事件循环。
23.ajax是怎么实现的
ajax:创建交互式网页应用的网页开发技术,在不重新加载整个页面的前提下,与服务器交换数据并更新部分内容,通过xmlHttpRequest对象向服务器发送异步请求,然后从服务器拿到数据,js更新DOM,具体步骤:
- 创建xmlHttpRequest对象xmh
- 通过xmh对象里的open()方法和服务器创建连接
- 构建请求数据所需的数据,并通过xmh对象的send()发送给服务器
- 通过xmh对象的onreadystatechange事件监听服务器端你的通信状态
- 接收并处理服务器相应的数据结果
- 把处理的数据更新到HTML上
jQuery实现:
$.ajax({
type: "POST", //发送是以POST还是GET
url: "ajax.php", //发送的地址
dataType: "json", //传输数据的格式
data: {"username": "zwkkkk1","password": 123456}, //传输的数据
//成功的回调函数
success: function(msg) {
console.log(msg)
},
//失败的回调函数
error: function() {
console.log("error")
}
})
24.get和post区别
- get是获取数据,post是提交数据
- get参数放在url上,安全性较差;post参数放在body中
- get请求刷新服务器或后退没有影响;post请求退回时会重新提交数据
- get请求时会被缓存,post不会
- get请求会被保存在浏览器历史记录中,post不会
- get请求只能进行url编码,post请求支持多种
25.Promise的原理
是个对象,封装了一个异步操作并且还可以获取成功或失败的结果,解决了回调地狱的问题(回调函数嵌套回调函数)
三种状态:pending,fulfilled,rejected
缺点:不能中途取消,如果不设置回调,promise内部抛出的问题无法反馈到外面,若当前处于pending状态,无法得知目前处于那个阶段。
原理:构造一个promise实例,实例需要传递函数的参数,这个函数有两个形参,函数类型,一个时resolve(执行第一个函数),一个是reject(执行第二个函数),还有.then方法。
function getNumber(flag){
var p = new Promise(function(resolve, reject){ //做一些异步操作
if (flag) {
console.log('success'); // 处理本身
return resolve('success param'); // 把任务处理的结果传递给回掉的then的success方法,作为其参数
} else {
console.log('failed');
return reject('falied param');
}
});
}
getNumber(true)
.then(function(data){
console.log('resolved');
console.log(data);
})
.catch(function(reason){
console.log('rejected');
console.log(reason);
});
.then(
(value)=>{
console.log(value);
},
(error)=>{
console.log(error);
}
);
catch方法和then的第二个参数一样,用来指定reject的回调,这样在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。
all方法提供了并行执行异步操作的能力,接收一个数组参数,并且在所有异步操作执行完后才执行回调。
any方法就是所有的Promise对象均失败后才会执行any中的失败回调,否则当任意一个Promise对象成功就会直接进入then回调。
race方法是返回第一个执行完的promise值。
26.Promise和async,await区别
都是处理异步请求的方法
优缺点:
1.promise是返回对象,我们需要用then,catch方法去处理和捕获异常,并且书写方式是链式,容易造成代码重叠,不好维护;async,await是通过tracatch捕获异常。
2.asycn,await最大的优点是能让代码看起来像同步一样,只需要遇到await就会立刻返回结果,然后再执行后面的操作,promise.then()的方式返回会出现请求还没返回就执行了后面的操作。
27.浏览器的存储方式
- cookies h5标准前的,兼容性好,请求头自带cookie,但存储量小,资源浪费,使用麻烦(封装)
- localstorage h5加入的以键值对为标准的方式,操作方便,永久存储,兼容性好,但保存值的类型被限制,浏览器在隐私模式下不可读取,不能被爬虫。
- sessionstorage 会话级限制存储方式。
- indexedDB h5标准存储方式,他是以键值对进行存储,可以快速读取,适合web场景
28.token
是验证身份的令牌,一般就是用户通过 账号密码登录后,服务器把这些凭证通过加密一系列操作后得到的字符串。
1.存在localstorage里,后期每次请求接口都需要把它作为一个字段传给后台,容易被xss攻击(跨站脚本攻击,做好相应措施,利大于弊)
2.存cookie中,会自动发送,缺点是不能跨域,会有CSRF攻击。
注:CSRF攻击是一种利用用户在目标网站上已认证的会话执行非预期操作的攻击方式。
29.token的登录流程
- 客户端用账号密码请求登录
- 服务端收到请求后,需要去验证账号密码
- 验证成功后,服务端会签发一个token,把这个token发送给客户端
- 客户端收到token后保存起来,可以放在cookie也可以放在local storage
- 客户端每次向服务端发送请求资源的时候,都需要携带这个token
- 服务端收到请求,接着就去验证客户端里的token,验证成功才会返回客户端请求的内容
30.页面渲染的流程
DNS解析--建立TCP连接--发送HTTP请求--渲染页面(浏览器会获取HTML和CSS的资源,然后把HTML解析成DOM树,CSS解析成CSSOM树,然后DOM树和CSSOM树合并为渲染树,布局,把渲染树的每个节点渲染在屏幕上(绘制))--断开TCP链接
注:TCP是面向连接的协议,它基于连接来传送TCP报文段。
31.DOM树和渲染树有啥区别?
DOM树是和HTML标签一一对应的,包括head和隐藏元素,渲染树不包含head和隐藏元素。
32.精灵图和base64的区别?
精灵图是把小图合成一张大图,访问页面时可以减少请求,提高加载速度。
base64是把图片转换成字符串,会和html,css一起下载到浏览器,减少请求,减少跨域,但是版本低的不合适。
33.svg格式
基于XML语法格式的图像格式,可缩放矢量图,是对图像形状的描述,本质是文本文件,体积小,并且放大缩小多少倍都不失真。
- SVG可直接插入页面中,成为DOM一部分,然后用js或css进行操作<svg></svg>
- SVG可作为文件被引入 <img src='pic.svg'/>
- SVG可以转为base64引入页面
34.了解JWT吗?
Json Web Token 通过json形式作为在web应用中的令牌,可以在各方之间安全的把信息作为json对象传输信息授权,具有简洁,包容性,因为token是json加密形式保存在客户端,所以JWT是跨语言的,原则上是任何web形式都支持。
JWT的认证流程:
- 前端把账号密码发送给后端的接口
- 后端核对账号密码成功后,把用户id等其他信息作为JWT负载,把它和头部分别进行base64编码后签名,形成一个JWT(token)
- 前端每日请求时都会把JWT放在HTTP请求头的Authorization字段中
- 后端检查是否存在,如果不存在就验证JWT的有效性(签名是否正确,token是否过期)
- 验证通过后后端使用JWT中包含的用户信息,进行其他操作,并返回对应结果
35.npm的底层环境是什么
npm:(node package manager)node的包管理和分发工具,已经成为分发node模块的标准,是JS的执行环境。
npm组成:网站,注册表,命令行工具
底层环境是v8引擎和libuv库
36.HTTP协议规定的协议头和请求头有什么
1.请求头信息:
Accept:text/html 浏览器告诉服务器所支持的数据类型 */*代表可处理所有类型
Host:主机和端口号 浏览器告诉服务器我想访问服务器的哪台主机
Referer:页面链接 浏览器告诉服务器我是从哪里来的(防盗链)
User-Agent:浏览器类型,版本信息
Date:浏览器告诉服务器我是什么时候访问的
Connection:连接方式 :keep-alive 完成后连接不会关闭/close 会关闭
Cookie
X-Request-With:请求方式。
2.响应头信息:
Location:这个就是告诉浏览器你要去找谁
Server:告诉浏览器服务器的类型
Connection-Type:告诉浏览器返回的数据类型
Refresh:控制了定时刷新,重定向
37.说一下浏览器的缓存策略
强缓存(本地缓存):不发送请求,直接使用缓存里的内容,浏览器把js,css,image等存在内存中,下次用户访问直接从内存中取,提高性能。
协商缓存(弱缓存):需要向后台发出请求,通过判断来决定是否使用协商缓存,如果请求内容没有变化,则返回304,浏览器就用缓存里的内容。
强缓存触发:HTTP.0:时间戳响应标头
HTTP.1:Cache-Control响应标头
协商缓存:请求头:if-modified-since 响应头:last-modified
请求头:if-none-match 响应头:Etag
38.说一下什么是‘同源策略’?
http://www.aaa.com:8080/index/vue.js
协议 子域名 主域名 端口号 资源
同源策略是浏览器的核心,如果没有这个策略就会遭受网络攻击,主要指的是协议+域名+端口号三者一致,若其中一个不一样则不是同源,会产生跨域
跨域是可以发送请求,后端也会正常返回结果,只不过这个结果被浏览器拦截了
解决跨域:
- JSONP 利用scrpt
- CORS 主要在于后端开启
- Websocket 双向通信
- 反向代理
39.防抖和节流是什么?
防抖:避免事件重复触发。
场景:1.事件触发频率过高的场景 2.输入框的自动保存事件
节流:把频繁触发的事件减少,每隔一段时间只执行一次函数。
场景:1.图片懒加载 2.ajax数据请求加载
区别:防抖只会在最后一次事件后执行触发函数,节流不管事件多么的频繁,都会保证在规定时间段内触发事件函数。
40.JSON
JSON是一种纯字符串形式的数据,它本身不提供任何方法,适合再网络中进行传输。
JSON数据存储在.json文件中,也可以把Json数据以字符串的形式保存在数据库,cookies中
JS提供了JSON.parse() :将数据转换为 JavaScript 对象。
JSON.stringify():将 JavaScript 对象转换为字符串.
使用场景:定义接口,序列化,生成token,配置文件package.json
41.当数据没有请求过来的时候该怎么办?
可以在渲染数据的地方给一些默认值,用if语句判断显示。
42.有没有做过无感登录?
- 在响应其中拦截,判断token,返回过期后,调用刷新token的接口
- 后端返回过期时间,前端判断token的过期时间,去调用刷新token的接口
- 写定时器,定时器刷新token接口
流程:
- 登录成功后保存token和refresh-token
- 在响应拦截器中对401状态码引入刷新token的api方法的调用
- 替换保存本地新的token
- 把错误对象里的token替换
- 再次发送未完成的请求
- 如果refresh-token过期了,判断是否过期,过期了就清除所有token重新登录
43.大文件上传怎么做的?
分片上传:
- 把需要上传的文件按照一定的规则,分割成相同大小的数据块
- 初始化一个分片上传任务,返回本次分片上传的唯一标识‘
- 按照一定的规则把各个数据块上传
- 发送完成后,服务器会判断数据上传的完成性,如果完整,那么就会把数据库合并成原始文件
断点续传:服务器返回,从哪里开始,浏览器自己处理。
44.for in和for of区别
- for in是无序遍历数组或对象的,也就是随机遍历,不按照顺序来; for of 是按照顺序遍历的
- for in语句用于遍历数组或者对象的属性,得到的是对象的key或数组,字符串的下标。for of只能便利数组,不能遍历对象,且直接得到值。
- for of 不同与 forEach, 它可以与 break、continue和return 配合使用,也就是说 for of 循环可以随时退出循环。
- 推荐在循环对象属性的时候使用 for...in,在遍历数组的时候的时候使用 for...of
45.优化性能的方法
- 加载优化(减少http请求数):合并图片,合并压缩css样式表和js脚本,去掉不必要的请求,优化首屏加载,利用缓存,预加载,图片懒加载,异步加载第三方资源,
- SEO优化
- 优化前端代码结构
- 将css文件放在头部用link引入,快速加载,将js脚本放在底部异步加载,减少阻塞。
- 优化脚本代码:使用定位脱离文档流,减少页面的重绘;尽量使用委托事件,减少dom事件,提高性能;尽量使用id选择器,可以快速定位位置。
- 减少不必要的cookie