欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

js的单线程和预编译是什么?

程序员文章站 2022-05-18 11:18:15
...

js是单线程的语言,也是一门解析型语言,所以他有着属于自己的一种运行方式,我们所说我预编译就是js解析型语言的一种解释。

1. 语言分析

在我们代码执行的时候,我们的浏览器会先将js所有需要执行的代码全面检测一遍,目的是排除低级的语法错误,检测到错误后,一个一个抛出,而不是一下子全部抛出,所以js是单线程,这也是一种编程上的同步。

什么是js的同步呢?

同步:从上往下按顺序执行的程序; 弊端:中间任何一个程序出现问题,下面代码一个都不会执行就像山间小路,一个不能走了,下面全堵住了

        var a = 10 ;
        var b = 20 ;
        console.log(a)
        console.log(c) // c 没有定义,所以报错,下面的 console.log(b) 就不会执行
        console.log(b)

来我们来看一下运行结果
js的单线程和预编译是什么?

什么是js的异步呢?

异步(多个出口):程序的执行顺序是无序的。不会存在等待现象,谁快是先执行
就像高速公路,有多条线,但还是只有一个出口

2. 预编译 (变量提升,函数声明提升 )

在我们学习预编译之前,我先给大家看一个代码:

        console.log(a)

        var a = 10 ;

那么他的输出过结果呢?让我们也来看一下

js的单线程和预编译是什么?
那么大家有没有一点奇怪呢?为什么没有报错,我们上面说js是一门解析型语言,按道理来说是解析一行执行一行,那么在我们输出a的时候,a还是没有定义的,所以他应该是会报错的,这里就要说到我们的预编译环节了。
首先我们先来简单的说一下变量提升,它是属于预编译的一种,我先给大家混个眼熟,然后后面再给大家详细讲解。下面我先讲解一下简略版!这个是简略版,大家且先看看:

    将所有变量提升当前作用域最顶端,赋值为undefined
    将函数声明提升到作用域最顶端
    当变量名与函数名重复的时候 , 变量赋值  会将函数本体覆盖 
    
    console.log(a);

    var a = 10 ;
    // 函数声明 
    function a (){

    }
    console.log(a);

那么下面的代码再浏览器的解析中会这样执行:

	//首先将所有变量提升当前作用域最顶端,赋值为undefined
    var a = undefined;

	// 将函数声明提升到作用域最顶端
    function a (){}// 当变量名与函数名重复的时候 , 变量赋值  会将函数本体覆盖 
   
    console.log(a);//所以a的就从一开始的undefined,变成 function a (){}
    
	a = 10;
	
	console.log(a); // 因为值赋是不会有提升效果,所以最后执行赋值 a = 10

js的单线程和预编译是什么?.
这样是否就可以解决大家的困惑了呢?
我来总结一下那么就是,①变量声明提升 ②函数声明,如果有重名的就将其覆盖,说到这里我们就可以解决大部分基础性问题了,但是我们讲了就要将其讲的透彻,他为什么会是这样的呢?

下面我就为大家来详细讲解预编译!

预编译存在于函数执行的前一刻!记住了,是函数执行的前一刻,大家先别去想上面的代码,先记住我的这句话,因为上面的代码讲解是存在问题的,仅仅是为了让大家了解一下什么叫做变量提升,函数声明提升 。
首先大家先看一下这四句话:

预编译存在于函数执行的前一刻!
1 生成一个AO 对象
2 将函数体中所有变量声明和形参,作为AO对象的属性名,提升到作用域的最顶端,值为undefined
3 将实参值和形参相统一
4 在函数体中,找函数声明,函数体赋予值(只有函数声明才会提升,表达式不会!!)
function fun(x, y) {
			console.log(a)
			console.log(b)
			console.log(x)
			console.log(y)
			console.log(c)
            var a = 0;
            var b = 0;
            function c() {}
            console.log(a)
			console.log(b)
			console.log(x)
			console.log(y)
			console.log(c)
        }
        fun(1, 2)

我们看到fun()执行,也就是函数执行,那么就再fun执行的前一刻会生成一个AO对象(四句话的第一句)

{
(四句话的第二句)
找函数体中所有变量声明和形参,作为AO对象的属性名,提升到作用域的最顶端,值为undefined ,所以 
var a = undefined;
var b = undefined;
var x = undefined;
var y = undefined;

紧接着(四句话的第三句)
将实参值和形参相统一 ,此时AO对象发生改变
var a = undefined; // 不变
var b = undefined; // 不变
var x = 1;
var y = 2;

最后(四句话的第四句)
在函数体中,找函数声明,函数体赋予值(只有函数声明才会提升,表达式不会!!)
我们找到了 function c() {}
所以AO对象再次变化
var a = undefined; // 不变
var b = undefined; // 不变
var x = 1; // 不变
var y = 2; // 不变
var c = function c() {}
当所有的函数声明找到,并且赋完值,那么我们的预编译的AO对象就执行完毕了
}

所以我们再来观看上面的代码:

function fun(x, y) {
            var a = 0;
            var b = 0;
            function c() {}
        }
        fun(1, 2)
他就会变成:
 function fun(x, y) {
		var a = undefined;
		var b = undefined;
		var x = 1; 
		var y = 2; 
		var c = function c() {}
		// 因为var和函数表达式已经提升了,但赋值没有提升,所以就变成下面这样
		console.log(a) // undefined
		console.log(b) // undefined
		console.log(x) // 1
		console.log(y) // 2
		console.log(c) // function c() {}
		a = 0;
		b = 0;
		console.log(a) // 0
		console.log(b) // 0
		console.log(x) // 1
		console.log(y) // 2
		console.log(c) //function c() {}
        }
        fun(1, 2)

这样就是预编译的结束和代码的执行的过程了,那么我们再来讲一下最上面的代码:

    console.log(a);

    var a = 10 ;
    // 函数声明 
    function a (){

    }
    console.log(a);

大家也可以将其看成一个大的预编译过程也就是GO对象生成的过程流程:
第一步:生成一个GO对象
GO{}
第二步:分析变量:go对象内容
go{a:undefined,}
第三步:分析函声明: go 如下
go(a:function a() {})
因为少了形参所以比上面的AO对象预编译要少一步,所以如果大家仅仅把预编译当成 变量提升,函数声明提升 是明显是不足够的!!
希望大家看了之后有所感悟,并附上一道题,当大伙看完后可以自己去联系一番:

        console.log(a)
        function fun(a, b) {
            console.log(a)
            console.log(b)
            console.log(c)
            var a = 5;
            var bb = function () {
                a++
            }
            if(false){
                var c = 10;
            }
            console.log(a)
            bb()
            console.log(a)
        }
        var a = 100
        fun(10,20)
        console.log(a)