摘要:直到抵達最外層的全局作用域,無論找到還是沒找到,查找過程都會停止。小結作用域是一套規則,用于確定在何處以及如何查找變量標志符。作用域查找會在找到第一個匹配的標識符時停止。函數作用域匿名和具名例如如下函數這叫做匿名函數表達式。
理解作用域
引擎
從頭到尾負責整個JavaScript程序的編譯和執行過程
編譯器
負責語法分析及代碼生成
作用域
負責收集并維護由所有聲明的標識符(變量)組成的一系列查詢,并實施一套非常嚴格的規則,確定當前執行的代碼對這些標識符有訪問權限。
作用域嵌套當一個塊或者函數嵌套在另一個函數或函數中時,就發生了作用域嵌套。
遍歷嵌套作用域規則:引擎從當前的執行作用域開始查找變量,如果找不到,就向上一級繼續查找。直到抵達最外層的全局作用域, 無論找到還是沒找到,查找過程都會停止。
小結作用域是一套規則,用于確定在何處以及如何查找變量(標志符)。
如果查找目的是對變量進行賦值,就是執行LHS查詢
如果查找目的是獲取變量的值,就是執行RHS查詢
作用域主要兩種工作模式:詞法作用域和動態作用域
詞法階段大部分標準語言編譯器的第一個工作階段叫做詞法化(也叫單詞化)。
簡單的說, 詞法作用域就是定義在詞法階段的作用域。換句話說,詞法作用域是由你在寫代碼的時候將變量和塊作用域寫在哪里來決定的,因此當詞法分析器處理代碼時會保持作用域不變。
作用域查找會在找到第一個匹配的標識符時停止。在多層的嵌套作用域中可以定義同名的標識符,叫做“遮蔽效應”
作用域查找始終是從運行時所處的最內部作用域開始,逐級向外或者向上查找, 知道遇見第一個匹配的標識符為止。
全局變量會自動成為全局對象(例如瀏覽器中的window對象)的屬性,因此可以不直接通過全局對象的詞法名稱, 而是間接的通過對全局對象屬性的引用來對其進行訪問。
例如window.a。通過這種技術可以訪問那些被同名變量鎖遮蔽的全局變量。但非全局變量如果被遮蔽了,無論如何都無法被訪問到。
無論函數在哪里被調用,也無論它如何被調用,它的詞法作用域都只由函數被聲明時所處的位置決定。
小結詞法作用域意味著作用域是由代碼書寫時候函數聲明的位置來決定的。
函數作用域和塊作用域 函數中的作用域函數作用域是指,屬于這個函數的全部變量都可以在整個函數的范圍內使用以及復用(事實上在嵌套的作用域中也可以使用)。
隱藏內部實現不應該這樣:
function doSomething(a) {
b = a + doSomethingElse(a * 2);
console.log(b * 3);
}
function doSomethingElse(a) {
return a - 1;
}
var b;
doSomething(2);
而是應該這樣, 隱藏變量:
function doSomething(a) {
function doSomethingElse(a) {
return a - 1;
}
var b;
b = a + doSomethingElse(a * 2);
console.log(b * 3);
}
doSomething(2);
#### 規避沖突
“隱藏”作用域中的變量和函數所帶來的另一個好處,是可以避免同名標識符之間的沖突,兩個標識符可能具有相同的名字但是用途卻不一樣,無意間可能造成命名沖突。 沖突會導致變量的值被意外覆蓋。
### 函數作用域
#### 匿名和具名
例如如下函數:
setTimeout(function() {
console.log("I waited 1 second");
}, 1000);
這叫做匿名函數表達式。
匿名函數表達式書寫起來簡單快捷,但是有幾個缺點:
匿名函數在棧追蹤中不會顯示出有意義的函數名,使得調試很困難
如果沒有函數名,當函數需要引用自身時只能使用已經過期的arguments.callee引用。
匿名函數省略了對于代碼可讀性/可理解性很重要的函數名。
行內函數表達式非常強大且有用----匿名和具名之間的區別并不會對這一點有任何影響。給函數表達式指定一個函數名可以有效解決以上問題。所以,最好始終給函數表達式命名。
setTimeout(function timeoutHandler() { // 有名字了
console.log("I waited 1 second");
}, 1000);
立即執行函數表達式
(function(){})() 和 (function(){}())
提升函數會首先別提升,然后才是變量。
出現在后面的函數聲明還是可以覆蓋前面的。
一個普通塊內部的函數聲明通常會被提升到所在作用域的頂部。
總結所有的聲明(變量和函數)都會被“移動”到各自作用域的最頂端, 這個過程被稱為 提升。
聲明本身會被提升,而包含函數表達式的賦值在內的賦值操作并不會被提升。
要注意避免重復聲明,特別是當普通的var聲明和函數聲明混合在一起的時候, 否則會引起很多危險的問題。
作用域閉包 定義當函數可以記住并訪問所在的詞法作用域時,就產生了閉包,即使函數是在所在詞法作用域以外被執行,這個引用,就叫做閉包。
無論通過何種手段將內部函數傳遞到所在詞法作用域以外,它都會持有對原始定義作用域的引用,無論在何處執行這個函數都會使用閉包
本質上講,無論何時何地,如果將函數當作第一級的值類型并到處傳遞,你就會看到閉包在這些函數中的應用。
例如在一些定時器、事件監聽器、Ajax請求等,只要使用了回調函數,實際上就是在使用閉包
循環和閉包let聲明可以用來劫持塊作用域,并且在這個作用域中聲明一個變量。
for循環頭部的let聲明還會有一個特殊的行為。這個行為指出變量在循環過程中不止被聲明一次,每次迭代都會聲明。隨后每個迭代都會使用上一個迭代結束時的值來初始化這個變量。
模塊模塊模式需要具備兩個必要條件:
必須有外部的封閉函數,該函數必須至少別調用一次(每次調用都會創建一個新的模塊實例)
封閉函數必須返回至少一個內部函數,這樣內部函數才能在私有作用域中形成閉包,并且可以訪問或者修改私有得狀態。
一個具有函數屬性的對系那個本身并不是真正的模塊。從方便觀察的角度看,一個從函數調用鎖返回的,只有數據屬性而沒有閉包函數得對象并不是真正的模塊。
現代的模塊機制大多數模塊依賴加載器/管理器本質上都是將這種模塊定義封裝進一個友好的API。
var MyModules = (function Manager() {
var modules = {};
function define(name, deps, impl) {
for (var i = 0; i < deps.length; i++) {
deps[i] = modules[deps[i]];
}
modules[name] = impl.apply(impl, deps);
}
function get(name) {
return modules[name];
}
return {
define: define,
get: get
};
})();
MyModules.define("bar", [], function() {
function hello(who) {
return "let me introduce: " + who;
}
return {
hello: hello
};
});
MyModules.define("foo", ["bar"], function(bar) {
var hungry = "xiaofan";
function awesome() {
console.log(bar.hello(hungry).toUpperCase());
}
return {
awesome: awesome
};
});
var bar = MyModules.get("bar");
var foo = MyModules.get("foo");
console.log(bar.hello("xiaofan"));
foo.awesome();
foo和bar模塊都是通過一個返回公共API的函數來定義的。foo甚至接受bar的實例作為依賴參數,并能響相應的使用它。
總結當函數可以記住并訪問所在的詞法作用域,即使函數是在當前詞法作用域以外執行,這時就產生了閉包。
模塊有兩個主要特征:
為創建內部作用域而調用了一個包裝函數
包裝函數的返回值必須包含至少一個對內部函數的引用,這樣就會創建涵蓋整個包裝函數內部作用域的閉包
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.hztianpu.com/yun/109083.html
摘要:原文鏈接原文作者你想知道的關于作用域的一切譯中有許多章節是關于的但是對于初學者來說甚至是一些有經驗的開發者這些有關作用域的章節既不直接也不容易理解這篇文章的目的就是為了幫助那些想更深一步學習了解作用域的開發者尤其是當他們聽到一些關于作用域的 原文鏈接: Everything you wanted to know about JavaScript scope原文作者: Todd Mott...
摘要:關于本書,我會寫好幾篇讀書筆記用以記錄那些讓我恍然大悟的瞬間,本文是第一篇弄懂的作用域和閉包。作用域也可以看做是一套依據名稱查找變量的規則。聲明實際上是根據你傳遞給它的對象憑空創建了一個全新的詞法作用域。 《你不知道的JavaScript》真的是一本好書,閱讀這本書,我有多次哦,原來是這樣的感覺,以前自以為理解了(其實并非真的理解)的概念,這一次真的理解得更加透徹了。關于本書,我會寫好...
摘要:也毫不例外,但在中作用域的特性與其他高級語言稍有不同,這是很多學習者久久難以理清的一個核心知識點。主要使用的是函數作用域。 關于作用域:About Scope 作用域是程序設計里的基礎特性,是作用域使得程序運行時可以使用變量存儲值、記錄和改變程序的狀態。JavaScript 也毫不例外,但在 JavaScript 中作用域的特性與其他高級語言稍有不同,這是很多學習者久久難以理清的一個核...
摘要:如果是嵌套的作用域的話,這些嵌套作用域會通過作用域鏈把嵌套作用域聯系在一起。全局沒有則報錯但是上級作用域沒法通過作用域鏈訪問下級作用域。通過作用域鏈能讓引擎對執行環境里所有有權訪問的變量和函數進行有序訪問。 一 為什么要有作用域 我們知道,變量對于程序來說是至關重要的,如果沒有變量存儲和訪問值,整個程序會受到限制。那么問題來了,既然程序這么需要變量,那么它到底是怎么樣去存儲變量和使用變...
摘要:作用域鏈前面說,作用域是根據名稱查找變量的一套規則。把這樣一層一層嵌套的作用域,叫做作用域鏈。因為這個函數名無法被外部作用域所訪問。的進階用法是給其傳入參數這樣的好處是可以縮短查詢時的作用域鏈。 上一篇文章中分析了 JS 中的數據類型和變量。這一篇文章將分析作用域,以及回答上一篇文章中變量提升的原因。 什么是作用域 作用域是一套規則,保存著變量,等待被引擎所查找。 var a = 1;...
閱讀 4516·2021-11-22 09:34
閱讀 1756·2021-11-04 16:10
閱讀 1942·2021-10-11 10:59
閱讀 3436·2019-08-30 15:44
閱讀 2217·2019-08-30 13:17
閱讀 3650·2019-08-30 11:05
閱讀 908·2019-08-29 14:02
閱讀 2781·2019-08-26 13:34