(大廠面試題)使用第一性原理推導—JS中代碼是怎么運行的
前言
自從接觸“第一性原理”,這個詞在網上被吹得神乎其神,可是它到底是什么?我還沒認真考究過,直到今天,通過瀏覽各位大佬的博文,對第一性原理有了點自己的理解,今天就和大家分享以下兩點:
- 我對“第一性原理”的理解
- 從第一性原理的角度聊一聊JS中代碼是怎么運行的
什么是第一性原理
當我看到下面馬斯克這段話的完整譯文,才終于把握到了“第一性原理”的實質,這令人膜拜的“第一性原理”,不就是“解耦合(decoupling)”嗎?

-
要搞清楚這個概念,我們不妨試想,我們作為開發人員,希望改進一款開源軟體產品的功能,你會怎么做?
你會打開一個新的空白源代碼檔案,從頭開始,一行行的寫代碼嗎?
基本上不會,那你會怎么做呢?
你會讀現有軟體的源代碼,把新的功能實作補充或更新到對應的位置,提交合并請求
在這個程序中,我們都是把前人做的東西作為基礎層,而后再在這個層次上,去疊加新的內容,
-
如果大家還沒有理解,我們不妨再思考一個問題:為什么現在資料科學這么火熱?Python、R和機器學習框架為何這么受到歡迎?甚至讓很多非IT人士也在樂此不疲地渴望學習、應用它們?
因為有許許多多的開發者,已經為你寫好了實作資料科學作業的各項基本功能,相關的軟體包已有成千上 萬,而且每天還在不停快速涌現,你根本不需要了解哪些功能究竟是如何實作出來的,只要會搜軟體、查檔案,直接“拿來主義”呼叫就能實作酷炫繁復的功能,方便得令人發指,
-
現在,大家再試想一下:如果之前的前提都不成立了呢?假設你目前的作業做所依賴的基礎層級存在問題呢?
這就比如“品牌假貨”的存在,大家都痛恨假貨,但是原本品牌的存在,就會減低大家識別商品質量的成本,如果一個案例中,兩種事物同時出現,總會被我們腦補為必然的關聯,于是就耦合在一起了,你不難感受到,耦合的結果是非常不利于創新的,而所謂的“第一性原理”,即是一種“解耦合”的思維方式
如何使用“第一性原理”思考-JS中代碼是怎么運行的
- 由第一性原理,可推出一下結論和問題
- 代碼一定是逐行運行的
- 分為編譯階段和執行階段,先后順序
- 編譯階段發生了什么?
- 執行階段執行了什么?
- 代碼運行時,變數是如何查找的?
showName()//函式的執行
var showName = function(){ // 拋去后面的函式賦值看,這其實就是一個變數宣告加一個變數賦值
console.log(2)
}
function showName(){ //函式的宣告
console.log(1)
}
showName()//函式的執行(呼叫)
- 接下來通過以這段代碼的運行程序為例,探討所有的問題
- 要探討編譯階段和執行階段都發生了什么,不妨將上述代碼分為以下兩個部分:

- 由上圖所示,我們可以看到這段代碼真正的運行順序:先進行函式和變數宣告提升,在編譯階段進行變數和函式宣告,創建scope物件,執行背景關系{showName},為執行階段準備好執行環境,在執行階段執行函式呼叫、變數賦值操作,
- 通過以上的推導,在JS中處于基礎層級最重要的幾個概念也就浮出來水面,下面帶大家粗略的了解下這幾個概念
宣告提升
-
什么是宣告提升?
- 是指在JS代碼執行中,Javascript引擎(V8),把變數的宣告部分和函式的宣告部分提升到代碼開頭的行為,變數提升后,會給變數設定默認值undefined
-
變數宣告提升
- 以下述代碼為例:
console.log(showName)// undefined var showName = 'Aaron_hj' console.log(showName)// Aaron_hj我們不難看出,在第一個列印showName處,變數showName還未宣告,但此處不會報錯,而是列印值undefined,這就是變數宣告提升:變數在宣告前已經可用, 實際上,代碼運行順序是:
var showName; //變數宣告提升 console.log(showName);// undefined showName = 'Aaron_hj'// 賦值 console.log(showName) //Aaron_hj注意:宣告提前是在JavaScript引擎的預編譯時進行,是在代碼開始運行之前,并且,只有宣告提升,賦值仍在原處
-
函式宣告提升
- 以下述代碼為例:
showName()// 函式的執行(呼叫) function showName(){ // 函式的宣告 console.log(1)我們不難看出,在呼叫showName()處,函式showName()還未宣告,但該代碼片仍能正常運行,這就是函式宣告提升:函式宣告陳述句可以被提升到外部腳本或者外部函式作用域的頂部, 實際上,代碼運行順序是:
function showName(){ // 函式的宣告 console.log(1)} showName()// 函式的執行(呼叫)注意:雖然函式宣告和變數宣告都會被提升,但是函式會首先被提升,然后才是變數,
作用域
-
變數作用域
- 變數作用域指變數可以起作用的范圍
- 變數又可分為全域變數,區域變數
- 全域變數在全域都有作用
- 區域變數只在函式作用域內起作用
- 所有不使用var定義的變數都視為全域變數
注意:在函式體內,同名的區域變數或者引數的優先級會高于全域變數,也就是說,如果函式記憶體在和全域變數同名的區域變數或者引數,那么全域變數將會被區域變數覆寫,
-
函式作用域
- 函式作用域是指在函式內宣告的所有變數在函式體內始終有定義,再結合宣告提升的概念,可理解為Javascript函式中的var宣告的變數都被提前到函式體的頂部,(注意:只有宣告會提升到函式體的頂部,變數賦值操作仍留在原來的位置)
執行背景關系
- 執行背景關系是一個Javascript中最基礎,但同時也是最重要的概念,每當控制器轉到執行階段代碼時,就會進入一個執行背景關系,執行背景關系可以理解為當前代碼的執行環境,它會形成一個作用域,每個函式執行時,都會給對應的函式創建這樣一個執行環境, 因此在一個Javascript程式中,必定會產生多個執行背景關系,Javascipt引擎會以堆疊的方式來處理他們,稱之為函式呼叫堆疊,堆疊底永遠時全域背景關系,而堆疊頂則是正在執行的背景關系,為了更加清晰地理解這個程序,根據下面的示例代碼,結合圖示給大家展示,
var name = 'Aaron_hj';
function showName(){
var anotherName = 'Aaron';
function swapName(){
var tmpName = anotherName;
anothorName = name
name = tmpName;
}
swapName();
}
showName();

第一步:全域背景關系入堆疊
第二步:showName的執行背景關系入堆疊
第三步:swapName的執行背景關系入堆疊
第四步:swapName的執行背景關系出堆疊
第五步:showName的執行背景關系出堆疊
全域背景關系在瀏覽器視窗關閉后出堆疊
注意:函式中,運行到return會直接終止函式可執行代碼的執行,因此會直接將當前背景關系彈出堆疊,
- 總結
- 詳細了解了這個程序之后,我們就可以對執行背景關系總結一些結論了,
- 單執行緒
- 同步執行,只有堆疊頂的背景關系處于執行中,其他背景關系需要等待
- 全域背景關系只有唯一的一個,它在瀏覽器關閉時出堆疊
- 函式的執行背景關系的個數沒有限制
- 每次某個函式被呼叫,就會有個新的執行背景關系為其創建,即使是呼叫的自身函式,也是如此,
(注:本博文旨在和大家分享博主自己的理解,歡迎大家來評論區一起討論學習~)
- 詳細了解了這個程序之后,我們就可以對執行背景關系總結一些結論了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/277349.html
標籤:其他
下一篇:Vue專題(一)聊一聊雙向系結
