JavaScript的預編譯歷程與作用域

博客原文地點:
https://finget.github.io/2018/03/01/javascriptPrecompile/

看不邃曉的處所迎接發問,有明白的不對的處所願望能夠指出來

JavaScript在運行時,要閱歷三步

  1. 語法剖析 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);

預編譯四部曲

  1. 建立AO對象 Activation Object(實行期上下文)
  2. 找形參和變量聲明,將變量和形參名作為AO屬性名,值為undefined
  3. 將實參值和形參一致
  4. 在函數體內里找函數聲明,值給予函數體

這四步的權重比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()
}

《JavaScript的預編譯歷程與作用域》

foo函數中沒有定義value,所以它會到它地點的上一層去找,並不會去bar內里找

把這個題做一個小小的轉變:

var value = 1;

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

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

bar();

如許效果就是2了。
《JavaScript的預編譯歷程與作用域》

在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沒有聲明就被賦值。

    原文作者:FinGet
    原文地址: https://segmentfault.com/a/1190000014811064
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞