阅读 592

浅谈 JS 里 一句代码是怎么运行的及其编译原理

浅谈 JS 里 一句代码是怎么运行的及其编译原理

  • 用第一性原理来推导出来

前言

可能有很多小伙伴在JS代码运行中只知道一些基本的,像代码是逐行运行的、分为编译阶段和执行阶段。当问到代码是怎么运行的 和 JS代码运行的编译原理时,有的小伙伴就可能理解不是很全面了。这也是很多大厂里面试所会问的问题。下面我会为大家详细的讲解一下在JS中代码是怎样运行的,它的编译原理是什么。

代码运行

首先我们在js文件中输入showName();来看看它的运行结果是什么
输入:

showName();
复制代码

输出:

ReferenceError: showName is not defined
复制代码

哎,系统报错说 未定义showName() 为什么是这样呢?
我们都知道JS是代码文件,导入内存,申请变量空间。在执行中,由于计算机在内存中没有找到showName,所以给出undefined。

然后我们再在后面输入console.log( myName);又会有怎样的结果呢?

showName();
console.log(myName);
复制代码

结果:

 ReferenceError: showName is not defined
复制代码

哎,你会发现跟之前没有区别,为什么是这样呢?
答案显然跟上面是一样的。
那么为什么计算会知道showName有没有被定义呢?
这就要知道任何代码都分为编译阶段 和 执行阶段。计算机在编译阶段的时候就会告诉你showName没有被定义。编译阶段可以检测出代码中那些最严重的的错误。在执行阶段中往往会终止在最开始的问题的地方。所以代码是逐行运行的。

接下来我们在输入:

console.log(myName);// 执行语句
var myName = '极客';// 赋值语句,执行阶段
复制代码

结果会是什么呢?

undefined
复制代码

咦,你会发现没有报错,只有一个undefined。为什么没有报错,怎么没有输出‘极客’呢?怎么只有一个undefined呢?
那就要引入 变量提升的知识点了
在编译阶段,JS中变量的声明会被提升到当前作用域的最顶端(全局作用域)构建一个scope 对象,会把所有的变量放进去并整理。给当前代码一个运行环境(执行上下文)
console.log(myName)是一条执行语句,所以在编译阶段,myName会被提升到scope中去。在执行阶段找得到myName,所以没有报错,只有undefined。 其实它实际执行顺序是这样的:

var myName;// 编译阶段
console.log(myName);
myName = '极客';// 执行阶段
复制代码

又会有人提问了,如果按照这个执行顺序的话,为什么输出结果不是null呢?
这是因为在js中,js的类型是由值决定的。

现在相信大家对普通变量声明的变量提升有了一定的了解,那么我们接下来了解一下函数的变量提升,看看这两者的区别。

函数的变量提升

接下来看一下下面的例子:

showName();
console.log(myname);
var myname = '极客时间';
function showName(){ // 函数与普通变量声明来做对比 函数可以来调用
    console.log('函数showName被执行了');
}
复制代码

你觉得它会输出什么呢?是不是相当于这样执行:

var myname;
showName();
console.log(myname);
myname = '极客时间';
function showName(){ // 函数与普通变量声明来做对比 函数可以来调用
    console.log('函数showName被执行了');
}
复制代码

然后输出:

undefined
undefined
复制代码

或者是它会报错?其实都不然
正确结果是:

函数showName被执行了
undefined
复制代码

为什么是这个结果呢?

1.png 因为这是一个完整的函数声明,函数在js中是排位第一的,在js中也被叫作“一等公民”。函数跟变量不同的地方在与函数拥有再次创建执行上下文的权利。为什么会有这种特权呢?因为函数在会创建一个函数作用域(局部作用域)

那么,大家再来看下面代码:

showName();
console.log(myname);
var myname = '极客时间';
// 函数表达式
var showName = function() {
    console.log('函数showName被执行了');
}
复制代码

这应该输出什么呢?
输出:

TypeError: showName is not a function
复制代码

怎么会报错呢?
一定会报错!!!JS的类型是由值决定的,由于编译阶段var showName变量提升到了最顶端,并且赋予了一个undefined,而showName();是一个函数调用,与作用域中的showName不匹配,所以它一定会报错。大家可以看图再理解一下。

2.jpg

3.jpg

总结

  1. 代码一定是逐行运行的
  2. 分为编译阶段和执行阶段,有先后顺序
  3. 区分哪些代码是在编译阶段运行的,比如变量声明
  4. 作用域的概念 scope是一个对象 目的是去存放当前代码,去实现等下执行上下文中的所有变量的引用
  5. 在执行代码时,会涉及到变量的查找
    var myName; //编译阶段 JS变量的类型由值决定 undefined
    myName = 'yjf'; //执行阶段
  6. 所谓的变量提升,是指在JS代码执行中,JavaScript引擎中(v8)把变量的声明部分和函数的声明部分提升到代码开头的行为,变量提升后,会给变量设置默认值undefined。

思考

showName()
var showName = funcation() {
    console.log(2);
}
funcation showName() {
    console.log(1);
}
showName()
复制代码
  • 以执行上下文环境、编译阶段、执行阶段,为什么答案是这个?
  • 变量提升的图画出来

答案:

q.png

文章分类
前端
文章标签