三 函式作用域 隱藏內部實現

2021-08-26 23:42:58 字數 3434 閱讀 4109

作用域包含了一系列的「氣泡」,每乙個都可以作為容器,其中包含了識別符號(變數、函式)的定義。

函式作用域的含義:屬於這個函式的全部變數都可以在整個函式範圍內使用及復用(事實上在巢狀的作用域中也可以使用)。這種方案能充分利用js變數可以根據需要改變值型別的「動態特性」。

function

()//更多**

var c=3;

}bar();//失敗

console.log(a,b,c);//三個全都失敗

這個**片段中,foo(..)的作用域氣泡中包含了識別符號a、b、c和bar。

bar(..)有自己的作用域氣泡。全域性作用域也有自己的作用域氣泡,它只包含了乙個識別符號foo。

無論識別符號宣告出現在作用域中的何處,這個識別符號所代表的變數或函式都將附屬於所處作用域的氣泡。識別符號a、b、c和bar都附屬於foo(..)的作用域氣泡,因此無法從foo(..)的外部對它們進行訪問。也就是說,這些識別符號無法從全域性作用域中進行訪問,因此會導致referenceerror錯誤。但是,這些識別符號(a、b、c和bar)在foo(..)內部可以被訪問。

對函式的傳統認知就是先宣告乙個函式,然後再向裡面新增**。反過來:從所寫的**中挑選出乙個任意片段,然後用函式宣告對它進行包裝,實際上就是把這些**「隱藏」起來。

實際的結果就是在這個**片段的周圍建立了乙個作用域氣泡,也就是說這段**中的任何宣告(變數或函式)都將繫結在這個新建立的包裝函式的作用域中,而不是先前所在的作用域中。換句話說,可以把變數和函式包裹在乙個函式的作用域中,然後利用這個作用域來「隱藏」它們。

最小授權或最小暴露原則:指在軟體設計中,應該最小限度地暴露必要內容,而將其他內容都「隱藏」起來,比如某個模組或物件的api設計。

這個原則可以延伸到如何選擇作用於來包含變數和函式。如果所有變數和函式都在全域性作用域中,當然可以在所有內部巢狀作用域中訪問到它們。但是這樣會破壞最小特權原則,因此可能會暴漏更多的變數或函式,而這些函式或變數很應該是私有的,正確的**因該是可以阻止針對這些變數或函式進行訪問的。

例如:

function

dosomething

(a)function

dosomethingelse

(a)var b;

dosomething(2);//15

**中,變數b和函式dosomethingelse(..)應該是dosomething(..)內部具體實現的「私有」內容。可能被有意或無意的以非預期的方式使用,從而導致超出了dosomething(..)的適用條件。更合理的設計會將這些私有的具體內容隱藏到dosomething(..)內部,如:

function

dosomething

(a) var b;

b=a+dosomethingelse(a*2);

console.log(b*3);

}dosomenthing(2);//15

現在,b和dosomethingelse(..)都無法從外部被訪問,而只能被dosomething(..)所控制。

功能性和最終效果都沒有受到影響,但是設計上將具體內容私有化了,設計良好的軟體都會依次進行實現。

2.1 規避衝突

「隱藏」作用域中的變數和函式所帶來的另乙個好處,是可以避免同名識別符號之間的衝突,兩個識別符號可能具有相同的名字但用途卻不一樣,無意間可能造成命令的衝突。衝突會導致變數的值被意外覆蓋。

例如:

function

foo()

for (var i=0;i<10;i++)

}foo();

bar(..)內部的賦值表示式 i=3 ,意外的覆蓋了宣告在foo(..)內部的for迴圈中的i ,i 被固定設定為3,導致無限迴圈。

bar(..)內部的賦值操作需要宣告乙個本地變數來使用,採用任何名字都可以,var i=3;就可以滿足要求(同時會為i 宣告乙個前面提到過的「遮蔽變數」)。另一種方法是採用乙個完全不同的識別符號名稱,比如 「var j=3;」。但是在軟體設計中可能自然而然地要求使用同樣的識別符號名稱,因此這種情況下使用作用域來「隱藏」內部宣告是唯一的 最佳選擇。

2.2.1全域性命名空間

變數衝突的乙個典型列子存在於全域性作用域中。當程式中載入了多個第三方庫時,如果它們沒有妥善地將內部私有的函式或變數隱藏起來,就會很容易引發衝突。

這些庫通常會在全域性作用域中宣告乙個名字足夠獨特的變數,通常是乙個物件。這個物件被用作庫的命名空間,所有需要暴露給外界的功能都會成為這個物件(命名空間)的屬性,而不是將自己的的識別符號暴露在頂級的詞法作用域中。

2.2.2模組管理

另一種避免衝突的方法–模組管理,就是從眾多模組管理器中挑選乙個來使用。使這些工具,任何庫都將無需將識別符號加入到全域性作用域中,而是通過依賴管理器的機制將庫的識別符號顯式地匯入到另外乙個特定的作用域中。

這些工具管理器並沒有能夠違反詞法作用域規則的功能,只是利用作用域的規則強制多有的識別符號都不能注入到共享作用域中,而且保持私有、無衝突的作用域中,這樣可以有效規避意外衝突。

第五章將會介紹模組模式的詳細內容。

在任意**片段外部新增包裝函式,可以將內部的變數和函式定義「隱藏」起來,外部作用域無法訪問包裝函式內部的任何內容。

例如:

var a=2;

function

foo()//<--新增行

foo(); //<--新增行

console.log(a);//2

這種技術可以解決一些問題,但是並不理想。首先必須宣告乙個具名函式foo(),意味著foo這個名稱本身「汙染」了所在作用域(這個例子中是全域性作用域)。其次,必須顯式的通過函式名(foo())呼叫這個函式才能執行其中**。

如果不需要函式名(或者至少函式名可以不汙染所在作用域),並且能夠自動執行,接下來,js提供了能夠同時解決這兩個問題的方案。

如下:

var a=2;

(function

foo())();//<--新增這一行

console.log(a);//2

包裝函式的宣告以(function…開始,函式會被當作函式表示式而不是乙個標準的函式宣告來處理。

區分函式宣告和表示式最簡單的方法是:看function關鍵字出現在宣告中的位置(不僅僅是一行**,而是整個宣告 中的位置)。如果function是宣告中的第乙個詞,那麼就是乙個函式宣告,否則就是乙個函式表示式。

函式宣告和函式表示式之間最重要的區別是:

它們的識別符號將會繫結在何處。

比較前面兩段**。第乙個片段中foo被繫結在所在作用域中,可以直接通過foo()來呼叫它。第二個片段中,(function foo())作為函式表示式意味著foo只能在..所代表的位置中被訪問,外部作用域則不行。foo變數名被隱藏在自身中意味著不會非必要的汙染外部作用域。

隱藏域的作用

隱藏域是用來收集或傳送資訊的不可見元素,對於網頁的訪問者來說,隱藏域是看不見的。當表單被提交時,隱藏域就會將資訊用你設定時定義的名稱和值傳送到伺服器上。格式 input type hidden name value 屬性解釋 type hidden 定義隱藏域 name屬性定義隱藏域的名稱,要保證資...

表單隱藏域的作用

隱藏域是用來收集或傳送資訊的不可見元素,對於網頁的訪問者來說,隱藏域是看不見的。當表單被提交時,隱藏域就會將資訊用你設定時定義的名稱和值傳送到伺服器上。格式 input type hidden name value 屬性解釋 type hidden 定義隱藏域 name屬性定義隱藏域的名稱,要保證資...

外部內部變數 作用域

外部變數和內部變數 乙個c程式就是由乙個或多個 外部 物件構成的,它們可能是外部變數或函式。在函式外部定義的變數就叫做外部變數,在函式內部定義 也可以認為在塊內定義 的變數叫做內部變數。函式本身一定是外部的 c語言不允許將函式定義在函式內部,也就是說不可在函式內部再定義函式。函式內部或塊內定義的內部...