js的单线程和预编译是什么?
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的异步呢?
异步(多个出口):程序的执行顺序是无序的。不会存在等待现象,谁快是先执行
就像高速公路,有多条线,但还是只有一个出口
2. 预编译 (变量提升,函数声明提升 )
在我们学习预编译之前,我先给大家看一个代码:
console.log(a)
var a = 10 ;
那么他的输出过结果呢?让我们也来看一下
那么大家有没有一点奇怪呢?为什么没有报错,我们上面说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
.
这样是否就可以解决大家的困惑了呢?
我来总结一下那么就是,①变量声明提升 ②函数声明,如果有重名的就将其覆盖,说到这里我们就可以解决大部分基础性问题了,但是我们讲了就要将其讲的透彻,他为什么会是这样的呢?
下面我就为大家来详细讲解预编译!
预编译存在于函数执行的前一刻!记住了,是函数执行的前一刻,大家先别去想上面的代码,先记住我的这句话,因为上面的代码讲解是存在问题的,仅仅是为了让大家了解一下什么叫做变量提升,函数声明提升 。
首先大家先看一下这四句话:
预编译存在于函数执行的前一刻!
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)
上一篇: js继承之圣杯模式代码实例
下一篇: Android Studio导出jar包