We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在学习JavaScript预编译之前,先了解一下JavaScript从编译到执行的过程,大致可分为四步:
JavaScript
AST
JavaScript是解释性语言,也就是说,编译一行,执行一行,但js并非上来就进入编译环节,它在编译之前存在预编译过程。
js
js中预编译一般有两种:全局的预编译和函数的预编译,分别发生在script内代码执行前和函数的执行前。
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
test
输出结果:
ƒ a() {} 123 123 ƒ () {}
要想弄明白最终的输出结果,就不得不好好学习一下预编译的详细过程。
在预编译学习中,经常听到一句话:函数声明整体提升,变量声明提升。
这句话可以解决大多数场景下的预编译面试题,但光凭这句话无法吃透预编译的,因此接下来我们来一起捋一下函数预编译的详细流程。
AO(Activation Object)
AO
undefined
学习了函数的预编译过程,就可以回头细细的品味一下上面的案例:
AO :{ a: undefined, b: undefined }
AO :{ a: 1, b: undefined }
AO :{ a: function a() {}, b: undefined, d: function d() {} }
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(){}
全局中不存在形参和实参,所以全局预编译只需处理变量声明和函数声明。
GO(Global Object)
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);
GO
GO/window: { a: undefined, test: function() {} }
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);
GO: { a: undefined, test: function test() {}, c: undefined }
JavaScript中变量如果未经声明就赋值,会默认将变量挂载到window对象上,这也就是所谓的imply global。c就是imply global。
imply global
c
// AO还会存储[[scope]]属性,存储AO的作用域链 AO: { b: undefined, [[scope]]: [TestAO, GO] }
有同学会问,if(a)为false,if内部不会执行,那test的AO中为什么还会有b啊?预编译并不是执行,它只不过把变量、函数等进行提升,只有在执行时,才会设计代码逻辑的判断。
if(a)
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销毁
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);
预编译的题目多数情况下就可以采用以下原则:
如果遇到复杂的情况,就要按照全局预编译的三部曲和函数预编译的四部曲一步一步推导。
最后,在预编译时一定要注意:return、if等代码逻辑判断是在执行时候做的,预编译不管这些,预编译只管变量、形参、函数等。
return、if
Sent from PPHub
The text was updated successfully, but these errors were encountered:
https://github.com/zcxiaobao/everyday-insist/blob/master/21interview/deepJS/precompiler.md
Sorry, something went wrong.
No branches or pull requests
预编译
在学习
JavaScript
预编译之前,先了解一下JavaScript
从编译到执行的过程,大致可分为四步:AST
)。预编译
js
中预编译一般有两种:全局的预编译和函数的预编译,分别发生在script
内代码执行前和函数的执行前。函数预编译
首先来看一个例子:
就以上述例子中的
a
为例,有形参a
,变量a
,函数a
,那test
函数执行时,此时的a
到底是什么呢?输出结果:
要想弄明白最终的输出结果,就不得不好好学习一下预编译的详细过程。
在预编译学习中,经常听到一句话:函数声明整体提升,变量声明提升。
这句话可以解决大多数场景下的预编译面试题,但光凭这句话无法吃透预编译的,因此接下来我们来一起捋一下函数预编译的详细流程。
函数预编译四部曲
AO(Activation Object)
对象AO
的属性名,值赋予undefined
AO
属性名,值赋予函数体案例分析
学习了函数的预编译过程,就可以回头细细的品味一下上面的案例:
AO
,并找形参和变量声明,值赋予undefined
console
的打印结果:全局预编译
全局中不存在形参和实参,所以全局预编译只需处理变量声明和函数声明。
全局预编译三部曲
GO(Global Object)
window
之上,若window
当前已存在当前属性,忽略当前操作,若没有,变量作为属性名,值赋予undefined
。window
上查看,不存在,函数作为函数名,值为函数体案例分析
将函数预编译案例稍微修改,如下:
GO
,变量提升,函数提升,得到GO
如下:a
的值为undefined
,随后a
赋值为1
,所以第二个a
的值为1
注意事项
1. 当函数中出现同样名称的函数名和变量名,编译器真的会先做变量提升再去函数提升吗?这个问题暂时无法验证,如果有大佬知道,希望可以评论告诉一下,谢谢
2. let/const声明的变量应当同样进行了变量提升,只不过它与var声明的变量做了一定的区分
常见面试题分析
题目一
GO
test
执行,生成test
的AO
test
函数执行题目二
答案
题目三
return; 与上面案例的if一样,预编译环节不会处理
答案
题目四
答案
题目五
答案
1
总结
预编译的题目多数情况下就可以采用以下原则:
如果遇到复杂的情况,就要按照全局预编译的三部曲和函数预编译的四部曲一步一步推导。
最后,在预编译时一定要注意:
return、if
等代码逻辑判断是在执行时候做的,预编译不管这些,预编译只管变量、形参、函数等。Sent from PPHub
The text was updated successfully, but these errors were encountered: