JS执行顺序❤️
Posted 学习React
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS执行顺序❤️相关的知识,希望对你有一定的参考价值。
title: JS执行顺序
order: 1
文章目录
一.概念
1.解释型语言执行步骤
预编译
扫描上下文。扫描错误
函数和变量声明的提升
解释执行
一条一条执行代码
2.任务队列event queue
📢异步任务进入event queue任务队列
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)
任务进入执行栈----同步任务还是异步任务----同步的进入主线程,异步的进入Event Table并注册函数。当指定的事情>完成时,Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的Event Loop(事件循环)
📢只要主线程空了,就会去读取"任务队列",这就是javascript的运行机制。这个过程会不断重复。
📢异步任务先进入event table,有结果后进去event queue注册事件,同步任务在主线程执行完后执行事件队列的事件
3.🧡Event Loop
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
4.宏任务、微任务的执行顺序
常见的宏任务:setTimeout, setInterval, setImmediate
常见的微任务:Promise.then
执行顺序:先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。
setTimeout(function(){
console.log('4');
});
new Promise(function(resolve){
console.log('1');
resolve();
}).then(function(){
console.log('3');
});
console.log('2');
5.变量提升
匿名函数声明比变量函数声明提身更高
var foo = function () {
console.log('2');
}
function foo() {
console.log('1');
}
foo();//2
5.作用域的深层次理解
当函数代码执行的前期 会创建一个执行期上下文内部对象 AO
(作用域)
这个内部的对象是预编译的时候创建出来的 因为当函数被调用的时候 会先进行预编译
在全局代码执行的前期会创建一个执行期的上下文的对象 GO
函数作用域预编译
-
创建ao对象
AO{}
-
找形参和变量声明 将变量和形参名 当作
AO
对象的属性名 值为undefined
-
实参形参相统一
-
在函数体里面找函数声明 值赋予函数体
全局作用域的预编译
- 创建
GO
对象 - 找变量声明 将变量名作为
GO
对象的属性名 值为undefined
- 找函数声明 值赋予函数体
6.堆(heap)和栈(stack)
栈会自动分配内存空间,会自动释放。堆动态分配的内层,大小不定也不会自动释放
7.内层的强引用和弱引用
- 强引用:
map
就是强引用、引用的那个对象置为null
了,map
应用的那个地址还存在 - 弱引用:
weakMap
就是弱引用、被引用的对象会自动被垃圾回收
二.执行问题
1.考察宏任务微任务
console.log('1');
setTimeout(function(){
console.log('2');
new Promise(function(resolve){
console.log('3');
resolve();
}).then(function(){
console.log('4');
})
})
new Promise(function(resolve){
console.log('5');
resolve();
}).then(function(){
console.log('6');
})
setTimeout(function(){
console.log('7');
new Promise(function(resolve){
console.log('8');
resolve();
}).then(function(){
console.log('9');
})
})
//输出1,5,6,2,3,4,7,8,9
2.考察promise
function fun2() {
console.log(1)
return Promise.reject('4')//没有catch无法捕获错误,阻止了后面执行
}
async function fun1(){
console.log(2)
await fun2()
console.log(3)
}
fun1()//打印1 2 报错
3.考察闭包
function Foo() {
var i = 0;
return function () {
console.log(i++);
}
}
var f1 = Foo(),
f2 = Foo();
f1();//0、闭包延长作用域
f1();//1
f2();//0、匿名函数所以没有指向同一个地址、指向两个不同函数对象
4.考察作用域
(function(){
//"use strict"在输出b的时候就会Uncaught ReferenceError: a is not defined
var a=b=5;//相等于var a=5;b=5
})();
console.log(b);//5
console.log(a);//Uncaught ReferenceError: a is not defined
5.考察预编译
function fn(a, c) {
var a = 123
console.log(a)//123
console.log(c)//function c
function a() {}
if (false) {
var d = 678
}
console.log(d)//undefined
console.log(b)//undefined
var b = function () {}
console.log(b)///funb
function c() {}
console.log(c)//function c
}
fn(1,2)
6.考察函数传参和地址
let obj = {name:'小明'}
function func(a) {
a.name="小花";
a={age:18};
}
func(obj);
console.log(obj)
三.定时器经典案例
最简单的一种、没有异步
for(var i=0;i<5;i++){//i++`相当于先赋值后`i+1
console.log(i)//立即打印了0,1,2,3,4
}
加上定时器、第二个参数可有可无
for (var i = 0; i < 5; ++i) {
setTimeout(()=>{
console.log(i)//立即打印出5个5
})
}
// js 运行环境为单线程,setTimeout 注册的函数需要等到线程空闲时才能执行,此时 for 循环已经结束,i 值为 5,又因为循环中 setTimeout 接受的参数函数通过闭包访问变量 i,所以 5 个定时输出都是 5。
将 setTimeout 放在立即执行函数中、不传参数i
for (var i = 0; i < 5; i++) {
(function () {
setTimeout(() => {
console.log(i)//隔了1s立即打印5个5
}, i*1000)
})()
}
传参数i
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(() => {
console.log(i)//每秒递增打印
}, i*1000)
})(i)
}
以上是关于JS执行顺序❤️的主要内容,如果未能解决你的问题,请参考以下文章