资讯专栏INFORMATION COLUMN

JavaScript的预编译过程与作用域

ziwenxie / 629人阅读

摘要:词法作用域是一种静态作用域这个例子的结果按静态作用域来分析执行函数,先从函数内部查找是否有局部变量,如果没有,就根据书写的位置,查找上面一层的代码,也就是等于,所以结果会打印。静态作用域,决定的是作用域链的顺序。

博客原文地址:https://finget.github.io/2018/03/01/javascriptPrecompile/
看不明白的地方欢迎提问,有理解的不对的地方希望能够指出来

JavaScript在运行时,要经历三步

语法分析 2.预编译 3.解析执行(自上而下)

JavaScript预编译

先思考这么一个题

function fn (a) {
  console.log(a);

  var a = 123;

  console.log(a);
  
  function a(){};

  console.log(a);

  var b =function (){};

  console.log(b);
}
fn(1);
预编译四部曲

创建AO对象 Activation Object(执行期上下文)

找形参和变量声明,将变量和形参名作为AO属性名,值为undefined

将实参值和形参统一

在函数体里面找函数声明,值赋予函数体

这四步的权重比4>3>2>1,也就是一个覆盖的过程.
函数声明在变量声明的前面

函数声明才存在变量提升。即function a(){};,而var b =function (){};不会提升。
详细分析 先看一个面试中常遇到的问题
console.log(a); // function a(){}
var a = 1;
function a(){};

逐行执行,在AO中是:

AO{
  a: undefied
}
AO{
  a: function(){}
}
换一换
var a = 1;
console.log(a); // 1
function a(){};

逐行执行,在AO中是:

AO{
  a: undefied
}

AO{
  a: function(){}
}

// js是自上而下执行的,先执行var a = 1; 所有AO中的a就被覆盖
AO{
  a: 1
}
按步骤分析文章开头的例子

第一步

AO{
}

第二步

AO{
  a: undefined,
  b: undefined
}

第三步

AO{
  a: 1,
  b: undefined
}

第四步

AO{
  a: function a(){},
  b: undefined
}
解释执行

执行的时候:

AO{
  a: function a(){},
  b: undefined
}
// a = 123;
AO{
  a: 123,
  b: undefined
}
结果:
function fn (a) {
  console.log(a); // function(){}

  var a = 123;

  console.log(a); // 123
  
  function a(){};

  console.log(a); // 123

  var b =function (){};
 
  console.log(b); // function(){}
}
fn(1);
加入window,全局环境
global = 100;
function fn() {
  console.log(global);
  global = 200;
  console.log(global);
  var global = 300;
}
fn();
var global;

在全局环境中会生成一个 GO对象 (Global Object),还是按照上面的四步执行。

GO {
  global: undefined  
}

// 执行到 global = 100 :

GO {
  global: 100  
}

当执行fn之前会先生成一个AO:

AO {
  global: undefined  
}

所以第一次打印globalundefined

这个时候虽然全局变量中的global已经是100,但是fn函数中自己有global变量,所以不会引用全局中的。

当执行到global = 200 :

AO {
  global: 200  
}

所以第二次打印global200

这里这中情况涉及到了‘作用域’。
作用域
这里参考的是https://github.com/mqyqingfeng/Blog/issues/3,可以去详细看看
JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。

词法作用域是一种静态作用域

var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    var value = 2;
    foo();
}

bar();

这个例子的结果:1

按静态作用域来分析

执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。

JavaScript的预编译过程来分析

这里有全局的环境:

// 这里我就写最后一步了
GO {
  value: 1,
  foo: function() {console.log(value)},
  bar: function() {var value = 2; foo()}
}
// 函数在执行时也会生成自己的AO
fooAO{
  // 没有
}
barAO{
  value: 2,
  foo: foo()
}

foo函数中没有定义value,所以它会到它所在的上一层去找,并不会去bar里面找

把这个题做一个小小的改变:
var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    value = 2;
    foo();
}

bar();

这样结果就是2了。

在bar函数中不定义value,而是让它直接改变value的值,他自己没有定义,它也会去全局GO里面找,这样bar里面的value和全局中的value就是同一个内存中的数,当代码执行到value=2,再执行foo()时,全局中的value也是2,所以输出2。在第一种,情况中,var value=2只改变了barAO中的值。

再来,我们再变一下:
function foo() {
    console.log(value);
}

function bar() {
    value = 2;
    foo();
}

bar();

结果还是2.

这次我们没有全局定义value,在bar中也没有定义,而是直接赋值。在JavaScript中如果一个变量未声明就直接赋值,那么这个变量就是个全局变量。所以GO中会定义一个value:2,foo也没有去bar里面找value。

静态作用域,决定的是作用域链的顺序。
最后思考一个问题
function fn(){
  var a = b = 100;
  console.log(window.a);
  console.log(window.b);
}
var a = b =100;
先将100赋值给b,即b=100,此时b没有声明就被赋值。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94879.html

相关文章

  • JavaScript-预编译

    摘要:预编译发生在函数执行前也就是说函数执行时,预编译已经结束。五总结理解预编译需要明白变量函数声明和变量赋值。预编译阶段,只进行变量函数声明,不会进行变量的初始化即变量赋值,所有变量的值都是变量赋值是在解释执行阶段才进行的。 一、JS的概念 JavaScript ( JS ) 是一种具有函数优先的轻量级解释型或即时编译型的编程语言。 二、JS语言特点 2.1 单线程 (1)JavaScri...

    Aldous 评论0 收藏0
  • JS编译之 LHS RHS(你不知道的JavaScript 小记一)

    摘要:关于两个专业术语的讨论起自对你不知道的一书的阅读学习。遇到,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。摘录来自你不知道的。 JS 编译之 LHS RHS 一、前言 最近和朋友聊技术的时候,聊到 LHS RHS,我竟然没听说过 没听说过。。。 于是成功引起了我的好奇心。 关于两个专业术语的讨论起自对《你不知道的JavaScript》一书的阅读学习。 二、编译...

    Cristic 评论0 收藏0
  • javascript作用

    摘要:结果结果函数定义式函数表达式结果预编译是以段为处理单元结果作用域链代码优化在标识符解析的时候,查找全局变量是最慢的,所以尽量使用局部变量。全局变量的被调用两次,查找该变量必须遍历整个作用域链,耗费时间可以把它存储到局部变量里再使用。 作用域属性是在定义函数的时候决定的,不是在调用函数的时候决定。 JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。 ...

    Cheriselalala 评论0 收藏0
  • JavaScript 作用学习笔记

    摘要:每一个运行期上下文都和一个作用域链关联。这个对象将被推入作用域链的头部,这意味着函数的所有局部变量现在处于第二个作用域链对象中,因此访问代价更高了。在代码块内部,函数的所有局部变量将会被放在第二个作用域链对象中。 参考: Javascript作用域原理 理解 JavaScript 作用域和作用域链 JavaScript 作用域 作用域就是变量与函数的可访问范围,即作用域控制着变量与函数...

    赵连江 评论0 收藏0
  • 详解JavaScript函数模式

    摘要:函数表达式又名匿名函数为变量赋的值是函数定义本身。在语言里任何匿名函数都是属于对象。这种情况下,就叫做回调函数。如上面代码示例展示了文档单击事件时以冒泡模式传递给回调函数的特别适用于事件驱动编程,因为回调模式支持程序以异步方式运行。 JavaScript设计模式的作用是提高代码的重用性,可读性,使代码更容易的维护和扩展 在javascript中,函数是一类对象,这表示他可以作为参数传递...

    wwolf 评论0 收藏0

发表评论

0条评论

ziwenxie

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<