javascript之预编译学习(附带多个面试题)(代码片段)

战场小包 战场小包     2022-12-10     642

关键词:

前言

在学习JavaScript预编译之前,先了解一下JavaScript从编译到执行的过程,大致分为四步:

  1. 词法分析
  2. 语法分析:检查代码是否存在错误,若有错误,引擎会抛出语法错误。同时会构建一颗抽象语法树(AST)。
  3. 预编译
  4. 解释执行

预编译

JavaScript是解释性语言,也就是说,编译一行,执行一行,但js并非上来就进入编译环节,它在编译之前存在预编译过程。

js中预编译一般有两种:全局的预编译和函数的预编译,分别发生在script内代码执行前和函数的执行前。

函数预编译

首先来看一个例子:

function test(a) 
    console.log(a);       
    var a = 123;
    console.log(a);       
    function a() 
    console.log(a);       
    var b = function() 
    console.log(b);       
    function d() 

test(1)

就以上述例子中的a为例,有形参a,变量a,函数a,那test函数执行时,此时的a到底是什么呢?

输出结果:

ƒ a() 
123
123
ƒ () 

要想弄明白最终的输出结果,就不得不好好学习一下预编译的详细过程。

在预编译学习中,经常听到一句话:函数声明整体提升,变量声明提升

这句话可以解决大多数场景下的预编译问题,但光凭这句话无法理解透预编译,接下来我们来一起捋一下函数预编译的大致流程。

函数预编译四部曲

  1. 预编译开始,会建立AO(Activation Object)对象
  2. 找形参和变量声明,使其作为AO的属性名,值赋予undefined
  3. 实参和形参相统一(将实参值赋值给形参)
  4. 找函数声明,函数名作为AO属性名,值赋予函数体

案例分析

学习了函数的预编译过程,就可以回头细细的品味一下上面的案例:

  1. 先建立AO,并找形参和变量声明
    AO :
        a: undefined,
        b: undefined
    
    
  2. 形参实参相统一
    AO :
        a: 1,
        b: undefined
    
    
  3. 找函数声明,值赋予函数体
    AO :
        a: function a() ,
        b: undefined,
        d: function d() 
    
    
  4. 预编译过程结束,挨着分析一下console的打印结果:
    第一个console.log(a); // 此时AO中a的值为function a()  
    执行赋值操作:
        a = 123 // AO中的a值修改为123
        第二个console.log(a) // 123
        第三个console.log(a) // 123
        b = function()  // AO中的b值修改为function b()
        console.log(b) // function b()
    

全局预编译

全局中不存在形参和实参,所以只需处理变量声明和函数声明

全局预编译三部曲

  1. 生成GO(Global Object)
  2. 找变量声明,由于全局变量默认挂载在window之上,若window当前已存在当前属性,忽略当前操作,若没有,变量作为属性名,值赋予undefined
  3. 找函数声明,函数类似与变量,先去window上查看,不存在,函数作为函数名,值为函数体

案例分析

将函数预编译案例稍微修改,如下:

// test部分的结果与函数部分相同,再次只分析全局部分
console.log(a);
var a = 1;
console.log(a);
function test(a) 
    console.log(a);
    var a = 123;
    console.log(a);
    function a() 
    console.log(a);
    console.log(b);
    var b = function() 
    console.log(b);

test(2);
  1. 生成GO,变量提升,函数提升,得到GO如下:
    GO/window: 
        a: undefined,
        test: function() 
    
    
  2. 因此第一个a的值为undefined,随后a赋值为1,所以第二个a的值为1

test中定义了变量a,因此打印的a为自身AO中的值。如果test中没有定义a,就会沿着作用域链,当GO中查找a。

注意事项

1. 当函数中出现同样名称的函数名和变量名,编译器真的会先做变量提升再去函数提升吗?查看了一些大佬的博客,当出现变量名和函数名相同时,变量提升应该会被忽略掉,不会做重复的无用之功

2. let/const声明的变量应当同样进行了变量提升,只不过它与var声明的变量做了一定的区分

常见面试题分析

题目一

function test() 
console.log(b);
if (a) 
    var b = 100;

console.log(b);
c = 234;
console.log(c);

var a;
test();
a = 10;
console.log(c);
  1. 生成GO
    GO: 
        a: undefined,
        test: function test() ,
        c: undefined
    
    

    JavaScript中变量如果未经声明就赋值,会默认将变量挂载到window对象上,这也就是所谓的imply global。c就是imply global

  2. test执行,生成test的AO
    // AO还会存储[[scope]]属性,存储AO的作用域链
    
    AO: 
        b: undefined,
        [[scope]]: [TestAO, GO]
    
    

    有同学会问,if(a)为false,if内部不会执行,那test的AO中为什么还会有b啊?预编译并不是执行,它只不过把变量、函数等进行提升,只有在执行时,才会设计代码逻辑的判断。

  3. 分析test函数执行
    console.log(b) // AO中b为undefined
    if (a) // AO中无a,沿[[scope]]找到GO中的a,值为undefined
    b = 100; // 不执行
    console.log(b) // undefined
    c = 234; // AO中没有c属性,沿[[scope]]找到GO中的c修改为234
    console.log(c) // 打印的是GO中的c,234
    // test执行完毕,AO销毁
    
  4. 分析剩余代码:
    a = 10; // GO中的a修改为10
    console.log(c) // GO中c值为234,234
    

题目二

var foo = 1;
function bar() 
    console.log(foo);  
    if (!foo) 
        var foo = 10;
    
    console.log(foo); 


bar();

答案

undefined
10

题目三

var a = 1;
function b() 
    console.log(a);  
    a = 10;
    return;
    function a()  

b();
console.log(a); 

return; 与上面案例的if一样,预编译环节不会处理

答案:

function a()  
1

题目四

console.log(foo); 
var foo = "A";
console.log(foo)  
var foo = function () 
    console.log("B");

console.log(foo); 
foo(); 
function foo()
    console.log("C");

console.log(foo)  
foo(); 

答案:

ƒ foo()
    console.log("C");

A
ƒ () 
    console.log("B");

B
ƒ () 
    console.log("B");

B

题目五

var foo = 1;


function bar(a) 
    var a1 = a;
    var a = foo;
    function a() 
        console.log(a); 
    
    a1();


bar(3);

答案:

1

总结

预编译的题目多数情况下就可以采用以下原则:

  • 函数声明,整体提升
  • 变量声明,声明提升

如果遇到复杂的情况,就要按照最原始的方式一步一步的解决问题。

最后,在预编译时一定要注意:return、if等代码逻辑判断是在执行时候做的,预编译不管这些,预编译只管变量、形参、函数等。

《2w字大章38道面试题》彻底理清js中this指向问题(代码片段)

...this都不成问题。学习this之前,建议先学习以下知识:JavaScript之预编译[1]JavaScript之手撕new[2]JavaScript之手撕call/apply[3]JavaScript之静态作用域与动态作用域[4]JavaScript之手撕数组高阶函数[5]在文章的最开始,陈列一下本篇文章... 查看详情

javascript经典面试题详解(代码片段)

...需要引擎,编译器,作用域的配合操作,(引擎负责整个JavaScript程序的编译及执行过程,编译器负责词法分析及代码生成等,作用域负责收集并维护由所有声明的标识 查看详情

2022年最新的android面试大厂必考174题(附带详细答案)(代码片段)

...互联网行业竞争也越来残酷,我们工程师只有不断地学习,提升自己的能力才更保障你拿到更好的薪水,进入理想的企业(阿里、字节、腾讯、美团等等…)但目前很多人出现了一系列的疑惑问题就是:以... 查看详情

史上最全python面试题详解(附带详细答案(持续更新))(代码片段)

1、简述解释型和编译型编程语言?概念:编译型语言:把做好的源程序全部编译成二进制代码的可运行程序。然后,可直接运行这个程序。解释型语言:把做好的源程序翻译一句,然后执行一句,直至结束!区别:编译型语言... 查看详情

前端面试套餐:vue面试题总结+javascript前端经典面试题+100道css面试题

前言在逛技术网站时,在一篇文章下面看到了一条评论,这条评论仁者见仁智者见智。我单独放出来说,是因为他讲到了基础。基础是重要的一个学习环节,在面试中很多问题都不难,都是基础上内容,回... 查看详情

verilog之预编译(代码片段)

verilog之预编译1、基本作用预编译,就是在verilog进入编译器前的准备工作。一般是完成一些文件的调用,一些编译器的设置,一些参数的定义。一般使用include,define,timescale就可以实现。比较灵活的使用是ifdef和endif的插入,可... 查看详情

javascript面试题总结(附答案)-蛙课网

...答案,以供有面试需要的java程序员学习使用。1、什么是JavaScript?答:JavaScript是客户端和服务器端脚本语言,可以插入到HTML页面中,并且是目前较热门的Web开发语言。同时,Jav 查看详情

kafka教程分享,附带学习经验

三面头条面试岗位是后台研发工程师,地点选择了上海,通过大佬内推,跳过死亡笔试,加上疫情期间,所以直接视频面,从3点开始,断断续续到晚上8点结束。一共三轮技术面试,每一轮都要写代... 查看详情

前端面试题整理---js基础

为了督促自己学习,整理了一下前端的面试题JavaScript:   JavaScript中如何监测一个变量是String类型?typeof(obj)==="string";typeofobj==="string";obj.constructor===string  JS中清除字符串空格的方法    方法一:使用正... 查看详情

最强面试题整理第一弹:python基础面试题(附答案)(代码片段)

...数是第一类对象。Python代码编写快,但是运行速度比编译型语言通常要慢。Python用途广泛,常被用走"胶水语言",可帮助其他语言和组件改善运行状况。使用Python,程序员可以专注于算法和数 查看详情

资源|25个机器学习面试题,期待你来解答

....weixin.qq.com/s/aL-gZ9LzYCkpTcsQF1FZjA  选自Medium机器之心编译参与:GeekAI、王淑婷、思源 机器学习有非常多令人困惑及不解的地方,很多问题都没有明确的答案。但在面试中,如何探查到面试官想要提问的知识点就显得非常... 查看详情

c语言面试题总结

1 预处理问题1:什么是预编译?何时需要预编译?答:预编译又称预处理,是整个编译过程最先做的工作,即程序执行前的一些预处理工作。主要处理#开头的指令。如拷贝#include包含的文件代码、替换#define定义的宏、条件编... 查看详情

javascript学习笔记(代码片段)

Javascript学习笔记ES6一、ES6简介二、ES6的新增语法①、let关键字②、let的经典面试题③、const关键字④、let、const、var的区别⑤、解构赋值⑥、箭头函数⑦箭头函数面试题⑧、剩余参数三、ES6的内置对象扩展①、Array的扩展方法②... 查看详情

java面试题,异常架构篇⭐(精心梳理⭐附带阿里巴巴开发手册+异常习题及答案)(代码片段)

...理),你准备好接招了吗?求怕累!内容最后附带阿里巴巴开发手册,整合不易,还请大家认真阅读,并一键三连加关注,给博主一些支持,谢谢了!笔芯❤文章目录Java异常架构与异常关键字一.... 查看详情

最强面试题整理第二弹:python进阶面试题(附答案)(代码片段)

...)Python进阶(已完成)Python后台开发爬虫机器学习对每道面试题会附带详细 查看详情

最强面试题整理第二弹:python进阶面试题(附答案)(代码片段)

...)Python进阶(已完成)Python后台开发爬虫机器学习对每道面试题会附带详细 查看详情

最强面试题整理第二弹:python进阶面试题(附答案)(代码片段)

...)Python进阶(已完成)Python后台开发爬虫机器学习对每道面试题会附带详细 查看详情

前端面试题

...题--综合前端面试题--Vue前端面试题--HTML+CSS前端面试题--JavaScript(一)前端面试题--JavaScript(二)前端面试题--webpack前端面试题--小程序JavaScript中的数据类型JavaScript中的数据类型JavaScript中的原型与原型链JavaScript声明变量详解GitHub&n... 查看详情