JavaScript 之 预编译 作用域,作用域链

第一次写博客,本来是学习jQuery遇到闭包问题,发现并没有理解闭包,发现闭包牵扯的知识点太多。复习了一遍(发现自己该记住的全忘了)写在博客里,自己也是小白,希望大神们指点迷津,必将感激不尽。

我们知道JavaScript有两个特点:单线程,解释型语(翻译一行,解释一行)。但其实翻译一行,解释一行是最后一部才这样做,在这之前会有一个语法分析:通篇扫描看有没有语法错误,但不执行,通篇扫描之后,会进行 预编译 然后 -->解释一行,执行一行。这就是我们所说的js运行三部曲:语法分析     预编译     解释执行

没错,预编译开始啦!

test()                        //VM129:1 Uncaught ReferenceError: test is not defined

console.log(a)         // VM118:1 Uncaught ReferenceError: a is not defined

test();     //456

function test(){

  console.log(456);

};

console.log(a);      //undefined

var a = 123;

上面四段代码当我们执行前两个的时候报错,浏览器告诉我们 test 和 a 没有被定义,而后两段代码并没有报错,这就是预编译。

在学习预编译的时候我们总是记住一句话:函数声明整体提升,变量    声明提升。也就是说预编译会把整个函数放在最最前面。而变量 声明提升是什么意思呢?

var a = 123;其实是变量声明和赋值的两个过程;1)var a;     2)a = 123;预编译只把声明提升在最前面

console.log(a); //undefined

var a = 123;

--->

var a;

console.log(a); //undefined

a = 123;

--------------------------

test(); //456

function test(){

console.log(456);

}

--->

funciton test(){

console.log(456);

}

test(); // 456

但是光记住这两句话并不能解决所有的问题。

看一下下面的

console.log(a);

function a(){

}

var a = 123;

想一下打印的是什么?

居然是

? a(){

}

再看看下面的更复杂的

console.log(a);

function a(a){

var a = 234;

var a = function(){

}

a();

}

var a = 123;

这个打印出来是什么呢?

? a(a){

var a = 234;

var a = function(){

}

a();

}

这是为什么呢?

下面来讲一下预编译: imply global 暗示全局变量:即任何变量。如果变量未经申明就赋值,此变量就为全局对象(window)所有。

a = 123;如果 var a = b = 123;在函数里a就是函数内部变量,b则是全局变量。

一切声明的全局变量,全是window的属性(window 就是全局的域):var a = 123;-----> window.a = 123;

使用var声明的变量会自动被添加到最接近的环境中。

预编译发生在函数执行的前一刻。

预编译四部曲:

1.创建AO对象/活动对象(activation object)(执行期上下文)

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

3.将实参值和形参统一

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

由此我们便知道上面的那两个例子打印的为什么是那样的。

下面我们来看下更复杂的

function fun(a) {

  console.log(a);

  var a = 123;

  console.log(a);

  function a() { }

  console.log(a);

  var b = function () { }

  console.log(b);

  function d() { }

}

fun(1);

-->

1.生成AO对象

AO{

}

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

AO{

  a: undefined;

  b:undefined;

}

3.将实参值和形参统一

AO{

  a: 1;

  b:undefined;

}

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

AO{

  a: function a(){};

  b:undefined;

  d:function(){}

}

预编译结束

函数执行 AO就像一个创库一样,函数执行期间里面的仓库也会变化

AO{

  a: function a(){};

  b:undefined;

  d:function(){}

}

function fun(a) {

  console.log(a);      // ? a() { }

  var a = 123;

  console.log(a);      //123

  function a() { }

  console.log(a);      //123

  var b = function () { }

  console.log(b);      //? () { }

  function d() { }

}

--------------------------

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

GO{

a:undefined;

test:function test(){}

}

AO{

b:undefined;

}

------------

执行函数

GO{

a:undefined;--->10

test:function test(){}

c:234

}

AO{

  b:undefined;

}

function test(){

  console.log(b); //undefined

  if(a){

    var b = 100;

  }

  console.log(b); //undefined

  c = 234;

  console.log(c); //234

}

var a;

test();

a = 10;

console.log(c); //234

作用域 作用域链

function test(){};

我们知道一个函数就像一个房子一样,这个房子形成单独的域,里面能看到外面的,外面的看不到里面的,我们可以把函数生成的空间叫做作用域那这个作用域到底是什么呢?

这个作用域是因函数产生而产生的,每个对象都有属性和方法,函数(function)也是一种特殊的对象,函数可以有test.name test.prototype ...这些是可以访问的

还有一些属性是不可以访问的隐式属性仅供JavaScript引擎处理。 比如[[scope]]:指的就是我们所说的作用域链,其中存储了执行期上下文的集合。

为什么时集合呢?作用域链:是[[scope]]中所存储的执行期上下文的集合,这个集合呈现链式连接,我们把这种连接叫做作用域链。

作用域链本质上是一个指向变量对象的指针列表,他只是引用,但不包含实际变量对象。

test.[[scope]]这里面存的就是作用域。系统会根据内部的原理去定期调用scope。

上面提到了执行期上下文(前面作用域也提到的AO对象就是这个):当函数执行的前一刻的时候,会创建一个称为执行期上下文的内部对象(AO activation object)。一个执行期上下文定义了一个函数执行时的环境

函数每次执行时对应的上下文都是独一无二的 test(); test();一样的函数但是执行期上下文并不相同,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,他所产生的执行上下文会销毁。

看一下下面的例子

function a(){}

var glob = 100;

a();

当a函数被定义 a.[[scope]]---> 0:GO{} 因为a函数在全局作用域里,所以他的第一位存的时GO

当a执行执行 a.[[scope]]---> 0:AO{}

1:GO{}

----------------------------------------------

function a(){

  function b(){

    function c(){}

    c();

  }

  b();

}

a();

a defined a.[[scope]]  ---> 0 : GO

a doing a.[[scope]]   ---> 0 : a AO

             1 : GO

b defined b.[[scope]]  ---> 0 : a AO

            1 : GO

b doing b.[[scope]]   ---> 0 : b AO

            1 : a AO

            2 : GO

c defined c.[[scope]]  ---> 0 : b AO

            1 : a AO

            2 : GO

b doing c.[[scope]]   ---> 0 : c AO

            1 : b AO

            2 : a AO

             3 : GO

原文地址:https://www.cnblogs.com/jiaobaba/p/10420531.html

时间: 2024-10-08 15:22:43

JavaScript 之 预编译 作用域,作用域链的相关文章

javascript的预编译和执行顺序

最近在复习javascript的事件处理时发现了一个问题,然后也是我来写javascript的预编译和执行顺序的问题 代码: 代码一<html> <head> <title>事件处理</title> <meta http-equiv="content-type" content="text/html;charset=utf-8"/> <script type='text/javascript'>

预编译、作用域链和闭包理解

在理解预编译之前,首先了解一下JS的解析过程:JS引擎在解析脚本的过程分为两个阶段,预编译和执行,首先预编译然后再从上之下一行一行的执行代码.其次,要了解作用域,作用域是一个变量或者函数能够使用的空间,分为全局作用域和局部作用域,全局变量的作用域为全局作用域,局部变量(函数内部或者ES6块内部的变量)的作用域为局部作用域. 预编译分为全局预编译和函数预编译,我们来详细的了解一下. 全局预编译分为两步: 1.生成全局对象:(Global Object)  GO = {   }: 2.找变量声明和关

JavaScript的预编译和执行

JavaScript引擎,不是逐条解释执行javascript代码,而是按照代码块一段段解释执行.所谓代码块就是使用<script>标签分隔的代码段. 整个代码块共有两个阶段,预编译阶段和执行阶段 一.编译阶段 对于常见编译型语言(例如:Java)来说,编译步骤分为:词法分析->语法分析->语义检查->代码优化和字节生成. 对于解释型语言(例如JavaScript)来说,通过词法分析和语法分析得到语法树后,就可以开始解释执行了. (1)词法分析是将字符流(char strea

还原真实,javascript之预编译 / 预解析

今天在群里吹水时,有群友提出一个问题.我一看很简单,就立马给出了答案:因为存在变量提升,所以输出undefined.本以为无人反驳,可确招来口诛笔伐.作为写实派的我,一贯以来坚持真实是我的使命,岂能容忍这等虚头巴脑的言论攻击.遂以此文记之,本在还原真实.奈何文笔拙劣,恐表述不当,误人子弟.若有不当之处,还请众佬及时斧正,以正视听. 就是下面这段代码: var obj = { say: function () { console.log(obj) }() } // 输出 undefined 为什么

JavaScript函数——预编译

四部曲 创建AO对象 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined. 将实参值和形参值统一 在函数体内找函数声明,值赋予函数体. 权重按顺序依次增加.以下例子即可体现上述规则 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); //f a(){} /

JavaScript作用域原理(二)——预编译

JavaScript是一种脚本语言, 它的执行过程, 是一种翻译执行的过程.并且JavaScript是有预编译过程的,在执行每一段脚本代码之前, 都会首先处理var关键字和function定义式(函数定义式和函数表达式). 一.变量执行之前,会被赋为undefined <p id="scope2" style="color:red"></p> function echo(p, html) { p.innerHTML += html + '&l

javascript运行过程中的“预编译阶段”和“执行阶段”

javascript相对于其它语言来说是一种弱类型的语言,在其它如java语言中,程序的执行需要有编译的阶段 而在javascript中也有类似的“预编译阶段”(javascript的预编译是以代码块为范围<script></script>,即每遇到一个代码块都会进行  预编译>执行), 首先科普下javascript中的两种声明方式,var和function,前者声明的是变量,后者声明的是方法 在预编译中,javascript对这两种声明做出了两种处理方案 <scri

JS的预编译和执行顺序 详析

原文:JS的预编译和执行顺序 详析 最近在复习javascript的事件处理时发现了一个问题,然后也是我来写javascript的预编译和执行顺序的问题   代码:   复制代码 代码一 <html>   <head>     <title>事件处理</title>     <meta http-equiv="content-type" content="text/html;charset=utf-8"/>

JS的预编译和执行顺序 详析(及全局与局部变量)

最近在复习javascript的事件处理时发现了一个问题,于是总结一下:javascript的预编译和执行顺序的问题:   <html> <head> <title>事件处理</title> <meta http-equiv="content-type" content="text/html;charset=utf-8"/> <script type='text/javascript'> //页