javascript 01 做用域

javascript-01 做用域

在傳統編譯語言的流程中,程序中的一段源代碼在執行以前會經歷三個步驟,統稱爲「編譯」。javascript

編譯原理

分詞/詞法分析(Tokenizing/Lexing)

分詞(tokenizing)和詞法分析(Lexing)之間的區別是很是微妙、晦澀的, 主要差別在於詞法單元的識別是經過有狀態仍是無狀態的方式進行的。簡 單來講,若是詞法單元生成器在判斷 a 是一個獨立的詞法單元仍是其餘詞法 單元的一部分時,調用的是有狀態的解析規則,那麼這個過程就被稱爲詞法 分析java

例如:面試

var a = 12;

這段程序一般會被分解成 爲下面這些詞法單元:var、a、=、12 、;。 ##解析/語法分析(Parsing)
這個過程是將詞法單元流(數組)轉換成一個由元素逐級嵌套所組成的表明了程序語法 結構的樹。這個樹被稱爲「抽象語法樹」 -- 簡稱 AST
AST: AST 抽象語法樹編程

代碼生成

將 AST 轉換爲可執行代碼的過程稱被稱爲代碼生成segmentfault

指的是源代碼語法所對應的樹狀結構。也就是說,對於一種具體編程語言下的源代碼,經過構建語法樹的形式將源代碼中的語句映射到樹中的每個節點上。數組

理解編譯原理過程

引擎: 從頭至尾負責整個 JavaScript 程序的編譯及執行過程,
編譯器: 負責語法分析及代碼生成
做用域: 負責收集並維護由全部聲明的標識符(變量)組成的一系列查詢,並實施一套很是嚴格的規則,肯定當前執行的代碼對這些標識符的訪問權限。瀏覽器

理解程序的執行過程編程語言

var a = 12;

若是這是面試題,面試官問我解析這個語句的過程。
我會說,爲一個變量 a 分配一個內存,而後將 12 的值,放到這個變量裏面。顯然沒有毛病,可是面試官可能想聽的並非這個,假如編譯器是面試官的話,以上的過程就並不徹底正確函數

var一個變量的時候a,編譯器會問做用域,這個變量名稱是否存在於做用域中,若是爲是的話,編譯器會忽略,繼續編譯,但若是爲否的話,編譯器會要求做用域,在當前的做用域中聲明一個變量命名爲 a。
賦值過程一樣也是相同的道理 a = 12
引擎運行時會首先詢問做用域,在當前的做用域集合中是否存在一個叫做 a 的 變量。若是是,引擎就會使用這個變量;若是否,引擎會繼續查找該變量
若是引擎最終找到了 a 變量,就會將 12 賦值給它。不然引擎就會舉手示意並拋出一個異常!
---2020 年 12 月 總結--
參考地址:
你不知道的 JavaScript
http://www.javashuo.com/article/p-bwiozzzq-h.html
https://blog.csdn.net/zhixingheyi_tian/article/details/80040003.net

詞法做用域

簡析做用域

function foo(a) {
  var b = a * 5;
  function bar(c) {
    console.log(a, b, c);
  }
  bar(b * 3);
}
foo(2); //2 10 30

有時候會有相似的面試題,當看到關於簡析題時候,要先分析下咱們的代碼中變量,以及做用域。先剝洋蔥,要看咱們的代碼的切套關係。

  • 01 最外層是咱們的全局做用域,能看到只有foo()
  • 02 中心層咱們能看到的標識有 做爲參數的a 被定義的bbar
  • 03 心裏層能夠看到的標識只有 做爲參數的c

如今咱們把代碼放到運行環境中,看咱們的代碼是如何執行的

瀏覽器引擎看到 console.log(a, b, c);的聲明。並開始找所打印的 a,b,c,變量。

  • a 首先咱們在心裏層找 a,也就是 bar()中,若是有就進行對應操做或運算,若是沒有就去上一層找 ,在中心層找到了咱們的a,咱們的 a 是做爲外層foo()的一個參數,全部 a 就是咱們傳參的 2

  • b 同理 在中心層找到咱們 b, var b = a * 5; 而 a 已是咱們傳參的 a = 2 了,因此 b 就等於 10

  • c 同理

全局做用域

var outerVar = "outer";
function fn() {
  console.log(outerVar);
}
fn(); //outer

最外層函數定義的變量擁有全局做用域,對於內部的函數來講,是能夠訪問的

局部做用域

function fn() {
  var innerVar = "inner";
  console.log(innerVar); //inner
}
fn();
console.log(innerVar); // ReferenceError: innerVar is not defined

函數做用域

由上面的的代碼簡析過程,咱們得知,每一個函數都會生成一層洋蔥層.

function foo(a) {
  var b = 2; // 一些代碼
  function bar() {
    // ...
  }
  // 更多的代碼
  var c = 3;
}
bar(); // ReferenceError: bar is not defined
console.log(a, b, c); //ReferenceError: a,b,c is not defined

咱們在全局沒法訪問函數內部的所定義的變量,可是在函數內部卻能夠訪問,foo(..) 的內部都是能夠被訪問的,一樣在 bar(..) 內部也能夠被訪問,【前提是 bar()中沒有與 foo()同名的變量】

function foo(a) {
  var b = 2; // 一些代碼
  console.log(a);
  function bar() {
    // ...
  }
  // 更多的代碼
  var c = 3;
  console.log(a, b, c); //undefined 2 3
}
foo();

由於 a 是參數,咱們並未傳參,因此是 undefined。

就是由於這個特性,咱們能夠將一些私有函數,和對象,放到函數內部,從最小特權原則,

function doSomething(a) {
  b = a + doSomethingElse(a * 2);
  console.log(b * 3);
}
function doSomethingElse(a) {
  return a - 1;
}
var b;
doSomething(2); // 15

---待續

相關文章
相關標籤/搜索