關於函式和變數的處理機制 附面試題

2021-08-24 20:22:45 字數 4576 閱讀 6932

關於函式和變數的處理機制,屬於我們學習js的必備技能,也是面試經常會遇到的問題

分享一點自己整理的知識點,加個人理解瀏覽器和node執行js**的時候,會提供乙個供**賴以執行的環境,我們把這個環境稱之為「全域性作用域」

瀏覽器:window

node:global

window和global代表全域性作用域,他們也是全域性物件(在js**的任何位置,我們都可以基於window.***或者global.***調取全域性物件中的屬性和方法[這些屬性方法一般都是瀏覽器內建的])

//=>例如:

window.settimeout(function(){},1000);

window.alert();

...

環境建立完成後,js**沒有立即執行,而是做一些詞法解析,其中詞法解析中有乙個重要的操作就是「變數提公升」 => 在變數提公升這個操作階段,渲染引擎會把「當前作用域」下所有「帶var和function關鍵字」的變數進行提前的「宣告或者定義」

[宣告:declare]

//=>var [variable name];

var ***; //=>宣告就是在建立變數或者函式

function

fn;[定義:defined]

//=>[variable name]=[value];

***=100; //=>定義就是在給宣告的變數賦值

fn(){};

//=>只宣告未定義,預設值是:undefined(未定義)

函式

由於引用資料型別儲存的值過於複雜(結構複雜及內容較多),渲染引擎會開闢乙個新的記憶體空間,單獨來儲存這些值,最後把空間的引用位址賦值給對應的變數,後期所有的操作都是基於位址先找到空間,然後對空間中的內容進行操作

[物件資料型別]

1. 開闢乙個記憶體空間(有乙個16進製制的位址)

2. 把物件中的屬性名和屬性值(鍵值對)依次儲存到記憶體空間中

3. 把記憶體空間的位址賦值給變數

/** [變數提公升]

* var obj;

** [**執行]

* 1.開闢乙個記憶體空間(bbbfff000)

* 2.把鍵值對儲存到空間中

* + x : 10

* + y : obj.x*3 此時的obj還沒有賦值呢,鍵值對儲存完成,才會把位址賦值給obj,此時的obj依然是undefined,undefined是無法調取屬性x的,所以報錯了

* obj =

*/var obj=;

console.log(obj.y);

[函式資料型別:建立乙個函式]

1. 開闢乙個記憶體空間(有乙個16進製制的位址)

2. 把函式體中的**當做「字串」儲存到記憶體空間中

+ 函式建立的時候,儲存的都是字串,所以說函式只建立不執行是毫無意義的

+ 變數提公升只對當前作用域下的var/function處理,主要原因是函式中存的都是字串,我們看到的函式中的var/function此時都還是字元呢

+ 當函式執行的時候目的就是要把這堆字串拿出來執行的

3. 把記憶體空間的位址賦值給變數

/** [全域性作用域:變數提公升]

* function fn = aaafff111;

* //

=>

var x; var y; 這兩步沒有,原因是此時的他們都還是無意義的字元

*/function fn()

fn();

**執行過程中的一些細節點

**執行的時候,如果當前的某個操作在變數提公升階段已經處理過了,渲染引擎很懶,不會進行重複的處理,所以:遇到 var ***=*** 只需要給變數賦值即可,不需要重新宣告;遇到 function ***() 直接的跳過即可,因為變數提公升階段函式已經宣告+定義了;

函式執行

重要的知識點:

- 在每乙個作用域(全域性和函式執行產生的私有的)的詞法解析階段(變數提公升或者形參賦值都在這個階段),宣告過的變數或者函式都是當前作用域私有的

+ 全域性作用域下宣告的變數是「全域性變數」:在js任何位置都可以使用(但是用多了會產生一些衝突和冗餘「全域性變數汙染」)

+ 在私有作用域下,「形參」以及「宣告的變數函式」都是「私有變數」:只能在當前作用域下使用,和外界的變數(即使重名)是互不干擾的

- 私有作用域**執行過程中,遇到乙個變數或者函式,但它不是當前作用域私有的變數,此時相當於在私有作用域中「操作上級作用域中的變數」

+ **執行遇到變數,首先驗證是否是自家私有的,是私有的則和外面沒有半毛錢關係,不是私有的進行第二步

+ 第二步是向當前作用域的上級作用域查詢,看是否是上級作用域中私有的,如果是,那麼我們此處操作的就是上級作用域中的變數,如果不是,則繼續向上查詢...

+ 如果找到window都沒有發現哪個作用域有這個變數,說明當前變數是不存在,如果是獲取這個變數值則會報錯,如果是設定變數的值,相當於給window全域性物件設定了乙個新的屬性

=>我們把這種向上級作用域一級級查詢的過程稱為「作用域鏈」

變數提公升的細節和意義

[意義]

可以讓我們開發者在**執行之前使用變數或者函式(尤其是函式:**執行之前已經建立完成了),不會發生錯誤

//=>建立變數帶var和不帶var的區別

1.在私有作用域中,帶var的變數會在變數提公升階段進行宣告,屬於私有變數(和外界無關聯);如果不帶var也不是形參,則它不是私有的變數,這樣和外界有關了!

var x=10,

y=20;

function

fn()

fn();

console.log(x,y); //=>10,200

2.在全域性作用域下,帶var和不帶var有一些區別

+ 帶var的是全域性變數(也相當於給全域性物件增加了乙個屬性),存在變數提公升

+ 不帶var的只是給全域性物件設定乙個屬性而已,沒有所謂的變數提公升

//=>在變數提公升階段,遇到相同的變數名(函式名),不會重複宣告,但是需要重複賦值,最後賦的值會替換掉之前賦的值
1.如何查詢當前作用域的上級作用域

函式執行會形成私有作用域(a),a的上級作用域是誰和他在哪執行沒關係,只和在哪定義的有關係;在哪個作用域下定義的,a的上級作用域就是誰!

2.堆疊記憶體

js中有兩大記憶體:堆記憶體(heap)、棧記憶體(stack)

堆記憶體作用:用來儲存內容的(物件儲存的是鍵值對,函式儲存的是**字串)

棧記憶體作用:也可以被稱為作用域,是**解析和執行的環境

3.堆疊記憶體釋放問題

棧記憶體的釋放:函式執行會形成乙個私有的棧記憶體,一般函式執行完成,棧記憶體會自己釋放(優化效能),除非棧記憶體中的某乙個東西(例如:棧中開闢的堆記憶體)被棧記憶體以外的變數(或者是其它的東西)占用了,此時的棧記憶體就不能被釋放掉;全域性作用域(棧記憶體)是在一開始載入js的時候誕生的,當頁面關閉的時候會自動釋放掉!

堆記憶體釋放:只要沒有變數占用這個堆記憶體,瀏覽器就會在空閒的時候把它釋放掉,所以專案中我們盡可能把不被使用的堆記憶體手動釋放掉,例如:obj=null;

函式執行會形成乙個私有的作用域(棧記憶體),私有棧中的私有變數被作用域保護起來(和外界的變數互不干擾),而且一旦當前棧記憶體中的某個東西被外面占用了,當前棧記憶體就不會被釋放掉,我們可以在棧記憶體中儲存一些資訊(這些資訊也不會被銷毀)… 我們把函式執行的這種「保護」、「儲存」特性稱之為「閉包」!!!

綜上 : 我們來看兩道題(附答案)
var i= 2,

x = 5;

var fn = function

(x)};

var f = fn(1);

f(2); //7

fn(3)(4); //10

f(5); //9

為了幫助大家理解,畫了一張圖,看圖能更清晰的理解整個題的執行過程

第二題

console.log(x, y);//

=>

undefined

undefined

var x = 10,

y = 20;

function fn()

x = fn(20);

console.log(x, y, z);//

=>

undefined

10030

希望本篇博文能更好的幫助大家學習~~~ q(≧▽≦q)

關於c 中的事件處理機制

在程式中怎麼實現事件。要明白事件,首先要知道什麼是委託。在c 中委託允許開發人員 將乙個物件中的方法傳遞給另乙個能呼叫該方法的類的某個物件。比如 可以將類ac中的乙個方法add 前 提是這個方法以被包含在某個委託中了 傳遞給另乙個類wms。此時類wms就能呼叫類ac中的add了。當然不 管你是以什麼...

異常的處理機制 捕獲和丟擲

jvm 預設是如何處理異常的呢?main函式收到乙個問題,有兩種處理方式 1.自己解決 2.自己解決不了,交給jvm解決 jvm有乙個預設的異常處理機制,就是將該異常顯示出來 包括 異常名稱 資訊 出現位置 異常的兩種處理方式 1.try catch finally 捕獲並處理 try catch ...

Python的異常處理機制和常見異常型別

1.定義 執行時檢測到的錯誤。2.現象 當異常發生時,程式不會再向下執行,而轉到函式的呼叫語句。3.常見異常型別 名稱異常 nameerror 變數未定義。型別異常 typeerror 不同型別資料進行運算。索引異常 indexerror 超出索引範圍。屬性異常 attributeerror 物件沒...