有人能夠給我簡要介紹詞彙做用域嗎? javascript
詞法(AKA靜態)做用域是指僅根據變量在代碼文本語料庫中的位置來肯定變量的範圍。 變量始終引用其頂層環境。 最好將其與動態範圍相關聯。 html
範圍定義了功能,變量等可用的區域。 例如,變量的可用性是在其上下文中定義的,也就是說,它們是在函數,文件或對象中定義的。咱們一般將這些變量稱爲局部變量。 java
詞法部分意味着您能夠經過閱讀源代碼來得出範圍。 git
詞法範圍也稱爲靜態範圍。 github
動態範圍定義了能夠在定義後從任何地方調用或引用的全局變量。 有時它們被稱爲全局變量,即便大多數programmin語言中的全局變量具備詞法範圍。 這意味着,能夠從讀取代碼中得出該變量在此上下文中可用的信息。 也許必須遵循use或include子句才能找到實例或定義,可是代碼/編譯器知道該位置的變量。 閉包
相比之下,在動態做用域中,您首先搜索本地函數,而後搜索調用本地函數的函數,而後搜索調用該函數的函數,依此類推,直到調用堆棧。 「動態」是指更改,由於每次調用給定函數時,調用堆棧均可能不一樣,所以該函數可能會根據調用源的不一樣而使用不一樣的變量。 (請參閱此處 ) app
要查看動態範圍的有趣示例,請參見此處 。 函數
Delphi / Object Pascal中的一些示例 spa
Delphi具備詞法範圍。
unit Main; uses aUnit; // makes available all variables in interface section of aUnit interface var aGlobal: string; // global in the scope of all units that use Main; type TmyClass = class strict private aPrivateVar: Integer; // only known by objects of this class type // lexical: within class definition, // reserved word private public aPublicVar: double; // known to everyboday that has access to a // object of this class type end; implementation var aLocalGlobal: string; // known to all functions following // the definition in this unit end.
最接近動態範圍的Delphi是RegisterClass()/ GetClass()函數對。 有關其用途,請參見此處 。
假設經過註冊代碼沒法預測調用RegisterClass([TmyClass])的時間(經過用戶調用按鈕的點擊方法調用),調用GetClass('TmyClass')的代碼將得到結果與否。 使用GetClass()調用RegisterClass()沒必要在單元的詞彙範圍內;
動態範圍的另外一種可能性是Delphi 2009中的匿名方法 (閉包),由於它們知道其調用函數的變量。 它不會從那裏遞歸地遵循調用路徑,所以不是徹底動態的。
我經過示例瞭解它們。 :)
首先,採用相似C的語法的詞法做用域(也稱爲靜態做用域):
void fun() { int x = 5; void fun2() { printf("%d", x); } }
每一個內部級別均可以訪問其外部級別。
一樣,在相似C的語法中,Lisp的第一個實現使用了另外一種稱爲動態範圍的方法:
void fun() { printf("%d", x); } void dummy1() { int x = 5; fun(); } void dummy2() { int x = 10; fun(); }
這裏fun
既能夠訪問x
在dummy1
或dummy2
,或x
任何函數調用fun
與x
在其聲明。
dummy1();
將打印5
dummy2();
將打印10。
第一個稱爲靜態,由於它能夠在編譯時推導,第二個稱爲動態,由於外部範圍是動態的,而且取決於函數的鏈調用。
我發現靜態範圍界定對眼睛來講更容易。 最終,大多數語言甚至都使用Lisp(能夠同時作,對嗎?)。 動態做用域就像將全部變量的引用傳遞給調用的函數同樣。
爲何編譯器沒法推斷函數的外部動態範圍的示例,請考慮咱們的最後一個示例,若是咱們編寫以下代碼:
if(/* some condition */) dummy1(); else dummy2();
調用鏈取決於運行時條件。 若是爲true,則調用鏈以下所示:
dummy1 --> fun()
若是條件爲假:
dummy2 --> fun()
在這兩種狀況下, fun
的外部範圍是調用者加上調用者的調用者,依此類推 。
僅說起C語言既不容許嵌套函數也不容許動態做用域。
var scope = "I am global"; function whatismyscope(){ var scope = "I am just a local"; function func() {return scope;} return func; } whatismyscope()()
上面的代碼將返回「我只是本地人」。 它不會返回「我是全球」。 由於函數func()會在函數whatismyscope的範圍內計算最初定義的位置。
它不會被調用的內容所困擾(即便是全局做用域,甚至也不會從另外一個函數中),這就是爲何不會打印我是全局的全局做用域值的緣由。
這稱爲詞法做用域,其中「根據JavaScript定義指南,使用定義時有效的做用域鏈執行函數 」。
詞法範圍是一個很是很是強大的概念。
但願這能夠幫助..:)
我喜歡@Arak之類的功能全面,與語言無關的答案。 因爲此問題被標記爲JavaScript ,所以,我想介紹一些針對該語言的註釋。
在javascript中,做用域的選擇是:
var _this = this; function callback(){ console.log(_this); }
var _this = this; function callback(){ console.log(_this); }
callback.bind(this)
我認爲,值得注意的是JavaScript 並無真正的動態做用域 。 .bind
調整this
關鍵字,這很接近,但在技術上並不相同。
這是演示兩種方法的示例。 每次決定如何肯定回調範圍時,您都須要執行此操做,所以這適用於Promise,事件處理程序等。
您可能會在這裏用JavaScript稱呼回調的Lexical Scoping
:
var downloadManager = { initialize: function() { var _this = this; // Set up `_this` for lexical access $('.downloadLink').on('click', function () { _this.startDownload(); }); }, startDownload: function(){ this.thinking = true; // request the file from the server and bind more callbacks for when it returns success or failure } //... };
範圍的另外一種方法是使用Function.prototype.bind
:
var downloadManager = { initialize: function() { $('.downloadLink').on('click', function () { this.startDownload(); }.bind(this)); // create a function object bound to `this` } //...
據我所知,這些方法在行爲上是等效的。