1.理解Javascript微任务和宏任务,比较普通console.log、setTimeout、new Promise()代码执行顺序.
在挂起任务时,JS引擎会将所有任务按照类别分到这两个队列中,首先在macro-task的队列(这个队列也被叫做task queue)中取出第一个任务,执行完毕后取出micro-task队列中的所有任务顺序执行;之后再取macro-task任务,周而复始,直至两个队列的任务都取完。
分类 | Javscript常见 |
同步执行 | script、Promise、new Promise |
异步执行 | setTimeout,setInterval |
宏任务(macro-task) | script,setTimeout(function() {}),setInterval(function() {}),new Promise(function() {}) |
微任务((micro-task)) | process.nextTick,new Promise(function() {}).then(() => {}),new Promise(function() {}).catch(() => {}) |
分析以下代码执行顺序:
console.log(1)
setTimeout(function () { console.log(2) }, 0)
new Promise(function (res, rej) {
console.log(3)
for (let i = 0; i < 10000; i++) {
i == 9999 && res()
}
}).then(() => { console.log(4) })
new Promise(function (res, rej) {
console.log(5)
for (let i = 0; i < 10000; i++) {
i == 9999 && rej()
}
}).catch(() => {console.log(6)})
console.log(7)
分析执行结果过程:
第一趟:同步任务主线程中找到script标签内容,宏任务console.log(1)输出1,遇到setTimeout注册异步事件但是不执行(Event Table),第一new Promise()中输出3,第二个new Promise()中输出5,console.log(7)输出7,微任务为第一个Promise的then方法和第二个Promise的catch方法,此时内部方法不执行,放在事件队列中,等待第一次宏任务执行完毕
第二趟:找到第一趟中所有的微任务执行,输出4和6
第三趟:同步任务主线程执行完毕,不断循环从异步事件队列(Event Queue)中取出,此过程也就是Javascript的执行运行机制过程(Event Loop),执行setTimeout中方法输出2
输出结果为:1,3,5,7,4,6,2
2.闭包是什么?闭包的作用和缺陷。
介绍:函数对象可以通过作用域链相互关联起来,函数体内的变量都可以保存在函数作用域内,这种特性叫做闭包
作用:
1)实现对象的私有数据
2)创建有状态的函数
3)用闭包解决递归调用问题(例如:快排)
4)用闭包模仿块级作用域(例如:使用立即执行函数)
缺陷:
1)引用的变量可能发生变化(使用立即执行函数修复)
// 以下代码执行每个arr[i]在不改变i值的情况下都为10
// 原因:在生成arr[i]函数时,并没有被执行,此时记录的i的值并不是实际的值,而是指向i的内存空间
var arr = [];
for(var i = 0; i<10; i++) {
arr[i] = function() {
return i
}
}
// 立即执行函数解决如下
// 原因:立即执行函数使变量i只在某次循环中有效,并记录当前内存中i值执行并返回出来得到实体的值
var arr = []
for(var i =0; i<10; i++) {
arr[i] = (function(a) {
return function() {
return a
}
})(i)
}
2)this指向问题(把函数体改为 ()=>)
var obj = {
name: 'reai',
getName: function() {
return function() {
console.log(this.name)
}
}
}
obj.getName()() //输出 undefined
// 原因:返回的函数会改变this的作用域,使this指向全局window对象
// 使用箭头函数不会改变this的指向,修改如下:
var obj = {
name: 'reai',
getName: function() {
return () => {
console.log(this.name)
}
}
}
obj.getName()() //输出 reai
3)内存泄露问题(引用外部dom对象数据时需要外部获取,再释放dom内存)
function deal() {
var app = document.getElementById('#app')
app.onclick = function() {
console.log(app.id)
}
}
// 处理办法
function deal() {
var app = document.getElementById('#app')
var id = app.id
app.onclick = function() {
console.log(id)
}
app = null // 内存释放
}
3.跨域解决方案及其解决过程
介绍:浏览器为了解决安全问题引入了同源策略,若两地址的协议、域名、端口其一不同就认定为不同的源,也就是存在跨域问题。
解决办法:
1)CORS(跨域资源共享)
CORS会把浏览器的请求分为简单请求和非简单请求
简单请求:
请求方法:GET\HEAD\POST
请求头:Accept、Accept-Language、Content-Language、Content-Type(值包括:applicatin/x-www-form-urlencoded 、multipart/form-data、text/plain)
此时请求头中包含Origin字段表示来源,响应中有字段Access-Control-Allow-Origin表示允许访问的资源域,设置为*为所有域都支持访问,但是不能携带Cookie
非简单请求:
先发送一次预检请求「Preflight」包含字段如下:
Origin 请求来源
Access-Control-Request-Method 请求的方法
Access-Control-Request-Headers 请求所包含的头部信息
响应包含字段如下:
Access-Control-Allow-Origin 允许的请求源地址
Access-Control-Allow-Methods 允许的请求方式
Access-Control-Allow-Headers 允许设置的头部信息
然后再发送一次主要请求再响应完成一次非简单的请求结束
2)JSONP方法
JSONP方法是通过动态创建script来访问地址,通过前台与后台约束的方法来回调返回参数的过程,用script.onerror监听4xx、5xx错误,JSONP只有GET方法,例子如下:
//
/**
obj = {
url: '',
data: '',
error: err => {},
success: res => {}
}
**/
function jsonp(obj) {
let head = document.getElementsByTagName('head')[0];
let script=document.createElement('script');
script.type="text/javascript";
head.appendChild(script);
script.onerror=function(){return obj.error && obj.error("Request failed")}
this.callbackName = 'jsonp_' + new Date().getTime()
obj.url += obj.url.indexOf("?") == -1 ? ("?callback=" + this.callbackName) : ("&callback=" + this.callbackName)
obj.url += (obj.data.length === 0 || typeof obj.data === "undefined") ? '' : ('&' + obj.data)
script.src = obj.url;
window[this.callbackName] = function(json) {
head.removeChild(script);
delete window[this.callbackName]
return obj.success && obj.success(json);
}
}
3)Cookie跨域共享
Ajax请求不会自动将当前URL关联的Cookie同请求一同发送到服务端,可以设置XMLHttpRequest对象的withCredentials属性为true来关联Cookie,
若发送cookie在不同源的服务器端,服务器必须支持CORS,设置响应头部字段(Access-Control-Allow-Origin不为*,Access-Control-Allow-Credentials: true),可以实现同域